Repository: cloudspannerecosystem/harbourbridge
Pull request #140 (2021-05-26 21:47:40)

 Fixing the search field glitch during keydown
Fixes #<issue_number_goes_here>

> It's a good idea to open an issue first for discussion.

- [ ] Tests pass
- [ ] Appropriate changes to README are included in PR

google-cla[bot]: We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors.  If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA ([login here](https://cla.developers.google.com/) to double check)?  If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.
In order to pass this check, please resolve this problem and then comment `@googlebot I fixed it.`. If the bot doesn't comment, it means it doesn't think anything has changed.

ℹ️ **Googlers: [Go here](https://goto.google.com/prinfo/https%3A%2F%2Fgithub.com%2Fcloudspannerecosystem%2Fharbourbridge%2Fpull%2F140) for more info**.

<!-- need_author_cla -->
amar-appdev: @googlebot I fixed it.
google-cla[bot]: We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors.  If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA ([login here](https://cla.developers.google.com/) to double check)?  If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.
In order to pass this check, please resolve this problem and then comment `@googlebot I fixed it.`. If the bot doesn't comment, it means it doesn't think anything has changed.

ℹ️ **Googlers: [Go here](https://goto.google.com/prinfo/https%3A%2F%2Fgithub.com%2Fcloudspannerecosystem%2Fharbourbridge%2Fpull%2F140) for more info**.

<!-- need_author_cla -->

Patch:
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..d41e359
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,21 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Launch Package",
+            "type": "go",
+            "request": "launch",
+            "mode": "auto",
+            "program": "${fileDirname}",
+            "args": [
+                "-driver=mysqldump", 
+                "-instance=e2e-spanner-testing", 
+                "-v=true", 
+                "-dump-file="
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9a86ba0..29fc6e2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -26,3 +26,12 @@ information on using pull requests.
 
 This project follows
 [Google's Open Source Community Guidelines](https://opensource.google/conduct/).
+
+## IDE and Debugger
+
+We support VSCode out of the box for debugging the project.
+Follow these steps to use the debugger
+1. Setup the [Delve debugger](https://github.com/golang/vscode-go)
+2. Add the command line arguments in the [launch.json](https://github.com/cloudspannerecosystem/harbourbridge/blob/master/.vscode/launch.json)
+3. Run the project from main.go
+
diff --git a/README.md b/README.md
index 3b0ed49..a05e169 100644
--- a/README.md
+++ b/README.md
@@ -3,9 +3,9 @@
 [![cloudspannerecosystem](https://circleci.com/gh/cloudspannerecosystem/harbourbridge.svg?style=svg)](https://circleci.com/gh/cloudspannerecosystem/harbourbridge)
 
 HarbourBridge is a stand-alone open source tool for Cloud Spanner evaluation and
-migration, using data from an existing PostgreSQL or MySQL database. The tool
-ingests schema and data from either a pg_dump/mysqldump file or directly from
-the source database, and supports both schema and data migration. For schema
+migration, using data from an existing PostgreSQL, MySQL or DynamoDB database. 
+The tool ingests schema and data from either a pg_dump/mysqldump file or directly 
+from the source database, and supports both schema and data migration. For schema
 migration, HarbourBridge automatically builds a Spanner schema from the schema
 of the source database. This schema can be customized using the HarbourBridge
 schema assistant. For data migration, HarbourBridge creates a new Spanner
@@ -17,12 +17,14 @@ For more details on schema customization and use of the schema assistant, see
 capabilities of HarbourBridge.
 
 HarbourBridge is designed to simplify Spanner evaluation and migration, and in
-particular for migrating moderate-size PostgreSQL/MySQL datasets to Spanner (up
-to about 100GB). Many features of PostgreSQL/MySQL, especially those that don't
+particular for migrating moderate-size PostgreSQL/MySQL/DynamoDB datasets to Spanner 
+(up to about 100GB). Many features of PostgreSQL/MySQL, especially those that don't
 map directly to Spanner features, are ignored, e.g. stored functions and
 procedures, and sequences. Types such as integers, floats, char/text, bools,
 timestamps, and (some) array types, map fairly directly to Spanner, but many
 other types do not and instead are mapped to Spanner's `STRING(MAX)`.
+In the case of DynamoDB, the schema is inferred based on a certain 
+amount of sampled data.
 
 View HarbourBridge as a way to get up and running fast, so you can focus on
 critical things like tuning performance and getting the most out of
@@ -44,8 +46,14 @@ To use the tool on a MySQL database called mydb, run
 mysqldump mydb | harbourbridge -driver=mysqldump
 ```
 
-HarbourBridge accepts pg_dump/mysqldump's standard plain-text format, but not archive or
-custom formats. More details on usage can be found in [Example usage](#example-usage) section.
+To use the tool on a DynamoDB database, run
+```sh
+harbourbridge -driver=dynamodb
+```
+
+HarbourBridge accepts pg_dump/mysqldump's standard plain-text format, but 
+not archive or custom formats. More details on usage can be found in 
+[Example usage](#example-usage) section.
 
 HarbourBridge automatically determines the cloud project and Spanner instance to
 use, and generates a new Spanner database name (prefixed with `{driver}_` and
@@ -58,7 +66,7 @@ level, and the database created by HarbourBridge will inherit default
 permissions from the instance. All data written by HarbourBridge is visible to
 anyone who can access the created database.**
 
-As it processes the PostgreSQL/MySQL data, HarbourBridge reports on progress, provides
+As it processes the data, HarbourBridge reports on progress, provides
 stats on the schema and data conversion steps, and an overall assessment of the
 quality of the conversion. It also generates a schema file, report file and
 a session file (and a bad-data file if data was dropped). See
@@ -138,17 +146,24 @@ To use the tool on a MySQL database called mydb, run
 mysqldump mydb | $GOPATH/bin/harbourbridge -driver=mysqldump
 ```
 
+To use the tool on a DynamoDB database, run
+
+```sh
+$GOPATH/bin/harbourbridge -driver=dynamodb
+```
 More details on running harbourbridge can be found in [Example usage](#example-usage) section.
 
 This command will use the cloud project specified by the `GCLOUD_PROJECT`
 environment variable, automatically determine the Cloud Spanner instance
-associated with this project, convert the PostgreSQL/MySQL schema for `mydb` to a
-Spanner schema, create a new Cloud Spanner database with this schema, and
-finally, populate this new database with the data from `mydb`. If project has
-multiple instances, then list of available instances will be shown and you will have to
-pick one of the available instances and set the `--instance` flag. The new Cloud
-Spanner database will have a name of the form `{DRIVER}_{DATE}_{RANDOM}`, where`{DRIVER}`
-is the drivername specified,`{DATE}`is today's date, and`{RANDOM}` is a random suffix for
+associated with this project, convert the source schema to a Spanner schema 
+(For MySQL/Postgres) or infer a schema from the DynamoDB instance, create a
+new Cloud Spanner database with this schema, and finally, populate this new
+database with the data from the source database. 
+If the project has multiple instances, then list of available instances 
+will be shown and you will have to pick one of the available instances and 
+set the `--instance` flag. The new Cloud Spanner database will have a name of 
+the form `{DRIVER}_{DATE}_{RANDOM}`, where`{DRIVER}` is the drivername 
+specified,`{DATE}`is today's date, and`{RANDOM}` is a random suffix for 
 uniqueness.
 
 See the [Troubleshooting Guide](#troubleshooting-guide) for help on debugging
@@ -196,19 +211,15 @@ to check the number of rows in table `mytable`.
 
 The tables created by HarbourBridge provide a starting point for evaluation of
 Spanner. While they preserve much of the core structure of your PostgreSQL/MySQL
-schema and data, many key features have been dropped, including (non-primary)
-indexes, functions, sequences, procedures, triggers, and views.
+schema and data, many key features have been dropped, including functions, 
+sequences, procedures,triggers, and views. For DynamoDB, the conversion from 
+schemaless to schema is focused on the use-case where customers use DynamoDB in 
+a consistent, structured way with a fairly well defined set of columns and types.
 
 As a result, the out-of-the-box performance you get from these tables could be
-slower than what you get from PostgreSQL/MySQL. HarbourBridge does preserve primary
-keys, but we do not currently translate other indexes. If your SQL query
-performance relies on PostgreSQL/MySQL indexes that are dropped, then the performance
-of the tables created by HarbourBridge could be significantly impaired.
-
-To improve performance, consider adding [Secondary
-Indexes](https://cloud.google.com/spanner/docs/secondary-indexes) to the tables
-created by HarbourBridge, using the existing PostgreSQL/MySQL indexes as a guide. Also
-consider using [Interleaved
+slower than what you get from PostgreSQL/MySQL/DynamoDB. 
+
+To improve performance, also consider using [Interleaved
 Tables](https://cloud.google.com/spanner/docs/schema-and-data-model#creating-interleaved-tables)
 to tune performance.
 
@@ -223,11 +234,12 @@ plans and aspirations for developing HarbourBridge further are outlined in the
 Whitepaper](https://github.com/cloudspannerecosystem/harbourbridge/blob/master/whitepaper.md).
 
 You can also change the way HarbourBridge behaves by directly editing the
-pg_dump/mysqldump output. For example, suppose you want to try out different primary keys
-for a table. First run pg_dump/mysqldump and save the output to a file. Then modify (or
-add) the relevant `ALTER TABLE ... ADD CONSTRAINT ... PRIMARY KEY ...` statement
-in the pg_dump/mysqldump output file so that the primary keys match what you need. Then
-run HarbourBridge on the modified pg_dump/mysqldump output.
+pg_dump/mysqldump output. For example, suppose you want to try out different 
+primary keys for a table. First run pg_dump/mysqldump and save the output to 
+a file. Then modify (or add) the relevant 
+`ALTER TABLE ... ADD CONSTRAINT ... PRIMARY KEY ...` statement in the 
+pg_dump/mysqldump output file so that the primary keys match what you need. 
+Then run HarbourBridge on the modified pg_dump/mysqldump output.
 
 ## Files Generated by HarbourBridge
 
@@ -242,9 +254,9 @@ HarbourBridge generates several files as it runs:
 
 - Report file (ending in `report.txt`): contains a detailed analysis of the
   PostgreSQL/MySQL to Spanner migration, including table-by-table stats and an
-  analysis of PostgreSQL/MySQL types that don't cleanly map onto Spanner types. Note
-  that PostgreSQL/MySQL types that don't have a corresponding Spanner type are
-  mapped to STRING(MAX).
+  analysis of PostgreSQL/MySQL types that don't cleanly map onto Spanner types. 
+  Note that PostgreSQL/MySQL types that don't have a corresponding Spanner type 
+  are mapped to STRING(MAX).
 
 - Bad data file (ending in `dropped.txt`): contains details of data
   that could not be converted and written to Spanner, including sample
@@ -267,8 +279,12 @@ dbname.
 created in this instance. If not specified, the tool automatically determines an
 appropriate instance using gcloud.
 
-`-driver` Specifies the driver to use for schema and data conversion. Supported drivers
-are _'postgres'_, _'pg_dump'_, _'mysql'_ and _'mysqldump'_. By default, the driver is _'pg_dump'_.
+`-driver` Specifies the driver to use for schema and data conversion. Supported 
+drivers are _'postgres'_, _'pg_dump'_, _'mysql'_ and _'mysqldump'_. By default, 
+the driver is _'pg_dump'_.
+
+`-schema-sample-size` Specifies the number of rows to use for inferring schema 
+(only for DynamoDB). By default, the schema sample size is 100,000.
 
 `-prefix` Specifies a file prefix for the report, schema, and bad-data files
 written by the tool. If no file prefix is specified, the name of the Spanner
@@ -284,30 +300,40 @@ Any data in the source database will be ignored.
 A spanner database will be created based on the schema state provided
 by a session file (`-session`) and data will be migrated.
 
+`-skip-foreign-keys` Controls whether we add foreign key constraints after
+data migration is complete. This flag cannot be used with schema-only mode,
+and does not affect the generation of foreign key statements during schema
+processing i.e. foreign key constraints will still appear in the generated 
+Spanner DDL files.
+
 `-session` Specifies a session file that contains all schema and data 
 conversion state endcoded as JSON.
 
 ## Example Usage
 
-Details on HarbourBridge example usage for PostgreSQL and MySQL can be
-found in the [PostgreSQL example usage](postgres/README.md#example-postgresql-usage)
-and [MySQL example usage](mysql/README.md#example-mysql-usage) sections respectively.
+Details on HarbourBridge example usage can be found here: 
+- [PostgreSQL example usage](postgres/README.md#example-postgresql-usage)
+- [MySQL example usage](mysql/README.md#example-mysql-usage)
+- [DynamoDB example usage](dynamodb/README.md#example-dynamodb-usage)
+
 
 ## Schema Conversion
 
-Details on HarbourBridge schema conversion for PostgreSQL and MySQL can be
-found in the [PostgreSQL schema conversion](postgres/README.md#schema-conversion)
-and [MySQL schema conversion](mysql/README.md#schema-conversion) sections respectively.
+Details on HarbourBridge schema conversion can be found here:
+- [PostgreSQL schema conversion](postgres/README.md#schema-conversion)
+- [MySQL schema conversion](mysql/README.md#schema-conversion)
+- [DynamoDB schema conversion](dynamodb/README.md#schema-conversion)
 
 ## Data Conversion
 
-HarbourBridge converts PostgreSQL/MySQL data to Spanner data based on the Spanner
-schema it constructs. Conversion for most data types is fairly straightforward,
-but several types deserve discussion. Note that HarbourBridge is not intended
-for databases larger than about 100GB. Details on HarbourBridge data conversion
-for PostgreSQL and MySQL can be found in the
-[PostgreSQL data conversion](postgres/README.md#data-conversion)
-and [MySQL data conversion](mysql/README.md#data-conversion) sections respectively.
+HarbourBridge converts PostgreSQL/MySQL/DynamoDB data to Spanner data based on 
+the Spanner schema it constructs. Conversion for most data types is fairly 
+straightforward, but several types deserve discussion. Note that HarbourBridge 
+is not intended for databases larger than about 100GB. Details on HarbourBridge 
+data conversion can be found here:
+- [PostgreSQL data conversion](postgres/README.md#data-conversion)
+- [MySQL data conversion](mysql/README.md#data-conversion)
+- [DynamoDB data conversion](dynamodb/README.md#data-conversion)
 
 ## Troubleshooting Guide
 
@@ -358,9 +384,9 @@ It is also possible to configure access via pg_dump's command-line options
 #### 1.2 Direct access to PostgreSQL
 
 In this case, HarbourBridge connects directly to the PostgreSQL
-database to retrieve table schema and data using the setting of
-standard _PGHOST_, _PGPORT_, _PGUSER_, _PGDATABASE_ environment
-variables. Password can be specified either in the _PGPASSWORD_
+database to retrieve table schema and data. Set the `-driver=postgres`
+and provide the environment variables `_PGHOST_`, `_PGPORT_`, `_PGUSER_`, 
+`_PGDATABASE_`. Password can be specified either in the `_PGPASSWORD_`
 environment variable or provided at the password prompt.
 
 #### 1.3 mysqldump
@@ -369,21 +395,32 @@ If you are using mysqldump (-driver=mysqldump), check that mysqldump is
 correctly configured to connect to your MySQL
 database via the command-line options `--host`, `--port`, and `--user`.
 Note that mysqldump uses the same options as mysql to connect to your
-database. See the [mysql](https://dev.mysql.com/doc/refman/8.0/en/mysql-commands.html) and
-[mysqldump](https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html) documentation.
+database. See the [mysql](https://dev.mysql.com/doc/refman/8.0/en/mysql-commands.html)
+and [mysqldump](https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html) documentation.
 
 #### 1.4 Direct access to MySQL
 
 In this case, HarbourBridge connects directly to the MySQL
-database to retrieve table schema and data using the setting of
-_MYSQLHOST_, _MYSQLPORT_, _MYSQLUSER_, _MYSQLDATABASE_ environment
-variables. Password can be specified either in the _MYSQLPWD_
-environment variable or provided at the password prompt.
+database to retrieve table schema and data. Set the `-driver=mysql`
+and provide the environment variables `_MYSQLHOST_`, `_MYSQLPORT_`, 
+`_MYSQLUSER_`, `_MYSQLDATABASE_`. Password can be specified either 
+in the `_MYSQLPWD_` environment variable or provided at the password 
+prompt.
+
+
+#### 1.5 Direct access to DynamoDB
+
+In this case, HarbourBridge connects directly to the DynamoDB
+database to retrieve table schema and data. Set the `-driver=dynamodb`
+and provide the environment variables `AWS_ACCESS_KEY_ID`, 
+`AWS_SECRET_ACCESS_KEY`, `AWS_REGION`. If you use a custom endpoint for 
+dynamodb, you can specify that using the environment variable 
+`DYNAMODB_ENDPOINT_OVERRIDE`.
 
 ### 2. Verify dump output
 
-Next, verify that pg_dump/mysqldump is generating plain-text output. If your database is
-small, try running
+Next, verify that pg_dump/mysqldump is generating plain-text output. If your 
+database is small, try running
 
 ```sh
 { pg_dump/mysqldump } > file
@@ -393,10 +430,11 @@ and look at the output file. It should be a plain-text file containing SQL
 commands. If your database is large, consider just dumping the schema via the
 `--schema-only` for pg_dump and `--no-data` for mysqldump command-line option.
 
-pg_dump/mysqldump can export data in a variety of formats, but HarbourBridge only accepts
-`plain` format (aka plain-text). See the
+pg_dump/mysqldump can export data in a variety of formats, but HarbourBridge 
+only accepts `plain` format (aka plain-text). See the
 [pg_dump documentation](https://www.postgresql.org/docs/9.3/app-pgdump.html) and
-[mysqldump documentation](https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html) for details about formats.
+[mysqldump documentation](https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html) 
+for details about formats.
 
 ### 3. Debugging HarbourBridge
 
@@ -404,14 +442,14 @@ The HarbourBridge tool can fail for a number of reasons.
 
 #### 3.1 No space left on device
 
-HarbourBridge needs to read the pg_dump/mysqldump output twice, once to build a schema and
-once for data ingestion. When pg_dump/mysqldump output is directly piped to HarbourBridge,
-`stdin` is not seekable, and so we write the output to a temporary file. That
-temporary file is created via Go's ioutil.TempFile. On many systems, this
-creates a file in `/tmp`, which is sometimes configured with minimal space. A
-simple workaround is to separately run pg_dump/mysqldump and write its output to a file in
-a directory with sufficient space. For example, if the current working
-directory has space, then:
+HarbourBridge needs to read the pg_dump/mysqldump output twice, once to build 
+a schema and once for data ingestion. When pg_dump/mysqldump output is directly
+piped to HarbourBridge, `stdin` is not seekable, and so we write the output to 
+a temporary file. That temporary file is created via Go's ioutil.TempFile. 
+On many systems, this creates a file in `/tmp`, which is sometimes configured 
+with minimal space. A simple workaround is to separately run pg_dump/mysqldump 
+and write its output to a file in a directory with sufficient space. For example,
+if the current working directory has space, then:
 
 ```sh
 { pg_dump/mysqldump } > tmpfile
@@ -425,18 +463,18 @@ environment variable.
 #### 3.2 Unparsable dump output
 
 HarbourBridge uses the [pg_query_go](https://github.com/lfittl/pg_query_go)
-library for parsing pg_dump and [pingcap parser](https://github.com/pingcap/parser) for parsing mysqldump.
-It is possible that the pg_dump/mysqldump output is corrupted or uses features
-that aren't parseable. Parsing errors should generate an error message of the
-form `Error parsing last 54321 line(s) of input`.
+library for parsing pg_dump and [pingcap parser](https://github.com/pingcap/parser) 
+for parsing mysqldump. It is possible that the pg_dump/mysqldump output is 
+corrupted or uses features that aren't parseable. Parsing errors should 
+generate an error message of the form `Error parsing last 54321 line(s) of input`.
 
 #### 3.2 Credentials problems
 
 HarbourBridge uses standard Google Cloud credential mechanisms for accessing
 Cloud Spanner. If this is mis-configured, you may see errors containing
 "unauthenticated", or "cannot fetch token", or "could not find default
-credentials". You might need to run `gcloud auth application-default login`. See
-the [Before you begin](#before-you-being) section for details.
+credentials". You might need to run `gcloud auth application-default login`. 
+See the [Before you begin](#before-you-being) section for details.
 
 #### 3.4 Can't create database
 
diff --git a/cmd/cmd.go b/cmd/cmd.go
index e3e61df..1465bde 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -36,11 +36,11 @@ var (
 // 2. Create database (if schemaOnly is set to false)
 // 3. Run data conversion (if schemaOnly is set to false)
 // 4. Generate report
-func CommandLine(driver, projectID, instanceID, dbName string, dataOnly, schemaOnly bool, schemaSampleSize int64, sessionJSON string, ioHelper *conversion.IOStreams, outputFilePrefix string, now time.Time) error {
+func CommandLine(driver, targetDb, projectID, instanceID, dbName string, dataOnly, schemaOnly, skipForeignKeys bool, schemaSampleSize int64, sessionJSON string, ioHelper *conversion.IOStreams, outputFilePrefix string, now time.Time) error {
 	var conv *internal.Conv
 	var err error
 	if !dataOnly {
-		conv, err = conversion.SchemaConv(driver, ioHelper, schemaSampleSize)
+		conv, err = conversion.SchemaConv(driver, targetDb, ioHelper, schemaSampleSize)
 		if err != nil {
 			return err
 		}
@@ -79,10 +79,11 @@ func CommandLine(driver, projectID, instanceID, dbName string, dataOnly, schemaO
 		fmt.Printf("\nCan't finish data conversion for db %s: %v\n", db, err)
 		return fmt.Errorf("can't finish data conversion")
 	}
-
-	if err = conversion.UpdateDDLForeignKeys(projectID, instanceID, dbName, conv, ioHelper.Out); err != nil {
-		fmt.Printf("\nCan't perform update operation on db %s with foreign keys: %v\n", db, err)
-		return fmt.Errorf("can't perform update schema with foreign keys")
+	if !skipForeignKeys {
+		if err = conversion.UpdateDDLForeignKeys(projectID, instanceID, dbName, conv, ioHelper.Out); err != nil {
+			fmt.Printf("\nCan't perform update operation on db %s with foreign keys: %v\n", db, err)
+			return fmt.Errorf("can't perform update schema with foreign keys")
+		}
 	}
 	banner := conversion.GetBanner(now, db)
 	conversion.Report(driver, bw.DroppedRowsByTable(), ioHelper.BytesRead, banner, conv, outputFilePrefix+reportFile, ioHelper.Out)
diff --git a/conversion/conversion.go b/conversion/conversion.go
index 4bc62f5..55fbf57 100644
--- a/conversion/conversion.go
+++ b/conversion/conversion.go
@@ -36,6 +36,7 @@ import (
 	"os"
 	"os/exec"
 	"strings"
+	"sync"
 	"sync/atomic"
 	"syscall"
 	"time"
@@ -43,6 +44,7 @@ import (
 	sp "cloud.google.com/go/spanner"
 	database "cloud.google.com/go/spanner/admin/database/apiv1"
 	instance "cloud.google.com/go/spanner/admin/instance/apiv1"
+	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/session"
 	dydb "github.com/aws/aws-sdk-go/service/dynamodb"
 	_ "github.com/go-sql-driver/mysql"
@@ -72,14 +74,23 @@ const (
 	// DYNAMODB is the driver name for AWS DynamoDB.
 	// This is an experimental driver; implementation in progress.
 	DYNAMODB string = "dynamodb"
+
+	// Target db for which schema is being generated.
+	TARGET_SPANNER               string = "spanner"
+	TARGET_EXPERIMENTAL_POSTGRES string = "experimental_postgres"
+)
+
+var (
+	// Set the maximum number of concurrent workers during foreign key creation.
+	MaxWorkers = 10
 )
 
-func SchemaConv(driver string, ioHelper *IOStreams, schemaSampleSize int64) (*internal.Conv, error) {
+func SchemaConv(driver string, targetDb string, ioHelper *IOStreams, schemaSampleSize int64) (*internal.Conv, error) {
 	switch driver {
 	case POSTGRES, MYSQL:
-		return schemaFromSQL(driver)
+		return schemaFromSQL(driver, targetDb)
 	case PGDUMP, MYSQLDUMP:
-		return schemaFromDump(driver, ioHelper)
+		return schemaFromDump(driver, targetDb, ioHelper)
 	case DYNAMODB:
 		return schemaFromDynamoDB(schemaSampleSize)
 	default:
@@ -152,7 +163,7 @@ func mysqlDriverConfig() (string, error) {
 	return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", user, password, server, port, dbname), nil
 }
 
-func schemaFromSQL(driver string) (*internal.Conv, error) {
+func schemaFromSQL(driver string, targetDb string) (*internal.Conv, error) {
 	driverConfig, err := driverConfig(driver)
 	if err != nil {
 		return nil, err
@@ -162,6 +173,7 @@ func schemaFromSQL(driver string) (*internal.Conv, error) {
 		return nil, err
 	}
 	conv := internal.MakeConv()
+	conv.TargetDb = targetDb
 	err = ProcessInfoSchema(driver, conv, sourceDB)
 	if err != nil {
 		return nil, err
@@ -213,11 +225,20 @@ func dataFromSQL(driver string, config spanner.BatchWriterConfig, client *sp.Cli
 	return writer, nil
 }
 
+func getDynamoDBClientConfig() *aws.Config {
+	cfg := aws.Config{}
+	endpointOverride := os.Getenv("DYNAMODB_ENDPOINT_OVERRIDE")
+	if endpointOverride != "" {
+		cfg.Endpoint = aws.String(endpointOverride)
+	}
+	return &cfg
+}
+
 func schemaFromDynamoDB(sampleSize int64) (*internal.Conv, error) {
 	conv := internal.MakeConv()
 	mySession := session.Must(session.NewSession())
-	client := dydb.New(mySession)
-	err := dynamodb.ProcessSchema(conv, client, []string{}, sampleSize)
+	dydbClient := dydb.New(mySession, getDynamoDBClientConfig())
+	err := dynamodb.ProcessSchema(conv, dydbClient, []string{}, sampleSize)
 	if err != nil {
 		return nil, err
 	}
@@ -226,9 +247,8 @@ func schemaFromDynamoDB(sampleSize int64) (*internal.Conv, error) {
 
 func dataFromDynamoDB(config spanner.BatchWriterConfig, client *sp.Client, conv *internal.Conv) (*spanner.BatchWriter, error) {
 	mySession := session.Must(session.NewSession())
-	dyclient := dydb.New(mySession)
-
-	dynamodb.SetRowStats(conv, dyclient)
+	dydbClient := dydb.New(mySession, getDynamoDBClientConfig())
+	dynamodb.SetRowStats(conv, dydbClient)
 	totalRows := conv.Rows()
 	p := internal.NewProgress(totalRows, "Writing data to Spanner", internal.Verbose())
 
@@ -249,7 +269,7 @@ func dataFromDynamoDB(config spanner.BatchWriterConfig, client *sp.Client, conv
 			writer.AddRow(table, cols, vals)
 		})
 
-	err := dynamodb.ProcessData(conv, dyclient)
+	err := dynamodb.ProcessData(conv, dydbClient)
 	if err != nil {
 		return nil, err
 	}
@@ -262,7 +282,7 @@ type IOStreams struct {
 	BytesRead           int64
 }
 
-func schemaFromDump(driver string, ioHelper *IOStreams) (*internal.Conv, error) {
+func schemaFromDump(driver string, targetDb string, ioHelper *IOStreams) (*internal.Conv, error) {
 	f, n, err := getSeekable(ioHelper.In)
 	if err != nil {
 		printSeekError(driver, err, ioHelper.Out)
@@ -271,6 +291,7 @@ func schemaFromDump(driver string, ioHelper *IOStreams) (*internal.Conv, error)
 	ioHelper.SeekableIn = f
 	ioHelper.BytesRead = n
 	conv := internal.MakeConv()
+	conv.TargetDb = targetDb
 	p := internal.NewProgress(n, "Generating schema", internal.Verbose())
 	r := internal.NewReader(bufio.NewReader(f), p)
 	conv.SetSchemaMode() // Build schema and ignore data in dump.
@@ -436,6 +457,7 @@ func UpdateDDLForeignKeys(project, instance, dbName string, conv *internal.Conv,
 		return fmt.Errorf("can't create admin client: %w\n", analyzeError(err, project, instance))
 	}
 	defer adminClient.Close()
+
 	// The schema we send to Spanner excludes comments (since Cloud
 	// Spanner DDL doesn't accept them), and protects table and col names
 	// using backticks (to avoid any issues with Spanner reserved words).
@@ -445,26 +467,50 @@ func UpdateDDLForeignKeys(project, instance, dbName string, conv *internal.Conv,
 	}
 	msg := fmt.Sprintf("Updating schema of database %s in instance %s with foreign key constraints ...", dbName, instance)
 	p := internal.NewProgress(int64(len(fkStmts)), msg, internal.Verbose())
-	for i, fkStmt := range fkStmts {
-		// TODO: Improve performance of the foreign key constraint updates.
-		// For example, issue all of the update ops first before waiting for them to complete
-		// so that that can execute in parallel. We could also print out ids of the
-		// long-running operations.
-		op, err := adminClient.UpdateDatabaseDdl(ctx, &adminpb.UpdateDatabaseDdlRequest{
-			Database:   fmt.Sprintf("projects/%s/instances/%s/databases/%s", project, instance, dbName),
-			Statements: []string{fkStmt},
-		})
-		if err != nil {
-			fmt.Printf("Can't add foreign key with statement %s: %s\n", fkStmt, err)
-			conv.Unexpected(fmt.Sprintf("Can't add foreign key with statement %s: %s", fkStmt, err))
-			continue
-		}
-		if err := op.Wait(ctx); err != nil {
-			fmt.Printf("Can't add foreign key with statement %s: %s\n", fkStmt, err)
-			conv.Unexpected(fmt.Sprintf("Can't add foreign key with statement %s: %s", fkStmt, err))
-		}
-		internal.VerbosePrintln("Updated schema with statement: " + fkStmt)
-		p.MaybeReport(int64(i + 1))
+
+	workers := make(chan int, MaxWorkers)
+	for i := 1; i <= MaxWorkers; i++ {
+		workers <- i
+	}
+	var progressMutex sync.Mutex
+	progress := int64(0)
+
+	// We dispatch parallel foreign key create requests to ensure the backfill runs in parallel to reduce overall time.
+	// This cuts down the time taken to a third (approx) compared to Serial and Batched creation. We also do not want to create
+	// too many requests and get throttled due to network or hitting catalog memory limits.
+	// Ensure atmost `MaxWorkers` go routines run in parallel that each update the ddl with one foreign key statement.
+	for _, fkStmt := range fkStmts {
+		workerId := <-workers
+		go func(fkStmt string, workerId int) {
+			defer func() {
+				// Locking the progress reporting otherwise progress results displayed could be in random order.
+				progressMutex.Lock()
+				progress++
+				p.MaybeReport(progress)
+				progressMutex.Unlock()
+				workers <- workerId
+			}()
+			internal.VerbosePrintf("Submitting new FK create request for %s: %s\n", dbName, fkStmt)
+			op, err := adminClient.UpdateDatabaseDdl(ctx, &adminpb.UpdateDatabaseDdlRequest{
+				Database:   fmt.Sprintf("projects/%s/instances/%s/databases/%s", project, instance, dbName),
+				Statements: []string{fkStmt},
+			})
+			if err != nil {
+				fmt.Printf("Cannot submit request for create foreign key with statement %s: %s\n", fkStmt, err)
+				conv.Unexpected(fmt.Sprintf("Can't add foreign key with statement %s: %s", fkStmt, err))
+				return
+			}
+			if err := op.Wait(ctx); err != nil {
+				fmt.Printf("Can't add foreign key with statement %s: %s\n", fkStmt, err)
+				conv.Unexpected(fmt.Sprintf("Can't add foreign key with statement %s: %s", fkStmt, err))
+				return
+			}
+			internal.VerbosePrintln("Updated schema with statement: " + fkStmt)
+		}(fkStmt, workerId)
+	}
+	// Wait for all the goroutines to finish.
+	for i := 1; i <= MaxWorkers; i++ {
+		<-workers
 	}
 	p.Done()
 	return nil
@@ -499,6 +545,7 @@ func GetInstance(project string, out *os.File) (string, error) {
 		fmt.Fprintf(out, "Could not find any Spanner instances for project %s\n", project)
 		return "", fmt.Errorf("no Spanner instances for %s", project)
 	}
+
 	// Note: we could ask for user input to select/confirm which Spanner
 	// instance to use, but that interacts poorly with piping pg_dump/mysqldump data
 	// to the tool via stdin.
@@ -538,25 +585,25 @@ func getInstances(project string) ([]string, error) {
 
 // WriteSchemaFile writes DDL statements in a file. It includes CREATE TABLE
 // statements and ALTER TABLE statements to add foreign keys.
+// The parameter name should end with a .txt.
 func WriteSchemaFile(conv *internal.Conv, now time.Time, name string, out *os.File) {
 	f, err := os.Create(name)
 	if err != nil {
 		fmt.Fprintf(out, "Can't create schema file %s: %v\n", name, err)
 		return
 	}
-	// The schema file we write out includes comments, includes foreign keys
+
+	// The schema file we write out below is optimized for reading. It includes comments, foreign keys
 	// and doesn't add backticks around table and column names. This file is
 	// intended for explanatory and documentation purposes, and is not strictly
 	// legal Cloud Spanner DDL (Cloud Spanner doesn't currently support comments).
-	// Change 'Comments' to false and 'ProtectIds' to true to write out a
-	// schema file that is legal Cloud Spanner DDL.
-	ddl := conv.SpSchema.GetDDL(ddl.Config{Comments: true, ProtectIds: false, Tables: true, ForeignKeys: true})
-	if len(ddl) == 0 {
-		ddl = []string{"\n-- Schema is empty -- no tables found\n"}
+	spDDL := conv.SpSchema.GetDDL(ddl.Config{Comments: true, ProtectIds: false, Tables: true, ForeignKeys: true})
+	if len(spDDL) == 0 {
+		spDDL = []string{"\n-- Schema is empty -- no tables found\n"}
 	}
 	l := []string{
 		fmt.Sprintf("-- Schema generated %s\n", now.Format("2006-01-02 15:04:05")),
-		strings.Join(ddl, ";\n\n"),
+		strings.Join(spDDL, ";\n\n"),
 		"\n",
 	}
 	if _, err := f.WriteString(strings.Join(l, "")); err != nil {
@@ -564,6 +611,32 @@ func WriteSchemaFile(conv *internal.Conv, now time.Time, name string, out *os.Fi
 		return
 	}
 	fmt.Fprintf(out, "Wrote schema to file '%s'.\n", name)
+
+	// Convert <file_name>.<ext> to <file_name>.ddl.<ext>.
+	nameSplit := strings.Split(name, ".")
+	nameSplit = append(nameSplit[:len(nameSplit)-1], "ddl", nameSplit[len(nameSplit)-1])
+	name = strings.Join(nameSplit, ".")
+	f, err = os.Create(name)
+	if err != nil {
+		fmt.Fprintf(out, "Can't create legal schema ddl file %s: %v\n", name, err)
+		return
+	}
+
+	// We change 'Comments' to false and 'ProtectIds' to true below to write out a
+	// schema file that is a legal Cloud Spanner DDL.
+	spDDL = conv.SpSchema.GetDDL(ddl.Config{Comments: false, ProtectIds: true, Tables: true, ForeignKeys: true})
+	if len(spDDL) == 0 {
+		spDDL = []string{"\n-- Schema is empty -- no tables found\n"}
+	}
+	l = []string{
+		strings.Join(spDDL, ";\n\n"),
+		"\n",
+	}
+	if _, err = f.WriteString(strings.Join(l, "")); err != nil {
+		fmt.Fprintf(out, "Can't write out legal schema ddl file: %v\n", err)
+		return
+	}
+	fmt.Fprintf(out, "Wrote legal schema ddl to file '%s'.\n", name)
 }
 
 // WriteSessionFile writes conv struct to a file in JSON format.
@@ -587,6 +660,25 @@ func WriteSessionFile(conv *internal.Conv, name string, out *os.File) {
 	fmt.Fprintf(out, "Wrote session to file '%s'.\n", name)
 }
 
+// WriteConvGeneratedFiles creates a directory labeled downloads with the current timestamp
+// where it writes the sessionfile, report summary and DDLs then returns the directory where it writes.
+func WriteConvGeneratedFiles(conv *internal.Conv, dbName string, driver string, BytesRead int64, out *os.File) (string, error) {
+	now := time.Now()
+	dirPath := "harbour_bridge_output/" + dbName + "/"
+	err := os.MkdirAll(dirPath, os.ModePerm)
+	if err != nil {
+		fmt.Fprintf(out, "Can't create directory %s: %v\n", dirPath, err)
+		return "", err
+	}
+	schemaFileName := dirPath + dbName + "_schema.txt"
+	WriteSchemaFile(conv, now, schemaFileName, out)
+	reportFileName := dirPath + dbName + "_report.txt"
+	Report(driver, nil, BytesRead, "", conv, reportFileName, out)
+	sessionFileName := dirPath + dbName + ".session.json"
+	WriteSessionFile(conv, sessionFileName, out)
+	return dirPath, nil
+}
+
 // ReadSessionFile reads a session JSON file and
 // unmarshal it's content into *internal.Conv.
 func ReadSessionFile(conv *internal.Conv, sessionJSON string) error {
diff --git a/dynamodb/README.md b/dynamodb/README.md
index f6b8340..3641638 100644
--- a/dynamodb/README.md
+++ b/dynamodb/README.md
@@ -12,8 +12,15 @@ environment variable.
 
 Before running HarbourBridge, make sure that you have
 [set up your AWS credentials/region](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html)
-correctly. HarbourBridge accesses your DynamoDB database via the aws go sdk
-package.
+correctly (set the environment variables `AWS_ACCESS_KEY_ID`, 
+`AWS_SECRET_ACCESS_KEY`, `AWS_REGION`). HarbourBridge accesses your 
+DynamoDB database via the aws go sdk package. If you use a custom endpoint
+for dynamodb, you can specify that using the environment variable 
+`DYNAMODB_ENDPOINT_OVERRIDE`.
+
+```sh
+export DYNAMODB_ENDPOINT_OVERRIDE=http://dynamodb.us-west-2.amazonaws.com`
+```
 
 To use the tool on a DynamoDB database (it will migrate all tables),
 run
@@ -130,4 +137,5 @@ The row result contains the data type and data itself. According to our
 [inferred schema](#schema-inference), we will parse the row to a format that
 Cloud Spanner can support. If the value parsing fails, we would drop the entire
 row and record it as bad data in the report. If a column does not appear or 
-column has a NULL data type, we would process this as a NULL value in Cloud Spanner. 
+column has a NULL data type, we would process this as a NULL value in 
+Cloud Spanner. 
diff --git a/dynamodb/schema.go b/dynamodb/schema.go
index 6b3e3a8..53ce9aa 100644
--- a/dynamodb/schema.go
+++ b/dynamodb/schema.go
@@ -123,41 +123,55 @@ func analyzeMetadata(client dynamoClient, s *schema.Table) error {
 		return fmt.Errorf("failed to make a DescribeTable API call for table %v: %v", s.Name, err)
 	}
 
-	// Primary keys
+	// Primary keys.
 	for _, i := range result.Table.KeySchema {
 		s.PrimaryKeys = append(s.PrimaryKeys, schema.Key{Column: *i.AttributeName})
 	}
 
-	// Secondary indexes
+	// DynamoDB supports 2 types of indexes: Global Secondary Indexes (GSI) and Local Secondary Indexes (LSI).
+	// In GSI, dydb creates another global table for indexes that scales seperately.
+	// As for LSI, every partition in dydb maintains its own local index for that partition.
+	// For spanner, we should convert both these types as how dydb implements them is irrelevant.
+	// For more details, checkout https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html
+
+	// Convert secondary indexes from GlobalSecondaryIndexes.
 	for _, i := range result.Table.GlobalSecondaryIndexes {
-		var keys []schema.Key
-		for _, j := range i.KeySchema {
-			keys = append(keys, schema.Key{Column: *j.AttributeName})
-		}
-		// s.SecIndexes = append(s.SecIndexes, index{Name: *i.IndexName, Keys: keys})
-		s.Indexes = append(s.Indexes, schema.Index{Name: *i.IndexName, Keys: keys})
+		s.Indexes = append(s.Indexes, getSchemaIndexStruct(*i.IndexName, i.KeySchema))
+	}
+
+	// Convert secondary indexes from LocalSecondaryIndexes.
+	for _, i := range result.Table.LocalSecondaryIndexes {
+		s.Indexes = append(s.Indexes, getSchemaIndexStruct(*i.IndexName, i.KeySchema))
 	}
 
 	return nil
 }
 
+func getSchemaIndexStruct(indexName string, keySchema []*dynamodb.KeySchemaElement) schema.Index {
+	var keys []schema.Key
+	for _, j := range keySchema {
+		keys = append(keys, schema.Key{Column: *j.AttributeName})
+	}
+	return schema.Index{Name: indexName, Keys: keys}
+}
+
 func scanSampleData(client dynamoClient, sampleSize int64, table string) (map[string]map[string]int64, int64, error) {
 	// A map from column name to a count map of possible data types.
 	stats := make(map[string]map[string]int64)
 	var count int64
-	// Build the query input parameters
+	// Build the query input parameters.
 	params := &dynamodb.ScanInput{
 		TableName: aws.String(table),
 	}
 
 	for {
-		// Make the DynamoDB Query API call
+		// Make the DynamoDB Query API call.
 		result, err := client.Scan(params)
 		if err != nil {
 			return nil, 0, fmt.Errorf("failed to make Query API call for table %v: %v", table, err)
 		}
 
-		// Iterate the items returned
+		// Iterate the items returned.
 		for _, attrsMap := range result.Items {
 			for attrName, attr := range attrsMap {
 				if _, ok := stats[attrName]; !ok {
diff --git a/dynamodb/toddl.go b/dynamodb/toddl.go
index a0f747c..7092430 100644
--- a/dynamodb/toddl.go
+++ b/dynamodb/toddl.go
@@ -28,6 +28,19 @@ import (
 // Spanner. It uses the source schema in conv.SrcSchema, and writes
 // the Spanner schema to conv.SpSchema.
 func schemaToDDL(conv *internal.Conv) error {
+	// Tracks Spanner names that have been used for indexes. We use this to ensure we generate unique names when
+	// we map from DynamoDB to Spanner since we want Spanner table names and index names to be distinct.
+	usedNames := make(map[string]bool)
+	// We need to pre-populate usedNames with Spanner table names to handle collisions
+	// with table names, foreign key names and index names.
+	for _, srcTable := range conv.SrcSchema {
+		spTableName, err := internal.GetSpannerTable(conv, srcTable.Name)
+		if err != nil {
+			conv.Unexpected(fmt.Sprintf("Couldn't map source table %s to Spanner: %s", srcTable.Name, err))
+			continue
+		}
+		usedNames[spTableName] = true
+	}
 	for _, srcTable := range conv.SrcSchema {
 		spTableName, err := internal.GetSpannerTable(conv, srcTable.Name)
 		if err != nil {
@@ -64,6 +77,7 @@ func schemaToDDL(conv *internal.Conv) error {
 			ColNames: spColNames,
 			ColDefs:  spColDef,
 			Pks:      cvtPrimaryKeys(conv, srcTable.Name, srcTable.PrimaryKeys),
+			Indexes:  cvtIndexes(conv, spTableName, srcTable.Name, srcTable.Indexes, usedNames),
 			Comment:  comment}
 	}
 	return nil
@@ -116,3 +130,27 @@ func cvtPrimaryKeys(conv *internal.Conv, srcTable string, srcKeys []schema.Key)
 	}
 	return spKeys
 }
+
+func cvtIndexes(conv *internal.Conv, spTableName string, srcTable string, srcIndexes []schema.Index, usedNames map[string]bool) []ddl.CreateIndex {
+	var spIndexes []ddl.CreateIndex
+	for _, srcIndex := range srcIndexes {
+		var spKeys []ddl.IndexKey
+		for _, k := range srcIndex.Keys {
+			spCol, err := internal.GetSpannerCol(conv, srcTable, k.Column, true)
+			if err != nil {
+				conv.Unexpected(fmt.Sprintf("Can't map index key column name for table %s", srcTable))
+				continue
+			}
+			spKeys = append(spKeys, ddl.IndexKey{Col: spCol, Desc: k.Desc})
+		}
+		spIndexName := internal.ToSpannerIndexName(srcIndex.Name, usedNames)
+		spIndex := ddl.CreateIndex{
+			Name:   spIndexName,
+			Table:  spTableName,
+			Unique: srcIndex.Unique,
+			Keys:   spKeys,
+		}
+		spIndexes = append(spIndexes, spIndex)
+	}
+	return spIndexes
+}
diff --git a/frontend/js/components/Search/Search.component.js b/frontend/js/components/Search/Search.component.js
index 97f3e46..8b5c36c 100644
--- a/frontend/js/components/Search/Search.component.js
+++ b/frontend/js/components/Search/Search.component.js
@@ -1,60 +1,62 @@
 import Actions from "../../services/Action.service.js";
 class Search extends HTMLElement {
-  get tabId() {
-    return this.getAttribute("tabid");
-  }
+    get tabId() {
+        return this.getAttribute("tabid");
+    }
 
-  focusCampo(id){
-    var inputField = document.getElementById(id);
-    if (inputField != null && inputField.value.length != 0){
-        if (inputField.createTextRange){
-            var FieldRange = inputField.createTextRange();
-            FieldRange.moveStart('character',inputField.value.length);
-            FieldRange.collapse();
-            FieldRange.select();
-        }else if (inputField.selectionStart || inputField.selectionStart == '0') {
-            var elemLen = inputField.value.length;
-            inputField.selectionStart = elemLen;
-            inputField.selectionEnd = elemLen;
+    focusCampo(id) {
+        var inputField = document.getElementById(id);
+        if (inputField != null && inputField.value.length != 0) {
+            if (inputField.createTextRange) {
+                var FieldRange = inputField.createTextRange();
+                FieldRange.moveStart('character', inputField.value.length);
+                FieldRange.collapse();
+                FieldRange.select();
+            } else if (inputField.selectionStart || inputField.selectionStart == '0') {
+                var elemLen = inputField.value.length;
+                inputField.selectionStart = elemLen;
+                inputField.selectionEnd = elemLen;
+                inputField.focus();
+            }
+        } else {
             inputField.focus();
         }
-    }else{
-        inputField.focus();
     }
-  }
 
-  connectedCallback() {
-    this.render();
-  }
+    connectedCallback() {
+        this.render();
+    }
+
+    render() {
 
-  render() {
-  
-    this.innerHTML = `
-    <form class="form-inline d-flex justify-content-center md-form form-sm mt-0 search-form" >
-      <i class="fas fa-search" aria-hidden="true"></i>
-      <input class="form-control form-control-sm ml-3 w-75 search-box" type="text" 
-      placeholder="Search table" value="${Actions.getSearchInputValue(this.tabId)}" id="search-input" autocomplete='off' aria-label="Search" >
-    </form>`;
+        this.innerHTML = `
+        <form class="form-inline d-flex justify-content-center md-form form-sm mt-0 search-form" >
+          <i class="fas fa-search" aria-hidden="true"></i>
+          <input class="form-control form-control-sm ml-3 w-75 search-box" type="text" 
+          placeholder="Search table" value="${Actions.getSearchInputValue(this.tabId)}" id="search-input" autocomplete='off' aria-label="Search" >
+        </form>`;
 
-    document
-      .getElementById('search-input')
-      .addEventListener("keyup", (e) =>
-        Actions.SearchTable(
-          document.getElementById('search-input').value,
-          this.tabId
-        )
-      );
-    let search = document.getElementById('search-input');
-    let value = Actions.getSearchInputValue(Actions.getCurrentTab())
-    if(value.length > 0){
-      search.value= value;
-      this.focusCampo('search-input')
+        document
+            .getElementById('search-input')
+            .addEventListener("keyup", (e) => {
+                if (e.key === "Enter" || e.target.value.length === 0) {
+                    e.preventDefault()
+                    Actions.SearchTable(
+                        document.getElementById('search-input').value,
+                        this.tabId
+                    )
+                }
+            }
+            );
+        let value = Actions.getSearchInputValue(Actions.getCurrentTab())
+        if (value.length > 0) {
+            this.focusCampo('search-input')
+        }
     }
-  }
 
-  constructor() {
-    super();
-  }
+    constructor() {
+        super();
+    }
 }
 
-window.customElements.define("hb-search", Search);
+window.customElements.define("hb-search", Search);
\ No newline at end of file
diff --git a/frontend/js/config/constantData.js b/frontend/js/config/constantData.js
index 0418766..a08dc72 100644
--- a/frontend/js/config/constantData.js
+++ b/frontend/js/config/constantData.js
@@ -1,102 +1,97 @@
 export const NAVLINKS = {
-  logo: {
-    css: { nav: "navbar navbar-static-top", img: "logo" },
-    img: { src: "../../../Icons/Icons/google-spanner-logo.png" },
-  },
-
-  links: [
-    {
-      text: "Home",
-      href: "#/",
-      aTagId: "homeScreen",
-      name: "headerMenu",
+    logo: {
+        css: { nav: "navbar navbar-static-top", img: "logo" },
+        img: { src: "../../../Icons/Icons/google-spanner-logo.png" },
     },
 
-    {
-      text: "Schema Conversion",
-      href: "javascript:;",
-      aTagId: "schemaScreen",
-      name: "headerMenu",
-    },
+    links: [{
+            text: "Home",
+            href: "#/",
+            aTagId: "homeScreen",
+            name: "headerMenu",
+        },
 
-    {
-      text: "Instructions",
-      href: "#/instructions",
-      aTagId: "instructions",
-      name: "headerMenu",
-    },
-  ],
+        {
+            text: "Schema Conversion",
+            href: "javascript:;",
+            aTagId: "schemaScreen",
+            name: "headerMenu",
+        },
+
+        {
+            text: "Instructions",
+            href: "#/instructions",
+            aTagId: "instructions",
+            name: "headerMenu",
+        },
+    ],
 };
 
 export const CLASS_NAMES = {
-  heading: 'heading',
-  subHeading: 'sub-heading',
-  sessionHeading : 'session-heading',
-  text: 'text'
+    heading: 'heading',
+    subHeading: 'sub-heading',
+    sessionHeading: 'session-heading',
+    text: 'text'
 }
 
 export const MODALCONFIGS = {
-  CONNECT_TO_DB_MODAL_BUTTONS: [{ value: "Connect", id: "connect-button", disabledProp: "disabled" }],
-  LOAD_DB_DUMP_MODAL_BUTTONS: [{ value: "Confirm", id: "load-connect-button", disabledProp: "disabled", modalDismiss: true }],
-  LOAD_SESSION_MODAL_BUTTONS: [{ value: "Confirm", id: "load-session-button", disabledProp: "disabled", modalDismiss: true }],
-  CONNECTION_SUCCESS_MODAL: [{ value: "Convert", id: "convert-button", disabledProp: "" }],
-  CONNECTION_FAILURE_MODAL: [{ value: "Ok", id: "connection-failure-button", disabledProp: "" }],
-  EDIT_GLOBAL_DATATYPE_MODAL: [{ value: "Next", id: "data-type-button", disabledProp: "" }],
-  EDIT_TABLE_WARNING_MODAL: [{ value: "Ok", id: "edit-table-warning", disabledProp: "" }],
-  ADD_INDEX_MODAL: [{ value: "CREATE", id: "create-index-button", disabledProp: "disabled", modalDismiss: true }],
-  FK_DROP_WARNING_MODAL: [{ value: "Yes", id: "fk-drop-confirm", disabledProp: "" }, { value: "No", id: "fk-drop-cancel", disabledProp: "" }],
+    CONNECT_TO_DB_MODAL_BUTTONS: [{ value: "Connect", id: "connect-button", disabledProp: "disabled" }],
+    LOAD_DB_DUMP_MODAL_BUTTONS: [{ value: "Confirm", id: "load-connect-button", disabledProp: "disabled", modalDismiss: true }],
+    LOAD_SESSION_MODAL_BUTTONS: [{ value: "Confirm", id: "load-session-button", disabledProp: "disabled", modalDismiss: true }],
+    CONNECTION_SUCCESS_MODAL: [{ value: "Convert", id: "convert-button", disabledProp: "" }],
+    CONNECTION_FAILURE_MODAL: [{ value: "Ok", id: "connection-failure-button", disabledProp: "" }],
+    EDIT_GLOBAL_DATATYPE_MODAL: [{ value: "Next", id: "data-type-button", disabledProp: "" }],
+    EDIT_TABLE_WARNING_MODAL: [{ value: "Ok", id: "edit-table-warning", disabledProp: "" }],
+    ADD_INDEX_MODAL: [{ value: "CREATE", id: "create-index-button", disabledProp: "disabled", modalDismiss: true }],
+    FK_DROP_WARNING_MODAL: [{ value: "Yes", id: "fk-drop-confirm", disabledProp: "" }, { value: "No", id: "fk-drop-cancel", disabledProp: "" }],
 }
 
-export const MAIN_PAGE_ICONS = [
-  {
-    image: "Icons/Icons/Group 2048.svg",
-    imageAltText: "connect to db",
-    label: "Connect to Database",
-    action: "openConnectionModal",
-    modalDataTarget: "#connectToDbModal",
-  },
+export const MAIN_PAGE_ICONS = [{
+        image: "Icons/Icons/Group 2048.svg",
+        imageAltText: "connect to db",
+        label: "Connect to Database",
+        action: "openConnectionModal",
+        modalDataTarget: "#connectToDbModal",
+    },
 
-  {
-    image: "Icons/Icons/Group 2049.svg",
-    imageAltText: "load database image",
-    label: "Load Database Dump",
-    action: "openDumpLoadingModal",
-    modalDataTarget: "#loadDatabaseDumpModal",
-  },
+    {
+        image: "Icons/Icons/Group 2049.svg",
+        imageAltText: "load database image",
+        label: "Load Database Dump",
+        action: "openDumpLoadingModal",
+        modalDataTarget: "#loadDatabaseDumpModal",
+    },
 
-  {
-    image: "Icons/Icons/importIcon2.jpg",
-    imageAltText: "Import schema image",
-    label: "Load Session File",
-    action: "openSessionFileLoadModal",
-    modalDataTarget: "#loadSchemaModal",
-  },
+    {
+        image: "Icons/Icons/importIcon2.jpg",
+        imageAltText: "Import schema image",
+        label: "Load Session File",
+        action: "openSessionFileLoadModal",
+        modalDataTarget: "#loadSchemaModal",
+    },
 ];
 
 export const MAIN_PAGE_STATIC_CONTENT = {
-  HEADING_TEXT: "Welcome To HarborBridge",
-  SUB_HEADING_TEXT: "Connect or import your database",
-  CONNECTION_SUCCESS_CONTENT: "Please click on convert button to proceed with schema conversion",
-  CONNECTION_FAILURE_CONTENT: "Please check database configuration details and try again !!",
+    HEADING_TEXT: "Welcome To HarborBridge",
+    SUB_HEADING_TEXT: "Connect or import your database",
+    CONNECTION_SUCCESS_CONTENT: "Please click on convert button to proceed with schema conversion",
+    CONNECTION_FAILURE_CONTENT: "Please check database configuration details and try again !!",
 }
 
-export const TAB_CONFIG_DATA = [
-  {
-    id: "report",
-    text: "Schema",
-  },
-
-  {
-    id: "ddl",
-    text: "DDL",
-  },
-  
-  {
-    id: "summary",
-    text: "Warnings",
-  },
-];
+export const TAB_CONFIG_DATA = [{
+        id: "report",
+        text: "Schema",
+    },
 
-export const HISTORY_TABLE_HEADING = "Conversion history";
+    {
+        id: "ddl",
+        text: "DDL",
+    },
 
+    {
+        id: "summary",
+        text: "Warnings",
+    },
+];
 
+export const HISTORY_TABLE_HEADING = "Conversion history";
\ No newline at end of file
diff --git a/frontend/js/pages/Instructions/Instructions.page.js b/frontend/js/pages/Instructions/Instructions.page.js
index 3864f62..3c9fd47 100644
--- a/frontend/js/pages/Instructions/Instructions.page.js
+++ b/frontend/js/pages/Instructions/Instructions.page.js
@@ -5,7 +5,7 @@ class Instructions extends HTMLElement {
     connectedCallback() {
         this.render();
         setActiveSelectedMenu('instructions');
-   }
+    }
 
     render() {
         this.innerHTML = `
diff --git a/frontend/js/pages/SchemaConversionScreen/SchemaConversionScreen.page.js b/frontend/js/pages/SchemaConversionScreen/SchemaConversionScreen.page.js
index 7c1be3c..3620b8b 100644
--- a/frontend/js/pages/SchemaConversionScreen/SchemaConversionScreen.page.js
+++ b/frontend/js/pages/SchemaConversionScreen/SchemaConversionScreen.page.js
@@ -16,81 +16,81 @@ import "../../services/Fetch.service.js";
 
 class SchemaConversionScreen extends HTMLElement {
 
-  get testing(){
-    return this.getAttribute("testing");
-  }
-
-  connectedCallback() {
-    this.stateObserver = setInterval(this.observeState, 150);
-    Actions.showSpinner()
-    this.render();
-  }
-
-  disconnectedCallback() {
-    clearInterval(this.stateObserver);
-  }
-
-  sendDatatoReportTab(tableNameArray, currentTabContent) {
-      for (let i = 0; i < tableNameArray.length; i++) {
-        let filterdata = {
-          SpSchema: currentTabContent.SpSchema[tableNameArray[i]],
-          SrcSchema: currentTabContent.SrcSchema[tableNameArray[i]],
-          ToSource: currentTabContent.ToSource[tableNameArray[i]],
-          ToSpanner: currentTabContent.ToSpanner[tableNameArray[i]],
-          summary : Store.getinstance().tableData["summaryTabContent"][tableNameArray[i]],
-          currentPageNumber : Actions.getCurrentPageNumber(i)
-        };
-        let component = document.querySelector(`#reportTab${i}`);
-        component.data = filterdata;
-      }
-  }
-
-  set Data(data) {
-      this.data = data;
-      this.render();
-  }
+    get testing() {
+        return this.getAttribute("testing");
+    }
 
-  getChangingValue(currentTab) {
-    currentTab = currentTab.substring(0, currentTab.length - 3);
-    let currentArray = Actions.carouselStatus(currentTab);
-    let flag = "Expand All";
-    for (let i = 0; i < currentArray.length; i++) {
-      if (currentArray[i] == true) {
-        flag = "Collapse All";
-      }
+    connectedCallback() {
+        this.stateObserver = setInterval(this.observeState, 150);
+        Actions.showSpinner()
+        this.render();
     }
-    return flag;
-  }
 
-  observeState = () => {
-    let updatedData = Store.getinstance();
-    if (JSON.stringify(updatedData) !== JSON.stringify(this.data)) {
-      this.data = JSON.parse(JSON.stringify(updatedData));
-      this.render();
+    disconnectedCallback() {
+        clearInterval(this.stateObserver);
     }
-  };
 
-  render() {
-    if (!this.data) {
-      return;
+    sendDatatoReportTab(tableNameArray, currentTabContent) {
+        for (let i = 0; i < tableNameArray.length; i++) {
+            let filterdata = {
+                SpSchema: currentTabContent.SpSchema[tableNameArray[i]],
+                SrcSchema: currentTabContent.SrcSchema[tableNameArray[i]],
+                ToSource: currentTabContent.ToSource[tableNameArray[i]],
+                ToSpanner: currentTabContent.ToSpanner[tableNameArray[i]],
+                summary: Store.getinstance().tableData["summaryTabContent"][tableNameArray[i]],
+                currentPageNumber: Actions.getCurrentPageNumber(i)
+            };
+            let component = document.querySelector(`#reportTab${i}`);
+            component.data = filterdata;
+        }
     }
-    const { currentTab, tableData, tableBorderData,searchInputValue } = this.data;
-    let currentTabContent = tableData[`${currentTab}Content`];
-    if(Object.keys(currentTabContent).length == 0) {
-      Actions.hideSpinner();
-      return;
+
+    set Data(data) {
+        this.data = data;
+        this.render();
     }
-    const changingText = this.getChangingValue(currentTab);
-    let tableNameArray;
-    if (currentTab === "reportTab") {
-      tableNameArray = Object.keys(currentTabContent.SpSchema)
-                            .filter((title)=>title.indexOf(searchInputValue[currentTab]) > -1);
-    } else {
-      tableNameArray = Object.keys(currentTabContent)
-                            .filter((title)=>title.indexOf(searchInputValue[currentTab]) > -1);
+
+    getChangingValue(currentTab) {
+        currentTab = currentTab.substring(0, currentTab.length - 3);
+        let currentArray = Actions.carouselStatus(currentTab);
+        let flag = "Expand All";
+        for (let i = 0; i < currentArray.length; i++) {
+            if (currentArray[i] == true) {
+                flag = "Collapse All";
+            }
+        }
+        return flag;
     }
-    
-    this.innerHTML = `
+
+    observeState = () => {
+        let updatedData = Store.getinstance();
+        if (JSON.stringify(updatedData) !== JSON.stringify(this.data)) {
+            this.data = JSON.parse(JSON.stringify(updatedData));
+            this.render();
+        }
+    };
+
+    render() {
+        if (!this.data) {
+            return;
+        }
+        const { currentTab, tableData, tableBorderData, searchInputValue } = this.data;
+        let currentTabContent = tableData[`${currentTab}Content`];
+        if (Object.keys(currentTabContent).length == 0) {
+            Actions.hideSpinner();
+            return;
+        }
+        const changingText = this.getChangingValue(currentTab);
+        let tableNameArray;
+        if (currentTab === "reportTab") {
+            tableNameArray = Object.keys(currentTabContent.SpSchema)
+                .filter((title) => title.indexOf(searchInputValue[currentTab]) > -1);
+        } else {
+            tableNameArray = Object.keys(currentTabContent)
+                .filter((title) => title.indexOf(searchInputValue[currentTab]) > -1);
+        }
+
+        this.innerHTML = `
     <div class="summary-main-content" id='schema-screen-content'>
       <div id="snackbar"></div>
       <div>
@@ -193,4 +193,4 @@ class SchemaConversionScreen extends HTMLElement {
   }
 }
 
-window.customElements.define("hb-schema-conversion-screen", SchemaConversionScreen);
+window.customElements.define("hb-schema-conversion-screen", SchemaConversionScreen);
\ No newline at end of file
diff --git a/frontend/js/services/Action.service.js b/frontend/js/services/Action.service.js
index 9b9e129..ad0b395 100644
--- a/frontend/js/services/Action.service.js
+++ b/frontend/js/services/Action.service.js
@@ -544,6 +544,18 @@ const Actions = (() => {
                 'UpdateCols': {}
             }
             let newColArrayForDuplicateCheck = [];
+            let columnsNamesArray = [];
+            // We follow this approach to create columnsNamesArray to ensure the order of column names in the array is the consistent with what is seen in the UI.
+            // This is needed later on where we compare the array index with tableColumnNumber.
+            jQuery(tableId).each(function(index) {
+                if (index > 1) {
+                    let srcColumnName = document.getElementById('src-column-name-' + tableNumber + tableColumnNumber + tableColumnNumber).innerHTML;
+                    let originalColumnName = data.ToSpanner[tableName].Cols[srcColumnName];
+                    columnsNamesArray.push(originalColumnName)
+                    tableColumnNumber++;
+                }
+            });
+            tableColumnNumber = 0;
             jQuery(tableId).each(function(index) {
                 if (index > 1) {
                     let newColumnName;
@@ -562,7 +574,6 @@ const Actions = (() => {
                         errorMessage.push("Column name(s) cannot be empty");
                         columnNameEmpty = true;
                     } else {
-                        let columnsNamesArray = Object.keys(data.ToSpanner[tableName].Cols);
                         columnNameExists = false;
                         for (let k = 0; k < columnsNamesArray.length; k++) {
                             if (k != tableColumnNumber && newColumnName === columnsNamesArray[k]) {
@@ -918,11 +929,11 @@ const Actions = (() => {
             return Store.getCurrentPageNumber(idx)
         },
 
-        changePage: (tableIndex,pageindex) => {
-            if(Actions.getCurrentPageNumber(tableIndex) == pageindex){
+        changePage: (tableIndex, pageindex) => {
+            if (Actions.getCurrentPageNumber(tableIndex) == pageindex) {
                 Actions.hideSpinner();
             }
-            Store.setPageNumber(tableIndex,pageindex);
+            Store.setPageNumber(tableIndex, pageindex);
         }
 
 
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 2112086..bdb5c77 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1347,8 +1347,7 @@
             "version": "1.5.1",
             "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
             "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
-            "funding": [
-                {
+            "funding": [{
                     "type": "github",
                     "url": "https://github.com/sponsors/feross"
                 },
@@ -1458,8 +1457,7 @@
             "version": "5.7.1",
             "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
             "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
-            "funding": [
-                {
+            "funding": [{
                     "type": "github",
                     "url": "https://github.com/sponsors/feross"
                 },
@@ -3024,8 +3022,7 @@
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
             "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
-            "funding": [
-                {
+            "funding": [{
                     "type": "github",
                     "url": "https://github.com/sponsors/feross"
                 },
@@ -5750,8 +5747,7 @@
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
             "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
-            "funding": [
-                {
+            "funding": [{
                     "type": "github",
                     "url": "https://github.com/sponsors/feross"
                 },
@@ -6231,8 +6227,7 @@
             "version": "5.2.1",
             "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
             "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
-            "funding": [
-                {
+            "funding": [{
                     "type": "github",
                     "url": "https://github.com/sponsors/feross"
                 },
@@ -12593,4 +12588,4 @@
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/go.mod b/go.mod
index f0c28f8..4abad94 100644
--- a/go.mod
+++ b/go.mod
@@ -3,27 +3,26 @@ module github.com/cloudspannerecosystem/harbourbridge
 go 1.13
 
 require (
-	cloud.google.com/go v0.78.0
+	cloud.google.com/go v0.93.3
 	cloud.google.com/go/spanner v1.10.0
 	github.com/DATA-DOG/go-sqlmock v1.4.1
 	github.com/aws/aws-sdk-go v1.34.5
-	github.com/davecgh/go-spew v1.1.1
 	github.com/go-sql-driver/mysql v1.5.0
 	github.com/gorilla/handlers v1.5.0
 	github.com/gorilla/mux v1.7.3
-	github.com/goware/modvendor v0.0.0-20190516042800-ce72b408a8fe // indirect
-	github.com/juju/errors v0.0.0-20200330140219-3fe23663418f
 	github.com/lfittl/pg_query_go v1.0.0
 	github.com/lib/pq v1.9.0
-	github.com/mattn/go-zglob v0.0.1 // indirect
 	//github.com/pingcap/parser v3.0.12+incompatible
 	github.com/pingcap/parser v0.0.0-20200422082501-7329d80eaf2c
 	github.com/pingcap/tidb v1.1.0-beta.0.20200423105559-af376db3dc46
-	github.com/pingcap/tipb v0.0.0-20200417094153-7316d94df1ee // indirect
 	github.com/sirupsen/logrus v1.5.0 // indirect
-	github.com/stretchr/testify v1.5.1
+	github.com/stretchr/testify v1.6.1
 	golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
-	google.golang.org/api v0.40.0
-	google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c
-	google.golang.org/grpc v1.35.0
+	google.golang.org/api v0.54.0
+	google.golang.org/genproto v0.0.0-20210827211047-25e5f791fe06
 )
+
+// cloud.google.com/go will upgrade grpc to v1.40.0
+// We need keep the replacement since google.golang.org/grpc/naming isn't
+// available in higher versions.
+replace google.golang.org/grpc => google.golang.org/grpc v1.29.1
diff --git a/go.sum b/go.sum
index c9054f5..34addaa 100644
--- a/go.sum
+++ b/go.sum
@@ -1,32 +1,28 @@
-bazil.org/fuse v0.0.0-20180421153158-65cc252bf669 h1:FNCRpXiquG1aoyqcIWVFmpTSKVcx2bQD38uZZeGtdlw=
-bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
-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.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/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.2 h1:CzaxDL0yS5OHsygr9wRodEjP93JHp67vzlRDGlVZTJw=
-cloud.google.com/go v0.46.2/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y=
 cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
 cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
 cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
 cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
 cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
-cloud.google.com/go v0.55.0 h1:eoz/lYxKSL4CNAiaUJ0ZfD1J3bfMYbU5B3rwM1C1EIU=
-cloud.google.com/go v0.55.0/go.mod h1:ZHmoY+/lIMNkN2+fBmuTiqZ4inFhvQad8ft7MT8IV5Y=
 cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
 cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
-cloud.google.com/go v0.60.0 h1:R+tDlceO7Ss+zyvtsdhTxacDyZ1k99xwskQ4FT7ruoM=
-cloud.google.com/go v0.60.0/go.mod h1:yw2G51M9IfRboUH61Us8GqCeF1PzPblB823Mn2q2eAU=
 cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
-cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
 cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
 cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
 cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
-cloud.google.com/go v0.78.0 h1:oKpsiyKMfVpwR3zSAkQixGzlVE5ovitBuO0qSmCf0bI=
 cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
+cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
+cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
+cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
+cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
+cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
+cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
+cloud.google.com/go v0.93.3 h1:wPBktZFzYBcCZVARvwVKqH1uEj+aLXofJEtrb4oOsio=
+cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
 cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@@ -39,14 +35,6 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
 cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
 cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
 cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
-cloud.google.com/go/spanner v1.0.0 h1:jLKThep5kbWLeBhLgtEfm/OPT08n1z7itVTR82WUBQg=
-cloud.google.com/go/spanner v1.0.0/go.mod h1:z7t0U9rMHnkwMx9CZr/AVr3h60tTWRyR4n17+emFjFE=
-cloud.google.com/go/spanner v1.0.1-0.20191018063856-b24632667359 h1:hXxnJY+Igw2K1cb/DpMeI3kK2nGWs5yGNFxDeraeg/0=
-cloud.google.com/go/spanner v1.0.1-0.20191018063856-b24632667359/go.mod h1:JSevdCvTOpvGQrJS7H86d6LzGIkWxlYm8C2nc6WNjmM=
-cloud.google.com/go/spanner v1.4.0 h1:gvmMlYl98fd0N3k16oFigS84giz0Z9OEkxak6PhqSYI=
-cloud.google.com/go/spanner v1.4.0/go.mod h1:7nV8byyEzhYJ/rVoqWDja7oF/IYQydFkZKoo8pynWTQ=
-cloud.google.com/go/spanner v1.7.0 h1:mvgDB+f4CfKXebTIi36aqY02eVG4nU/Z3KV6stQaYCc=
-cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk=
 cloud.google.com/go/spanner v1.10.0 h1:B2I4WJY5DyY2LW1iCeY+23hCTyWlsQWsupKlTW7l6ws=
 cloud.google.com/go/spanner v1.10.0/go.mod h1:eVM2ifUL7rm+wM4gAXMuwO8CpNjbAJXK7CKZjUtt8Jw=
 cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
@@ -66,6 +54,7 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
 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-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
 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=
@@ -88,10 +77,7 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
 github.com/chzyer/readline v0.0.0-20171208011716-f6d7a1f6fbf3/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y=
 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=
@@ -115,12 +101,10 @@ github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5je
 github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
 github.com/cznic/y v0.0.0-20170802143616-045f81c6662a/go.mod h1:1rk5VM7oSnA4vjp+hrLQ3HWHa+Y4yPCa3/CsJrcNnvs=
 github.com/danjacques/gofslock v0.0.0-20191023191349-0a45f885bc37/go.mod h1:DC3JtzuG7kxMvJ6dZmf2ymjNyoXwgtklr7FN+Um2B0U=
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 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=
 github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
-github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
 github.com/dgraph-io/ristretto v0.0.1 h1:cJwdnj42uV8Jg4+KLrYovLiCgIfz9wtWm6E6KA+1tLs=
 github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
@@ -135,11 +119,7 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
 github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -166,6 +146,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
 github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
 github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
@@ -210,14 +191,14 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
 github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
 github.com/golang/protobuf v0.0.0-20180814211427-aa810b61a9c7/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=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
 github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
 github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -225,52 +206,57 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
 github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
+github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 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-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-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
+github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
 github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20190930153522-6ce02741cba3/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3 h1:SRgJV+IoxM5MKyFdlSUeNy6/ycRUF2yBAKdAQswoHUk=
 github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
+github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
 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/google/uuid v1.1.2/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=
@@ -282,8 +268,6 @@ github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY
 github.com/gorilla/websocket v1.2.0/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/goware/modvendor v0.0.0-20190516042800-ce72b408a8fe h1:UH0Uvgblv5tsV8QWER09+ZtxIs90pjelhtvWndSJvRM=
-github.com/goware/modvendor v0.0.0-20190516042800-ce72b408a8fe/go.mod h1:iTv+KoD55Rr5NW/P2WlDEJQ2Wa7+LRblD6/u/xJl/i0=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
@@ -294,7 +278,6 @@ github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtg
 github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
 github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69/go.mod h1:YLEMZOtU+AZ7dhN9T/IpGhXVGly2bvkJQ+zxj3WeVQo=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -316,10 +299,7 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
 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/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
-github.com/juju/errors v0.0.0-20200330140219-3fe23663418f h1:MCOvExGLpaSIzLYB4iQXEHP4jYVU6vmzLNQPdMVrxnM=
-github.com/juju/errors v0.0.0-20200330140219-3fe23663418f/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
 github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY=
 github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
@@ -330,6 +310,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
 github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 h1:2U0HzY8BJ8hVwDKIzp7y4voR9CX/nvcfymLmg2UiOio=
 github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@@ -343,8 +324,6 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx
 github.com/lfittl/pg_query_go v1.0.0 h1:rcHZK5DBEUoxtO6dACP+UVCHKtA1ZsELBW0rSjOXMAE=
 github.com/lfittl/pg_query_go v1.0.0/go.mod h1:jcikG62RKf+NIWmbLzjjk73m4x6um2pKf3h+TJyINms=
 github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
-github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
-github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
 github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -362,10 +341,6 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
 github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
-github.com/mattn/go-zglob v0.0.0-20180803001819-2ea3427bfa53 h1:tGfIHhDghvEnneeRhODvGYOt305TPwingKt6p90F4MU=
-github.com/mattn/go-zglob v0.0.0-20180803001819-2ea3427bfa53/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
-github.com/mattn/go-zglob v0.0.1 h1:xsEx/XUoVlI6yXjqBK062zYhRTZltCNmYPx6v+8DNaY=
-github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
 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/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
@@ -469,9 +444,12 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh
 github.com/sergi/go-diff v1.0.1-0.20180205163309-da645544ed44/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/shirou/gopsutil v2.19.10+incompatible h1:lA4Pi29JEVIQIgATSeftHSY0rMGI9CLrl2ZvDLiahto=
 github.com/shirou/gopsutil v2.19.10+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U=
 github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
+github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
 github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/vfsgen v0.0.0-20181020040650-a97a25d856ca h1:3fECS8atRjByijiI8yYiuwLwQ2ZxXobW7ua/8GRB3pI=
 github.com/shurcooL/vfsgen v0.0.0-20181020040650-a97a25d856ca/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q=
@@ -488,10 +466,10 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 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=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
 github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=
 github.com/swaggo/http-swagger v0.0.0-20200103000832-0e9263c4b516/go.mod h1:O1lAbCgAAX/KZ80LM/OXwtWFI/5TvZlwxSg8Cq08PV0=
@@ -534,21 +512,20 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
 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.etcd.io/etcd v0.5.0-alpha.5.0.20191023171146-3cf2f69b5738 h1:lWF4f9Nypl1ZqSb4gLeh/DGvBYVaUYHuiB93teOmwgc=
 go.etcd.io/etcd v0.5.0-alpha.5.0.20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
 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.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
 go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
 go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
+go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
 go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
@@ -563,6 +540,7 @@ go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+
 go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
 go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
 go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
 go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
@@ -576,10 +554,8 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACk
 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-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/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/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -587,7 +563,6 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20190912063710-ac5d2bfcbfe0/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
 golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
 golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
@@ -596,20 +571,18 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-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=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac h1:8R1esu+8QioDxo4E4mX6bFztO+dMTM49DNAaWfO5OeY=
 golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
 golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
 golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -617,12 +590,12 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 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-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -636,7 +609,6 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 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 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -648,52 +620,52 @@ golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
 golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE=
 golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
 golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 h1:a8jGStKg0XqKDlKqjLrXn0ioF5MH36pT7Z0BRTqLhbk=
+golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 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=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99 h1:5vD4XjIc0X5+kHZjx4UecYdjA6mJo+XXNoaW0EjU5Os=
 golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93 h1:alLDrZkL34Y2bnGHfvC1CYBRBXCXgx8AC2vY4MRtYX4=
 golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a h1:4Kd8OPUx1xgUwrHDaviWZO8MsgoZTZYC3g+8m16RBww=
+golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 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/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -709,7 +681,6 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/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 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -724,42 +695,43 @@ golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d h1:62ap6LNOjDU6uGmKXHJbSfciMoV+FeI1sRXx/pLDL44=
-golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
 golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200828194041-157a740278f4 h1:kCCpuwSAoYJPkNc6x0xT9yTtV4oKtARo4RGBQWOfg9E=
 golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200916084744-dbad9cb7cb7a h1:chkwkn8HYWVtTE5DCQNKYlkyptadXYY0+PuyaVdyMo4=
-golang.org/x/sys v0.0.0-20200916084744-dbad9cb7cb7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43 h1:SgQ6LNaYJU0JIuEHv9+s6EbhSCwYeAf5Yvj6lpYlqAE=
 golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210304195927-444254391f19 h1:B4VmpLO5AaG7XCNX3FF2Zj6LSat1kaKifJN9yL09sCg=
-golang.org/x/sys v0.0.0-20210304195927-444254391f19/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
+golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/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=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 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/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -768,7 +740,6 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/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-20190114222345-bf090417da8b/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@@ -784,8 +755,6 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
 golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190917162342-3b4f30a44f3b h1:5PDpbTpVmeVPIQOoxshLbs4ATaIDQrZN5z3nTUtm2+8=
-golang.org/x/tools v0.0.0-20190917162342-3b4f30a44f3b/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -801,8 +770,6 @@ golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapK
 golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200125223703-d33eef8e6825 h1:aNQeSIHKi0RWpKA5NO0CqyLjx6Beh5l0LLUEnndEjz0=
-golang.org/x/tools v0.0.0-20200125223703-d33eef8e6825/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -812,24 +779,17 @@ golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapK
 golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
 golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200318054722-11a475a590ac/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
 golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200325203130-f53864d0dba1 h1:odiryKYJy7CjdrZxhrcE1Z8L9+kGyGZOnfpuauvdCeU=
 golang.org/x/tools v0.0.0-20200325203130-f53864d0dba1/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
 golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
 golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200630154851-b2d8b0336632 h1:fxWeLV4ol1icb47+btwfzuf8gOkJE/PPo9bLjSvZzA8=
-golang.org/x/tools v0.0.0-20200630154851-b2d8b0336632/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200828161849-5deb26317202/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200903185744-af4cc2cd812e h1:RvNtqusJ+6DJ07/by/M84a6/Dd17XU6n8QvhvknjJno=
 golang.org/x/tools v0.0.0-20200903185744-af4cc2cd812e/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
 golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
 golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -837,16 +797,20 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 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=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
 google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8=
 google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
 google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
 google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
@@ -854,26 +818,28 @@ google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb
 google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
 google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
 google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40=
 google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
 google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
 google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.28.0 h1:jMF5hhVfMkTZwHW1SDpKq5CkgWLXOb31Foaca9Zr3oM=
 google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
 google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
 google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
-google.golang.org/api v0.31.0 h1:1w5Sz/puhxFo9lTtip2n47k7toB/U2nCqOKNHd3Yrbo=
 google.golang.org/api v0.31.0/go.mod h1:CL+9IBCa2WWU6gRuBWaKqGWLFFwbEUXkfeMkHLQWYWo=
 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
 google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
-google.golang.org/api v0.40.0 h1:uWrpz12dpVPn7cojP82mk02XDgTJLDPc2KbVTxrWb4A=
 google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
+google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
+google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
+google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
+google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
+google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
+google.golang.org/api v0.54.0 h1:ECJUVngj71QI6XEm7b1sAf8BljU5inEhMbKPR8Lxhhk=
+google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
 google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@@ -885,7 +851,6 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn
 google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51 h1:Ex1mq5jaJof+kRnYi3SlYJ8KKa9Ao3NHyIT5XJ1gF6U=
 google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
 google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
 google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
@@ -900,53 +865,41 @@ google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfG
 google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200318110522-7735f76e9fa5 h1:Bs8aCQBqwnuSvG/tB3ip/W8JLeuQt1+1ppSHYi4n9RM=
-google.golang.org/genproto v0.0.0-20200318110522-7735f76e9fa5/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
-google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5 h1:a/Sqq5B3dGnmxhuJZIHFsIxhEkqElErr5TaU6IqBAj0=
-google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d h1:92D1fum1bJLKSdr11OJ+54YeCMCGYIygTA7R/YZxH5M=
 google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c h1:7A9LQhrZmuCPI79/sYSbscFqBp4XFYf6oaIQuV1xji4=
 google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/grpc v0.0.0-20180607172857-7a6a684ca69e/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
-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.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
-google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
+google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
+google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
+google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
+google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
+google.golang.org/genproto v0.0.0-20210827211047-25e5f791fe06 h1:Ogdiaj9EMVKYHnDsESxwlTr/k5eqCdwoQVJEcdg0NbE=
+google.golang.org/genproto v0.0.0-20210827211047-25e5f791fe06/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs=
-google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
-google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8=
-google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -956,8 +909,11 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
-google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 gopkg.in/alecthomas/gometalinter.v2 v2.0.12/go.mod h1:NDRytsqEZyolNuAgTzJkZMkSQM7FIKyzVzGhjB/qfYo=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA=
@@ -980,19 +936,21 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
 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=
-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.3/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.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=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 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/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 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/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
diff --git a/internal/convert.go b/internal/convert.go
index cae88aa..80a7db6 100644
--- a/internal/convert.go
+++ b/internal/convert.go
@@ -36,6 +36,7 @@ type Conv struct {
 	sampleBadRows  rowSamples     // Rows that generated errors during conversion.
 	Stats          stats
 	TimezoneOffset string // Timezone offset for timestamp conversion.
+	TargetDb       string // The target database to which HarbourBridge is writing.
 }
 
 type mode int
diff --git a/main.go b/main.go
index 75d119f..a8dc0fa 100644
--- a/main.go
+++ b/main.go
@@ -41,8 +41,11 @@ var (
 	verbose          bool
 	schemaOnly       bool
 	dataOnly         bool
+	skipForeignKeys  bool
 	sessionJSON      string
 	webapi           bool
+	dumpFilePath     string
+	targetDb         = conversion.TARGET_SPANNER
 )
 
 func init() {
@@ -54,8 +57,11 @@ func init() {
 	flag.BoolVar(&verbose, "v", false, "verbose: print additional output")
 	flag.BoolVar(&schemaOnly, "schema-only", false, "schema-only: in this mode we do schema conversion, but skip data conversion")
 	flag.BoolVar(&dataOnly, "data-only", false, "data-only: in this mode we skip schema conversion and just do data conversion (use the session flag to specify the session file for schema and data mapping)")
+	flag.BoolVar(&skipForeignKeys, "skip-foreign-keys", false, "skip-foreign-keys: if true, skip creating foreign keys after data migration is complete (ddl statements for foreign keys can still be found in the downloaded schema.ddl.txt file and the same can be applied separately)")
 	flag.StringVar(&sessionJSON, "session", "", "session: specifies the file we restore session state from (used in schema-only to provide schema and data mapping)")
 	flag.BoolVar(&webapi, "web", false, "web: run the web interface (experimental)")
+	flag.StringVar(&dumpFilePath, "dump-file", "", "dump-file: location of dump file to process")
+	flag.StringVar(&targetDb, "target-db", conversion.TARGET_SPANNER, "target-db: Specifies the target DB. Defaults to spanner")
 }
 
 func usage() {
@@ -92,9 +98,21 @@ func main() {
 	if dataOnly && sessionJSON == "" {
 		panic(fmt.Errorf("when using data-only mode, the session must specify the session file to use"))
 	}
+	if schemaOnly && skipForeignKeys {
+		panic(fmt.Errorf("can't use both schema-only and skip-foreign-keys at once. Foreign Key creation can only be skipped when data migration takes place."))
+	}
 
-	ioHelper := &conversion.IOStreams{In: os.Stdin, Out: os.Stdout}
-	fmt.Println("Using driver (source DB):", driverName)
+	if targetDb == conversion.TARGET_EXPERIMENTAL_POSTGRES {
+		if !(driverName == conversion.PGDUMP || driverName == conversion.POSTGRES) {
+			panic(fmt.Errorf("can only convert to experimental postgres when source %s or %s. (target-db: %s driver: %s)", conversion.PGDUMP, conversion.POSTGRES, targetDb, driverName))
+		}
+	} else if targetDb != conversion.TARGET_SPANNER {
+		panic(fmt.Errorf("unkown target-db %s", targetDb))
+	}
+
+	input := loadInput(dumpFilePath)
+	ioHelper := &conversion.IOStreams{In: input, Out: os.Stdout}
+	fmt.Printf("Using driver (source DB): %s target-db: %s\n", driverName, targetDb)
 
 	var project, instance string
 	if !schemaOnly {
@@ -134,8 +152,23 @@ func main() {
 
 	// TODO (agasheesh@): Collect all the config state in a single struct and pass the same to CommandLine instead of
 	// passing multiple parameters. Config state would be populated by parsing the flags and environment variables.
-	err = cmd.CommandLine(driverName, project, instance, dbName, dataOnly, schemaOnly, schemaSampleSize, sessionJSON, ioHelper, filePrefix, now)
+	err = cmd.CommandLine(driverName, targetDb, project, instance, dbName, dataOnly, schemaOnly, skipForeignKeys, schemaSampleSize, sessionJSON, ioHelper, filePrefix, now)
 	if err != nil {
 		panic(err)
 	}
 }
+
+// Load the dump file if parameter has been passed by the user.
+// If no parameter has been passed, then read from standard input
+func loadInput(dumpFile string) *os.File {
+	if dumpFile != "" {
+		fmt.Printf("\nloading dump file from path: %s\n", dumpFile)
+		file, err := os.Open(dumpFile)
+		if err != nil {
+			fmt.Printf("\nerror reading file: %v err:%v", dumpFile, err)
+			panic(err)
+		}
+		return file
+	}
+	return os.Stdin
+}
diff --git a/mysql/data.go b/mysql/data.go
index c051fa8..2990e3a 100644
--- a/mysql/data.go
+++ b/mysql/data.go
@@ -77,7 +77,7 @@ func ConvertData(conv *internal.Conv, srcTable string, srcCols []string, srcSche
 		if spColDef.T.IsArray {
 			x, err = convArray(spColDef.T, srcColDef.Type.Name, vals[i])
 		} else {
-			x, err = convScalar(spColDef.T, srcColDef.Type.Name, conv.TimezoneOffset, vals[i])
+			x, err = convScalar(conv, spColDef.T, srcColDef.Type.Name, conv.TimezoneOffset, vals[i])
 		}
 		if err != nil {
 			return "", []string{}, []interface{}{}, err
@@ -98,7 +98,7 @@ func ConvertData(conv *internal.Conv, srcTable string, srcCols []string, srcSche
 // appropriate Spanner value. It is the caller's responsibility to
 // detect and handle NULL values: convScalar will return error if a
 // NULL value is passed.
-func convScalar(spannerType ddl.Type, srcTypeName string, TimezoneOffset string, val string) (interface{}, error) {
+func convScalar(conv *internal.Conv, spannerType ddl.Type, srcTypeName string, TimezoneOffset string, val string) (interface{}, error) {
 	// Whitespace within the val string is considered part of the data value.
 	// Note that many of the underlying conversions functions we use (like
 	// strconv.ParseFloat and strconv.ParseInt) return "invalid syntax"
@@ -106,7 +106,7 @@ func convScalar(spannerType ddl.Type, srcTypeName string, TimezoneOffset string,
 	// We do not expect mysqldump to generate such output.
 	switch spannerType.Name {
 	case ddl.Bool:
-		return convBool(val)
+		return convBool(conv, val)
 	case ddl.Bytes:
 		return convBytes(val)
 	case ddl.Date:
@@ -126,9 +126,21 @@ func convScalar(spannerType ddl.Type, srcTypeName string, TimezoneOffset string,
 	}
 }
 
-func convBool(val string) (bool, error) {
+func convBool(conv *internal.Conv, val string) (bool, error) {
 	b, err := strconv.ParseBool(val)
 	if err != nil {
+		// MySQL uses TINYINT(1) to implement BOOL/BOOLEAN, and does not
+		// enforce/validate boolean values i.e. any value that can be stored
+		// in a TINYINT (-128 to 127) can be stored in BOOL/BOOLEAN.
+		// If ParseBool(val) fails, this is very likely the cause.
+		// To handle this, re-parse as INT64 and treat as true if value is non-zero.
+		// Note: if ParseBool(val) fails, then val is probably a non-zero number.
+		i, err2 := convInt64(val)
+		if err2 == nil && i >= -128 && i <= 127 {
+			b = i != 0
+			conv.Unexpected(fmt.Sprintf("Expected boolean value, but found integer value %v; mapping it to %v\n", val, b))
+			return b, err2
+		}
 		return b, fmt.Errorf("can't convert to bool: %w", err)
 	}
 	return b, err
diff --git a/mysql/infoschema.go b/mysql/infoschema.go
index 123b320..241b062 100644
--- a/mysql/infoschema.go
+++ b/mysql/infoschema.go
@@ -247,18 +247,15 @@ func processColumns(conv *internal.Conv, cols *sql.Rows, constraints map[string]
 			conv.Unexpected(fmt.Sprintf("Can't scan: %v", err))
 			continue
 		}
-		unique := false
 		ignored := schema.Ignored{}
 		for _, c := range constraints[colName] {
 			// c can be UNIQUE, PRIMARY KEY, FOREIGN KEY or CHECK
 			// We've already filtered out PRIMARY KEY.
 			switch c {
-			case "UNIQUE":
-				unique = true
 			case "CHECK":
 				ignored.Check = true
-			case "FOREIGN KEY", "PRIMARY KEY":
-				// Nothing to do here -- these are both handled elsewhere.
+			case "FOREIGN KEY", "PRIMARY KEY", "UNIQUE":
+				// Nothing to do here -- these are all handled elsewhere.
 			}
 		}
 		ignored.Default = colDefault.Valid
@@ -269,7 +266,6 @@ func processColumns(conv *internal.Conv, cols *sql.Rows, constraints map[string]
 			Name:    colName,
 			Type:    toType(dataType, columnType, charMaxLen, numericPrecision, numericScale),
 			NotNull: toNotNull(conv, isNullable),
-			Unique:  unique,
 			Ignored: ignored,
 		}
 		colDefs[colName] = c
diff --git a/mysql/mysqldump.go b/mysql/mysqldump.go
index 60cb17f..30ac181 100644
--- a/mysql/mysqldump.go
+++ b/mysql/mysqldump.go
@@ -25,6 +25,8 @@ import (
 	"github.com/cloudspannerecosystem/harbourbridge/schema"
 	"github.com/pingcap/parser"
 	"github.com/pingcap/parser/ast"
+	"github.com/pingcap/parser/opcode"
+	"github.com/pingcap/tidb/types"
 	driver "github.com/pingcap/tidb/types/parser_driver"
 )
 
@@ -207,6 +209,7 @@ func processCreateTable(conv *internal.Conv, stmt *ast.CreateTableStmt) {
 		return
 	}
 	tableName, err := getTableName(stmt.Table)
+	internal.VerbosePrintf("processing create table elem=%s stmt=%v\n", tableName, stmt)
 	if err != nil {
 		logStmtError(conv, stmt, fmt.Errorf("can't get table name: %w", err))
 		return
@@ -269,11 +272,8 @@ func processConstraint(conv *internal.Conv, table string, constraint *ast.Constr
 	case ast.ConstraintIndex:
 		st.Indexes = append(st.Indexes, schema.Index{Name: constraint.Name, Keys: toSchemaKeys(constraint.Keys)})
 	case ast.ConstraintUniq:
-		// Convert unique column constraint in MySQL to a corresponding unique index in Spanner since
-		// Spanner doesn't support unique constraints on columns.
-		// TODO: Avoid Spanner-specific schema transformations in this file -- they should only
-		// appear in toddl.go. This file should focus on generic transformation from source
-		// database schemas into schema.go.
+		// Convert unique column constraint in mysql to a corresponding unique index in schema
+		// Note that schema represents all unique constraints as indexes.
 		st.Indexes = append(st.Indexes, schema.Index{Name: constraint.Name, Unique: true, Keys: toSchemaKeys(constraint.Keys)})
 	default:
 		updateCols(conv, ct, constraint.Keys, st.ColDefs, table)
@@ -326,13 +326,10 @@ func updateCols(conv *internal.Conv, ct ast.ConstraintType, colNames []*ast.Inde
 		colName := column.Column.OrigColName()
 		cd := colDef[colName]
 		switch ct {
-		case ast.ConstraintUniq:
-			cd.Unique = true
 		case ast.ConstraintCheck:
 			cd.Ignored.Check = true
 		case ast.ConstraintPrimaryKey:
 			cd.NotNull = true
-			cd.Unique = true
 		}
 		colDef[colName] = cd
 	}
@@ -373,11 +370,8 @@ func processAlterTable(conv *internal.Conv, stmt *ast.AlterTableStmt) {
 					conv.SrcSchema[tableName] = ctable
 				}
 				if constraint.isUniqueKey {
-					// Convert unique column constraint in mysql to a corresponding unique index in Spanner since
-					// Spanner doesn't support unique constraints on columns.
-					// TODO: Avoid Spanner-specific schema transformations in this file -- they should only
-					// appear in toddl.go. This file should focus on generic transformation from source
-					// database schemas into schema.go.
+					// Convert unique column constraint in mysql to a corresponding unique index in schema
+					// Note that schema represents all unique constraints as indexes.
 					ctable := conv.SrcSchema[tableName]
 					ctable.Indexes = append(ctable.Indexes, schema.Index{Name: "", Unique: true, Keys: []schema.Key{schema.Key{Column: colname, Desc: false}}})
 					conv.SrcSchema[tableName] = ctable
@@ -447,7 +441,6 @@ func updateColsByOption(conv *internal.Conv, tableName string, col *ast.ColumnDe
 		switch op := elem.Tp; op {
 		case ast.ColumnOptionPrimaryKey:
 			column.NotNull = true
-			column.Unique = true
 			// If primary key is defined in a column then `isPk` will be true
 			// and this column will be added in colDef as primary keys.
 			cc.isPk = true
@@ -466,7 +459,6 @@ func updateColsByOption(conv *internal.Conv, tableName string, col *ast.ColumnDe
 				column.Ignored.Default = true
 			}
 		case ast.ColumnOptionUniqKey:
-			column.Unique = true
 			cc.isUniqueKey = true
 		case ast.ColumnOptionCheck:
 			column.Ignored.Check = true
@@ -726,6 +718,19 @@ func getVals(row []ast.ExprNode) ([]string, error) {
 		switch valueNode := item.(type) {
 		case *driver.ValueExpr:
 			values = append(values, fmt.Sprintf("%v", valueNode.GetValue()))
+		case *ast.UnaryOperationExpr:
+			if valueNode.Op != opcode.Minus {
+				return nil, fmt.Errorf("unexpected UnaryOperationExpr node with opcode %v", valueNode.Op)
+			}
+			valExpr, ok := valueNode.V.(*driver.ValueExpr)
+			if !ok {
+				return nil, fmt.Errorf("unexpected UnaryOperationExpr node with value type %T", valueNode.V)
+			}
+			value, err := getNegativeUnaryVals(valExpr)
+			if err != nil {
+				return nil, fmt.Errorf("unexpected UnaryOperationExpr node with value %v", valExpr.GetValue())
+			}
+			values = append(values, value)
 		default:
 			return nil, fmt.Errorf("unexpected value node %T", valueNode)
 		}
@@ -733,6 +738,21 @@ func getVals(row []ast.ExprNode) ([]string, error) {
 	return values, nil
 }
 
+func getNegativeUnaryVals(valExpr *driver.ValueExpr) (string, error) {
+	switch val := valExpr.GetValue().(type) {
+	case int64:
+		return fmt.Sprintf("%v", -1*val), nil
+	case *types.MyDecimal:
+		floatVal, err := val.ToFloat64()
+		if err != nil {
+			return "", fmt.Errorf("unexpected UnaryOperationExpr with value %v", val)
+		}
+		return fmt.Sprintf("%v", -1*floatVal), nil
+	default:
+		return "", fmt.Errorf("unexpected UnaryOperationExpr value with type %T", val)
+	}
+}
+
 func getTableNameInsert(stmt *ast.TableRefsClause) (string, error) {
 	if stmt.TableRefs == nil {
 		return "", fmt.Errorf("can't build table name as tablerefs is empty")
diff --git a/postgres/infoschema.go b/postgres/infoschema.go
index f907697..a70b6ed 100644
--- a/postgres/infoschema.go
+++ b/postgres/infoschema.go
@@ -281,19 +281,16 @@ func processColumns(conv *internal.Conv, cols *sql.Rows, constraints map[string]
 			conv.Unexpected(fmt.Sprintf("Can't scan: %v", err))
 			continue
 		}
-		unique := false
 		ignored := schema.Ignored{}
 		for _, c := range constraints[colName] {
 			// c can be UNIQUE, PRIMARY KEY, FOREIGN KEY,
 			// or CHECK (based on msql, sql server, postgres docs).
 			// We've already filtered out PRIMARY KEY.
 			switch c {
-			case "UNIQUE":
-				unique = true
 			case "CHECK":
 				ignored.Check = true
-			case "FOREIGN KEY", "PRIMARY KEY":
-				// Nothing to do here -- these are both handled elsewhere.
+			case "FOREIGN KEY", "PRIMARY KEY", "UNIQUE":
+				// Nothing to do here -- these are handled elsewhere.
 			}
 		}
 		ignored.Default = colDefault.Valid
@@ -301,7 +298,6 @@ func processColumns(conv *internal.Conv, cols *sql.Rows, constraints map[string]
 			Name:    colName,
 			Type:    toType(dataType, elementDataType, charMaxLen, numericPrecision, numericScale),
 			NotNull: toNotNull(conv, isNullable),
-			Unique:  unique,
 			Ignored: ignored,
 		}
 		colDefs[colName] = c
diff --git a/postgres/toddl.go b/postgres/toddl.go
index 1ed0523..ac09679 100644
--- a/postgres/toddl.go
+++ b/postgres/toddl.go
@@ -64,9 +64,15 @@ func schemaToDDL(conv *internal.Conv) error {
 			}
 			spColNames = append(spColNames, colName)
 			ty, issues := toSpannerType(conv, srcCol.Type.Name, srcCol.Type.Mods)
-			if len(srcCol.Type.ArrayBounds) > 1 {
-				ty = ddl.Type{Name: ddl.String, Len: ddl.MaxLength}
-				issues = append(issues, internal.MultiDimensionalArray)
+
+			if conv.TargetDb == "experimental_postgres" { //TODO : Use constant instead. Using string to prevent import cycle
+				ty = overrideExperimentalType(srcCol, ty)
+			} else {
+				if len(srcCol.Type.ArrayBounds) > 1 {
+					ty = ddl.Type{Name: ddl.String, Len: ddl.MaxLength}
+					issues = append(issues, internal.MultiDimensionalArray)
+				}
+				ty.IsArray = len(srcCol.Type.ArrayBounds) == 1
 			}
 			// TODO: add issues for all elements of srcCol.Ignored.
 			if srcCol.Ignored.ForeignKey {
@@ -78,7 +84,7 @@ func schemaToDDL(conv *internal.Conv) error {
 			if len(issues) > 0 {
 				conv.Issues[srcTable.Name][srcCol.Name] = issues
 			}
-			ty.IsArray = len(srcCol.Type.ArrayBounds) == 1
+
 			spColDef[colName] = ddl.ColumnDef{
 				Name:    colName,
 				T:       ty,
@@ -162,6 +168,18 @@ func toSpannerType(conv *internal.Conv, id string, mods []int64) (ddl.Type, []in
 	return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}, []internal.SchemaIssue{internal.NoGoodType}
 }
 
+// Override the types to map to experimental postgres types.
+func overrideExperimentalType(srcCol schema.Column, originalType ddl.Type) ddl.Type {
+	switch originalType.Name {
+	case ddl.Numeric, ddl.Date:
+		return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}
+	}
+	if len(srcCol.Type.ArrayBounds) > 0 {
+		return ddl.Type{Name: ddl.String, Len: ddl.MaxLength}
+	}
+	return originalType
+}
+
 func quoteIfNeeded(s string) string {
 	for _, r := range s {
 		if unicode.IsLetter(r) || unicode.IsDigit(r) || unicode.IsPunct(r) {
diff --git a/schema/schema.go b/schema/schema.go
index 3c53127..41a22b0 100644
--- a/schema/schema.go
+++ b/schema/schema.go
@@ -47,7 +47,6 @@ type Column struct {
 	Name    string
 	Type    Type
 	NotNull bool
-	Unique  bool
 	Ignored Ignored
 }
 
@@ -73,6 +72,17 @@ type Key struct {
 }
 
 // Index represents a database index.
+// Index represents a database index.
+// The only way we represent unique constraints is via indexes. All source database
+// unique constraints will be transformed into this representation, including:
+// i) A column level constraint (as part of a CREATE TABLE statement)
+// ii) A table level constraint (as part of a CREATE TABLE statement)
+// iii) An index (as part of a CREATE TABLE statement)
+// iv) Added via an ALTER TABLE constraint (changing column constraints, table constraints or index definitions)
+// v) Added via a CREATE UNIQUE INDEX statement (which internally maps to an alter table statement).
+// We use this single representation of unique constraints to simplify their processing and avoid having
+// to handle lots of cases for the same concept. Our choice of an index representation for unique is largely
+// motivated by the fact that databases typically implement UNIQUE via an index.
 type Index struct {
 	Name   string
 	Unique bool
diff --git a/web/session.go b/web/session.go
index 9958c84..6f66894 100644
--- a/web/session.go
+++ b/web/session.go
@@ -37,6 +37,7 @@ type session struct {
 }
 
 func createSession(w http.ResponseWriter, r *http.Request) {
+	ioHelper := &conversion.IOStreams{In: os.Stdin, Out: os.Stdout}
 	now := time.Now()
 	dbName := sessionState.dbName
 	var err error
@@ -46,12 +47,13 @@ func createSession(w http.ResponseWriter, r *http.Request) {
 			http.Error(w, fmt.Sprintf("Can not create database name : %v", err), http.StatusInternalServerError)
 		}
 	}
-	sessionFile := ".session.json"
-	filePrefix := "frontend/"
-	out := os.Stdout
-	filePath := filePrefix + dbName + sessionFile
-	conversion.WriteSessionFile(sessionState.conv, filePath, out)
+	dirPath, err := conversion.WriteConvGeneratedFiles(sessionState.conv, dbName, sessionState.driver, ioHelper.BytesRead, ioHelper.Out)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Cannot write files : %v", err), http.StatusInternalServerError)
+	}
+	filePath := dirPath + dbName + ".session.json"
 	session := session{Driver: sessionState.driver, FilePath: filePath, DBName: dbName, CreatedAt: now.Format(time.RFC1123)}
+	sessionState.dbName = dbName
 	sessionState.sessionFile = filePath
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(session)
diff --git a/web/web.go b/web/web.go
index f4f81da..1ad7ad6 100644
--- a/web/web.go
+++ b/web/web.go
@@ -166,7 +166,7 @@ func convertSchemaDump(w http.ResponseWriter, r *http.Request) {
 		http.Error(w, fmt.Sprintf("failed to open dump file %v : %v", dc.FilePath, err), http.StatusNotFound)
 		return
 	}
-	conv, err := conversion.SchemaConv(dc.Driver, &conversion.IOStreams{In: f, Out: os.Stdout}, 0)
+	conv, err := conversion.SchemaConv(dc.Driver, conversion.TARGET_SPANNER, &conversion.IOStreams{In: f, Out: os.Stdout}, 0)
 	if err != nil {
 		http.Error(w, fmt.Sprintf("Schema Conversion Error : %v", err), http.StatusNotFound)
 		return
@@ -770,26 +770,12 @@ func dropSecondaryIndex(w http.ResponseWriter, r *http.Request) {
 }
 
 // updateSessionFile updates the content of session file with
-// latest sessionState.conv.
+// latest sessionState.conv while also dumping schemas and report.
 func updateSessionFile() error {
-	filePath := sessionState.sessionFile
-	if filePath == "" {
-		return fmt.Errorf("Session file path is empty")
-	}
-	f, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644)
-	if err != nil {
-		return err
-	}
-	convJSON, err := json.MarshalIndent(sessionState.conv, "", " ")
-	if err != nil {
-		return err
-	}
-	err = f.Truncate(0)
+	ioHelper := &conversion.IOStreams{In: os.Stdin, Out: os.Stdout}
+	_, err := conversion.WriteConvGeneratedFiles(sessionState.conv, sessionState.dbName, sessionState.driver, ioHelper.BytesRead, ioHelper.Out)
 	if err != nil {
-		return err
-	}
-	if _, err := f.Write(convJSON); err != nil {
-		return err
+		return fmt.Errorf("encountered error %w. Cannot write files", err)
 	}
 	return nil
 }


Test patch:
diff --git a/.github/workflows/integration-tests-against-emulator.yaml b/.github/workflows/integration-tests-against-emulator.yaml
index 60e63df..4aefbef 100644
--- a/.github/workflows/integration-tests-against-emulator.yaml
+++ b/.github/workflows/integration-tests-against-emulator.yaml
@@ -23,8 +23,14 @@ jobs:
       MYSQLDATABASE: test
       MYSQLPWD: root
 
+      # set DynamoDB related environment variables
+      AWS_ACCESS_KEY_ID: dummyId
+      AWS_SECRET_ACCESS_KEY: dummyKey
+      AWS_REGION: dummyRegion
+      DYNAMODB_ENDPOINT_OVERRIDE: http://localhost:8000
+
     services:
-      emulator:
+      spanner_emulator:
         image: gcr.io/cloud-spanner-emulator/emulator:1.2.0
         ports:
           - 9010:9010
@@ -48,6 +54,11 @@ jobs:
         - 3306:3306
         # needed because the mysql container does not provide a healthcheck
         options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
+      dynamodb_emulator:
+        image: amazon/dynamodb-local
+        ports:
+        - 8000:8000
+        options: --workdir /home/dynamodblocal --health-cmd "curl --fail http://127.0.0.1:8000/shell/ || exit 1" --health-interval 10s --health-timeout 5s --health-retries 5
 
     steps:
       - uses: actions/checkout@v2
@@ -71,7 +82,7 @@ jobs:
       - run: mysql -v -P 3306 --protocol=tcp -u root -proot test < test_data/mysqldump.test.out
 
       # create a spanner instance
-      - uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
+      - uses: google-github-actions/setup-gcloud@master
         with:
           version: '290.0.1'
       - run: gcloud info
diff --git a/dynamodb/schema_test.go b/dynamodb/schema_test.go
index 8e7f513..8400392 100644
--- a/dynamodb/schema_test.go
+++ b/dynamodb/schema_test.go
@@ -396,9 +396,11 @@ func TestParseIndexes(t *testing.T) {
 	attrNameA := "a"
 	attrNameB := "b"
 	attrNameC := "c"
+	attrNameD := "d"
 	hashKeyType := "HASH"
 	sortKeyType := "RANGE"
-	indexName := "secondary_index_c"
+	globalIndexName := "secondary_index_c"
+	localIndexName := "secondary_index_d"
 	describeTableOutputs := []dynamodb.DescribeTableOutput{
 		{
 			Table: &dynamodb.TableDescription{
@@ -409,12 +411,20 @@ func TestParseIndexes(t *testing.T) {
 				},
 				GlobalSecondaryIndexes: []*dynamodb.GlobalSecondaryIndexDescription{
 					{
-						IndexName: &indexName,
+						IndexName: &globalIndexName,
 						KeySchema: []*dynamodb.KeySchemaElement{
 							{AttributeName: &attrNameC, KeyType: &hashKeyType},
 						},
 					},
 				},
+				LocalSecondaryIndexes: []*dynamodb.LocalSecondaryIndexDescription{
+					{
+						IndexName: &localIndexName,
+						KeySchema: []*dynamodb.KeySchemaElement{
+							{AttributeName: &attrNameD, KeyType: &hashKeyType},
+						},
+					},
+				},
 			},
 		},
 	}
@@ -430,7 +440,10 @@ func TestParseIndexes(t *testing.T) {
 
 	pKeys := []schema.Key{{Column: "a"}, {Column: "b"}}
 	assert.Equal(t, pKeys, dySchema.PrimaryKeys)
-	secIndexes := []schema.Index{{Name: "secondary_index_c", Keys: []schema.Key{{Column: "c"}}}}
+	secIndexes := []schema.Index{
+		{Name: "secondary_index_c", Keys: []schema.Key{{Column: "c"}}},
+		{Name: "secondary_index_d", Keys: []schema.Key{{Column: "d"}}},
+	}
 	assert.Equal(t, secIndexes, dySchema.Indexes)
 }
 
diff --git a/dynamodb/toddl_test.go b/dynamodb/toddl_test.go
index 2c99646..c3150d1 100644
--- a/dynamodb/toddl_test.go
+++ b/dynamodb/toddl_test.go
@@ -43,7 +43,12 @@ func TestToSpannerType(t *testing.T) {
 			"j": {Name: "j", Type: schema.Type{Name: typeNumberSet}},
 			"k": {Name: "k", Type: schema.Type{Name: typeNumberStringSet}},
 		},
-		PrimaryKeys: []schema.Key{{Column: "a"}, {Column: "b"}}}
+		PrimaryKeys: []schema.Key{{Column: "a"}, {Column: "b"}},
+		Indexes: []schema.Index{
+			{Name: "index1", Keys: []schema.Key{{Column: "b"}, {Column: "c"}}},
+			{Name: "test", Keys: []schema.Key{{Column: "d"}}},
+		},
+	}
 	conv.SrcSchema[name] = srcSchema
 	assert.Nil(t, schemaToDDL(conv))
 	actual := conv.SpSchema[name]
@@ -65,6 +70,10 @@ func TestToSpannerType(t *testing.T) {
 			"k": {Name: "k", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength, IsArray: true}},
 		},
 		Pks: []ddl.IndexKey{{Col: "a"}, {Col: "b"}},
+		Indexes: []ddl.CreateIndex{
+			{Name: "index1", Table: name, Keys: []ddl.IndexKey{{Col: "b"}, {Col: "c"}}},
+			{Name: "test_2", Table: name, Keys: []ddl.IndexKey{{Col: "d"}}},
+		},
 	}
 	assert.Equal(t, expected, actual)
 }
diff --git a/frontend/puppeteerTest.js b/frontend/puppeteerTest.js
index 866dcfe..49f2dda 100644
--- a/frontend/puppeteerTest.js
+++ b/frontend/puppeteerTest.js
@@ -92,6 +92,11 @@ puppeteer.launch(config.launchOptions).then(async(browser) => {
     */
     await resumeSession(page);
 
+    /*
+    14. Rename a column then revert back to original name
+    */
+    await renameAndRevertColumnName(page);
+    
     // Close Browser
     await browser.close();
 });
@@ -560,6 +565,65 @@ const resumeSession = async(page) => {
     await passed(13);
 };
 
+const renameAndRevertColumnName = async(page) => {
+    // expand customer table
+    await page.waitForSelector(homePage.customerTable);
+    await page.click(homePage.customerTable);
+
+    await page.waitForTimeout(200);
+
+    // click edit schema button
+    await page.waitForSelector(homePage.customerEditSchemaButton);
+    await page.click(homePage.customerEditSchemaButton);
+
+    await page.waitForTimeout(200);
+
+    // Change the value of the 6th column (566 means table 5 column 6, 0-indexed)
+    // from "active" to "changed"
+    await page.waitForSelector("input#column-name-text-566");
+    await page.evaluate(
+        () => (document.getElementById("column-name-text-566").value = "changed")
+    );
+
+    await page.waitForTimeout(200);
+
+    // click save schema
+    await page.waitForSelector(homePage.customerEditSchemaButton);
+    await page.click(homePage.customerEditSchemaButton);
+
+    await page.waitForTimeout(200);
+
+    // again click edit schema
+    await page.waitForSelector(homePage.customerEditSchemaButton);
+    await page.click(homePage.customerEditSchemaButton);
+
+    await page.waitForTimeout(200);
+
+    // change column name back to "active"
+    await page.waitForSelector("input#column-name-text-566");
+    await page.evaluate(
+        () => (document.getElementById("column-name-text-566").value = "active")
+    );
+
+    await page.waitForTimeout(200);
+
+    // click save
+    await page.waitForSelector(homePage.customerEditSchemaButton);
+    await page.click(homePage.customerEditSchemaButton);
+
+    try {
+        // wait for error message for 400 ms
+        await page.waitForSelector(homePage.errorModalButton, {timeout: 400});
+        // it reached here hence an error message popped up
+        await page.click(homePage.errorModalButton);
+        await console.log("Test Failed: ", 14)
+    } catch (e) {
+        // timeout occured hence there was no error message
+        await passed(14);
+    }
+
+};
+
 let arr = [];
 for (let i = 1; i <= 14; i++) {
     arr.push("select#data-type-option" + i);
diff --git a/mysql/data_test.go b/mysql/data_test.go
index 10f52e4..ea8e7ec 100644
--- a/mysql/data_test.go
+++ b/mysql/data_test.go
@@ -74,7 +74,13 @@ func TestConvertData(t *testing.T) {
 		in    string      // Input value for conversion.
 		e     interface{} // Expected result.
 	}{
-		{"bool", ddl.Type{Name: ddl.Bool}, "", "1", true},
+		{"bool 0", ddl.Type{Name: ddl.Bool}, "", "0", false},
+		{"bool 1", ddl.Type{Name: ddl.Bool}, "", "1", true},
+		{"bool true", ddl.Type{Name: ddl.Bool}, "", "true", true},
+		{"bool false", ddl.Type{Name: ddl.Bool}, "", "false", false},
+		{"bool 5", ddl.Type{Name: ddl.Bool}, "", "5", true},
+		{"bool -128", ddl.Type{Name: ddl.Bool}, "", "-128", true},
+		{"bool 127", ddl.Type{Name: ddl.Bool}, "", "127", true},
 		{"bytes", ddl.Type{Name: ddl.Bytes, Len: ddl.MaxLength}, "", string([]byte{137, 80}), []byte{0x89, 0x50}}, // need some other approach to testblob type
 		{"date", ddl.Type{Name: ddl.Date}, "", "2019-10-29", getDate("2019-10-29")},
 		{"float64", ddl.Type{Name: ddl.Float64}, "", "42.6", float64(42.6)},
@@ -227,6 +233,11 @@ func TestConvertError(t *testing.T) {
 			cols: []string{"a", "b", "c"},
 			vals: []string{"6", "6.6", "truee"},
 		},
+		{
+			name: "Error in bool 128",
+			cols: []string{"a", "b", "c"},
+			vals: []string{"6", "6.6", "128"},
+		},
 	}
 	tableName := "testtable"
 	spTable := ddl.CreateTable{
diff --git a/mysql/mysqldump_test.go b/mysql/mysqldump_test.go
index 1215b89..f8a365f 100644
--- a/mysql/mysqldump_test.go
+++ b/mysql/mysqldump_test.go
@@ -738,6 +738,15 @@ CREATE TABLE test (a text PRIMARY KEY, b text);`,
 			expectedData: []spannerData{
 				spannerData{table: "test", cols: []string{"id", "a", "b", "c", "d"}, vals: []interface{}{int64(1), int64(88), int64(44), int64(22), float64(444.9876)}}},
 		},
+		{
+			name: "Data conversion: negative values for smallint, mediumint, bigint, double",
+			input: `
+	CREATE TABLE test (id integer PRIMARY KEY, a smallint, b mediumint, c bigint, d double);
+	INSERT INTO test (id, a, b, c, d) VALUES (-1, -88, -44, -22, -444.9876);
+	`,
+			expectedData: []spannerData{
+				spannerData{table: "test", cols: []string{"id", "a", "b", "c", "d"}, vals: []interface{}{int64(-1), int64(-88), int64(-44), int64(-22), float64(-444.9876)}}},
+		},
 		// test with different timezone
 		{
 			name: "Data conversion:  text, timestamp, datetime, varchar",
diff --git a/postgres/toddl_test.go b/postgres/toddl_test.go
index 383e6c0..4871b40 100644
--- a/postgres/toddl_test.go
+++ b/postgres/toddl_test.go
@@ -95,6 +95,79 @@ func TestToSpannerType(t *testing.T) {
 	assert.Equal(t, expectedIssues, conv.Issues[name])
 }
 
+// This is just a very basic smoke-test for toExperimentalSpannerType.
+// The real testing of toSpannerType happens in process_test.go
+// via the public API ProcessPgDump (see TestProcessPgDump).
+func TestToExperimentalSpannerType(t *testing.T) {
+	conv := internal.MakeConv()
+	conv.SetSchemaMode()
+	conv.TargetDb = "experimental_postgres"
+	name := "test"
+	srcSchema := schema.Table{
+		Name:     name,
+		ColNames: []string{"a", "b", "c", "d", "e", "f"},
+		ColDefs: map[string]schema.Column{
+			"a": schema.Column{Name: "a", Type: schema.Type{Name: "int8"}},
+			"b": schema.Column{Name: "b", Type: schema.Type{Name: "float4"}},
+			"c": schema.Column{Name: "c", Type: schema.Type{Name: "bool"}},
+			"d": schema.Column{Name: "d", Type: schema.Type{Name: "varchar", Mods: []int64{6}}},
+			"e": schema.Column{Name: "e", Type: schema.Type{Name: "numeric"}},
+			"f": schema.Column{Name: "f", Type: schema.Type{Name: "date"}},
+		},
+		PrimaryKeys: []schema.Key{schema.Key{Column: "a"}},
+		ForeignKeys: []schema.ForeignKey{schema.ForeignKey{Name: "fk_test", Columns: []string{"d"}, ReferTable: "ref_table", ReferColumns: []string{"dref"}},
+			schema.ForeignKey{Name: "fk_test2", Columns: []string{"a"}, ReferTable: "ref_table2", ReferColumns: []string{"aRef"}}},
+		Indexes: []schema.Index{schema.Index{Name: "index1", Unique: true, Keys: []schema.Key{schema.Key{Column: "a", Desc: false}, schema.Key{Column: "d", Desc: true}}},
+			schema.Index{Name: "index2", Unique: false, Keys: []schema.Key{schema.Key{Column: "d", Desc: true}}}},
+	}
+	conv.SrcSchema[name] = srcSchema
+	conv.SpSchema["ref_table"] = ddl.CreateTable{
+		Name:     "ref_table",
+		ColNames: []string{"dref", "b", "c"},
+		ColDefs: map[string]ddl.ColumnDef{
+			"dref": ddl.ColumnDef{Name: "dref", T: ddl.Type{Name: ddl.String, Len: int64(6)}},
+			"b":    ddl.ColumnDef{Name: "b", T: ddl.Type{Name: ddl.Float64}},
+			"c":    ddl.ColumnDef{Name: "c", T: ddl.Type{Name: ddl.Bool}},
+		},
+		Pks: []ddl.IndexKey{ddl.IndexKey{Col: "dref"}},
+	}
+	conv.SpSchema["ref_table2"] = ddl.CreateTable{
+		Name:     "ref_table2",
+		ColNames: []string{"aref", "b", "c"},
+		ColDefs: map[string]ddl.ColumnDef{
+			"aref": ddl.ColumnDef{Name: "aref", T: ddl.Type{Name: ddl.Int64}},
+			"b":    ddl.ColumnDef{Name: "b", T: ddl.Type{Name: ddl.Float64}},
+			"c":    ddl.ColumnDef{Name: "c", T: ddl.Type{Name: ddl.Bool}},
+		},
+		Pks: []ddl.IndexKey{ddl.IndexKey{Col: "aref"}},
+	}
+	assert.Nil(t, schemaToDDL(conv))
+	actual := conv.SpSchema[name]
+	dropComments(&actual) // Don't test comment.
+	expected := ddl.CreateTable{
+		Name:     name,
+		ColNames: []string{"a", "b", "c", "d", "e", "f"},
+		ColDefs: map[string]ddl.ColumnDef{
+			"a": ddl.ColumnDef{Name: "a", T: ddl.Type{Name: ddl.Int64}},
+			"b": ddl.ColumnDef{Name: "b", T: ddl.Type{Name: ddl.Float64}},
+			"c": ddl.ColumnDef{Name: "c", T: ddl.Type{Name: ddl.Bool}},
+			"d": ddl.ColumnDef{Name: "d", T: ddl.Type{Name: ddl.String, Len: int64(6)}},
+			"e": ddl.ColumnDef{Name: "e", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}},
+			"f": ddl.ColumnDef{Name: "f", T: ddl.Type{Name: ddl.String, Len: ddl.MaxLength}},
+		},
+		Pks: []ddl.IndexKey{ddl.IndexKey{Col: "a"}},
+		Fks: []ddl.Foreignkey{ddl.Foreignkey{Name: "fk_test", Columns: []string{"d"}, ReferTable: "ref_table", ReferColumns: []string{"dref"}},
+			ddl.Foreignkey{Name: "fk_test2", Columns: []string{"a"}, ReferTable: "ref_table2", ReferColumns: []string{"aref"}}},
+		Indexes: []ddl.CreateIndex{ddl.CreateIndex{Name: "index1", Table: name, Unique: true, Keys: []ddl.IndexKey{ddl.IndexKey{Col: "a", Desc: false}, ddl.IndexKey{Col: "d", Desc: true}}},
+			ddl.CreateIndex{Name: "index2", Table: name, Unique: false, Keys: []ddl.IndexKey{ddl.IndexKey{Col: "d", Desc: true}}}},
+	}
+	assert.Equal(t, expected, actual)
+	expectedIssues := map[string][]internal.SchemaIssue{
+		"b": []internal.SchemaIssue{internal.Widened},
+	}
+	assert.Equal(t, expectedIssues, conv.Issues[name])
+}
+
 func dropComments(t *ddl.CreateTable) {
 	t.Comment = ""
 	for _, c := range t.ColNames {
diff --git a/test_data/frontend/sakila-schema.sql b/test_data/frontend/sakila-schema.sql
new file mode 100644
index 0000000..99aeb58
--- /dev/null
+++ b/test_data/frontend/sakila-schema.sql
@@ -0,0 +1,695 @@
+-- Sakila Sample Database Schema
+-- Version 1.2
+
+-- Copyright (c) 2006, 2019, Oracle and/or its affiliates.
+
+-- Redistribution and use in source and binary forms, with or without
+-- modification, are permitted provided that the following conditions are
+-- met:
+
+-- * Redistributions of source code must retain the above copyright notice,
+--   this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+--   notice, this list of conditions and the following disclaimer in the
+--   documentation and/or other materials provided with the distribution.
+-- * Neither the name of Oracle nor the names of its contributors may be used
+--   to endorse or promote products derived from this software without
+--   specific prior written permission.
+
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+-- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+SET NAMES utf8mb4;
+SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
+SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
+SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';
+
+DROP SCHEMA IF EXISTS sakila;
+CREATE SCHEMA sakila;
+USE sakila;
+
+
+--
+-- Table structure for table `BadTableNoPK`
+--
+
+CREATE TABLE `BadTableNoPK` (
+  `id` varchar(100) DEFAULT NULL,
+  `x` bigint(20) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+--
+-- Table structure for table `actor`
+--
+
+CREATE TABLE actor (
+  actor_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
+  first_name VARCHAR(45) NOT NULL,
+  last_name VARCHAR(45) NOT NULL,
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY  (actor_id),
+  KEY idx_actor_last_name (last_name)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `address`
+--
+
+CREATE TABLE address (
+  address_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
+  address VARCHAR(50) NOT NULL,
+  address2 VARCHAR(50) DEFAULT NULL,
+  district VARCHAR(20) NOT NULL,
+  city_id SMALLINT UNSIGNED NOT NULL,
+  postal_code VARCHAR(10) DEFAULT NULL,
+  phone VARCHAR(20) NOT NULL,
+  -- Add GEOMETRY column for MySQL 5.7.5 and higher
+  -- Also include SRID attribute for MySQL 8.0.3 and higher
+  /*!50705 location GEOMETRY */ /*!80003 SRID 0 */ /*!50705 NOT NULL,*/
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY  (address_id),
+  KEY idx_fk_city_id (city_id),
+  /*!50705 SPATIAL KEY `idx_location` (location),*/
+  CONSTRAINT `fk_address_city` FOREIGN KEY (city_id) REFERENCES city (city_id) ON DELETE RESTRICT ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `category`
+--
+
+CREATE TABLE category (
+  category_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
+  name VARCHAR(25) NOT NULL,
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY  (category_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `city`
+--
+
+CREATE TABLE city (
+  city_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
+  city VARCHAR(50) NOT NULL,
+  country_id SMALLINT UNSIGNED NOT NULL,
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY  (city_id),
+  KEY idx_fk_country_id (country_id),
+  CONSTRAINT `fk_city_country` FOREIGN KEY (country_id) REFERENCES country (country_id) ON DELETE RESTRICT ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `country`
+--
+
+CREATE TABLE country (
+  country_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
+  country VARCHAR(50) NOT NULL,
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY  (country_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `customer`
+--
+
+CREATE TABLE customer (
+  customer_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
+  store_id TINYINT UNSIGNED NOT NULL,
+  first_name VARCHAR(45) NOT NULL,
+  last_name VARCHAR(45) NOT NULL,
+  email VARCHAR(50) DEFAULT NULL,
+  address_id SMALLINT UNSIGNED NOT NULL,
+  active BOOLEAN NOT NULL DEFAULT TRUE,
+  create_date DATETIME NOT NULL,
+  last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY  (customer_id),
+  KEY idx_fk_store_id (store_id),
+  KEY idx_fk_address_id (address_id),
+  KEY idx_last_name (last_name),
+  CONSTRAINT fk_customer_address FOREIGN KEY (address_id) REFERENCES address (address_id) ON DELETE RESTRICT ON UPDATE CASCADE,
+  CONSTRAINT fk_customer_store FOREIGN KEY (store_id) REFERENCES store (store_id) ON DELETE RESTRICT ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `film`
+--
+
+CREATE TABLE film (
+  film_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
+  title VARCHAR(128) NOT NULL,
+  description TEXT DEFAULT NULL,
+  release_year YEAR DEFAULT NULL,
+  language_id TINYINT UNSIGNED NOT NULL,
+  original_language_id TINYINT UNSIGNED DEFAULT NULL,
+  rental_duration TINYINT UNSIGNED NOT NULL DEFAULT 3,
+  rental_rate DECIMAL(4,2) NOT NULL DEFAULT 4.99,
+  length SMALLINT UNSIGNED DEFAULT NULL,
+  replacement_cost DECIMAL(5,2) NOT NULL DEFAULT 19.99,
+  rating ENUM('G','PG','PG-13','R','NC-17') DEFAULT 'G',
+  special_features SET('Trailers','Commentaries','Deleted Scenes','Behind the Scenes') DEFAULT NULL,
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY  (film_id),
+  KEY idx_title (title),
+  KEY idx_fk_language_id (language_id),
+  KEY idx_fk_original_language_id (original_language_id),
+  CONSTRAINT fk_film_language FOREIGN KEY (language_id) REFERENCES language (language_id) ON DELETE RESTRICT ON UPDATE CASCADE,
+  CONSTRAINT fk_film_language_original FOREIGN KEY (original_language_id) REFERENCES language (language_id) ON DELETE RESTRICT ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `film_actor`
+--
+
+CREATE TABLE film_actor (
+  actor_id SMALLINT UNSIGNED NOT NULL,
+  film_id SMALLINT UNSIGNED NOT NULL,
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY  (actor_id,film_id),
+  KEY idx_fk_film_id (`film_id`),
+  CONSTRAINT fk_film_actor_actor FOREIGN KEY (actor_id) REFERENCES actor (actor_id) ON DELETE RESTRICT ON UPDATE CASCADE,
+  CONSTRAINT fk_film_actor_film FOREIGN KEY (film_id) REFERENCES film (film_id) ON DELETE RESTRICT ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `film_category`
+--
+
+CREATE TABLE film_category (
+  film_id SMALLINT UNSIGNED NOT NULL,
+  category_id TINYINT UNSIGNED NOT NULL,
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (film_id, category_id),
+  CONSTRAINT fk_film_category_film FOREIGN KEY (film_id) REFERENCES film (film_id) ON DELETE RESTRICT ON UPDATE CASCADE,
+  CONSTRAINT fk_film_category_category FOREIGN KEY (category_id) REFERENCES category (category_id) ON DELETE RESTRICT ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `film_text`
+-- 
+-- InnoDB added FULLTEXT support in 5.6.10. If you use an
+-- earlier version, then consider upgrading (recommended) or 
+-- changing InnoDB to MyISAM as the film_text engine
+--
+
+-- Use InnoDB for film_text as of 5.6.10, MyISAM prior to 5.6.10.
+SET @old_default_storage_engine = @@default_storage_engine;
+SET @@default_storage_engine = 'MyISAM';
+/*!50610 SET @@default_storage_engine = 'InnoDB'*/;
+
+CREATE TABLE film_text (
+  film_id SMALLINT NOT NULL,
+  title VARCHAR(255) NOT NULL,
+  description TEXT,
+  PRIMARY KEY  (film_id),
+  FULLTEXT KEY idx_title_description (title,description)
+) DEFAULT CHARSET=utf8mb4;
+
+SET @@default_storage_engine = @old_default_storage_engine;
+
+--
+-- Triggers for loading film_text from film
+--
+
+DELIMITER ;;
+CREATE TRIGGER `ins_film` AFTER INSERT ON `film` FOR EACH ROW BEGIN
+    INSERT INTO film_text (film_id, title, description)
+        VALUES (new.film_id, new.title, new.description);
+  END;;
+
+
+CREATE TRIGGER `upd_film` AFTER UPDATE ON `film` FOR EACH ROW BEGIN
+    IF (old.title != new.title) OR (old.description != new.description) OR (old.film_id != new.film_id)
+    THEN
+        UPDATE film_text
+            SET title=new.title,
+                description=new.description,
+                film_id=new.film_id
+        WHERE film_id=old.film_id;
+    END IF;
+  END;;
+
+
+CREATE TRIGGER `del_film` AFTER DELETE ON `film` FOR EACH ROW BEGIN
+    DELETE FROM film_text WHERE film_id = old.film_id;
+  END;;
+
+DELIMITER ;
+
+--
+-- Table structure for table `inventory`
+--
+
+CREATE TABLE inventory (
+  inventory_id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
+  film_id SMALLINT UNSIGNED NOT NULL,
+  store_id TINYINT UNSIGNED NOT NULL,
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY  (inventory_id),
+  KEY idx_fk_film_id (film_id),
+  KEY idx_store_id_film_id (store_id,film_id),
+  CONSTRAINT fk_inventory_store FOREIGN KEY (store_id) REFERENCES store (store_id) ON DELETE RESTRICT ON UPDATE CASCADE,
+  CONSTRAINT fk_inventory_film FOREIGN KEY (film_id) REFERENCES film (film_id) ON DELETE RESTRICT ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `language`
+--
+
+CREATE TABLE language (
+  language_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
+  name CHAR(20) NOT NULL,
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (language_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `payment`
+--
+
+CREATE TABLE payment (
+  payment_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
+  customer_id SMALLINT UNSIGNED NOT NULL,
+  staff_id TINYINT UNSIGNED NOT NULL,
+  rental_id INT DEFAULT NULL,
+  amount DECIMAL(5,2) NOT NULL,
+  payment_date DATETIME NOT NULL,
+  last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY  (payment_id),
+  KEY idx_fk_staff_id (staff_id),
+  KEY idx_fk_customer_id (customer_id),
+  CONSTRAINT fk_payment_rental FOREIGN KEY (rental_id) REFERENCES rental (rental_id) ON DELETE SET NULL ON UPDATE CASCADE,
+  CONSTRAINT fk_payment_customer FOREIGN KEY (customer_id) REFERENCES customer (customer_id) ON DELETE RESTRICT ON UPDATE CASCADE,
+  CONSTRAINT fk_payment_staff FOREIGN KEY (staff_id) REFERENCES staff (staff_id) ON DELETE RESTRICT ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+--
+-- Table structure for table `rental`
+--
+
+CREATE TABLE rental (
+  rental_id INT NOT NULL AUTO_INCREMENT,
+  rental_date DATETIME NOT NULL,
+  inventory_id MEDIUMINT UNSIGNED NOT NULL,
+  customer_id SMALLINT UNSIGNED NOT NULL,
+  return_date DATETIME DEFAULT NULL,
+  staff_id TINYINT UNSIGNED NOT NULL,
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (rental_id),
+  UNIQUE KEY  (rental_date,inventory_id,customer_id),
+  KEY idx_fk_inventory_id (inventory_id),
+  KEY idx_fk_customer_id (customer_id),
+  KEY idx_fk_staff_id (staff_id),
+  CONSTRAINT fk_rental_staff FOREIGN KEY (staff_id) REFERENCES staff (staff_id) ON DELETE RESTRICT ON UPDATE CASCADE,
+  CONSTRAINT fk_rental_inventory FOREIGN KEY (inventory_id) REFERENCES inventory (inventory_id) ON DELETE RESTRICT ON UPDATE CASCADE,
+  CONSTRAINT fk_rental_customer FOREIGN KEY (customer_id) REFERENCES customer (customer_id) ON DELETE RESTRICT ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `staff`
+--
+
+CREATE TABLE staff (
+  staff_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
+  first_name VARCHAR(45) NOT NULL,
+  last_name VARCHAR(45) NOT NULL,
+  address_id SMALLINT UNSIGNED NOT NULL,
+  picture BLOB DEFAULT NULL,
+  email VARCHAR(50) DEFAULT NULL,
+  store_id TINYINT UNSIGNED NOT NULL,
+  active BOOLEAN NOT NULL DEFAULT TRUE,
+  username VARCHAR(16) NOT NULL,
+  password VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY  (staff_id),
+  KEY idx_fk_store_id (store_id),
+  KEY idx_fk_address_id (address_id),
+  CONSTRAINT fk_staff_store FOREIGN KEY (store_id) REFERENCES store (store_id) ON DELETE RESTRICT ON UPDATE CASCADE,
+  CONSTRAINT fk_staff_address FOREIGN KEY (address_id) REFERENCES address (address_id) ON DELETE RESTRICT ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- Table structure for table `store`
+--
+
+CREATE TABLE store (
+  store_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
+  manager_staff_id TINYINT UNSIGNED NOT NULL,
+  address_id SMALLINT UNSIGNED NOT NULL,
+  last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY  (store_id),
+  UNIQUE KEY idx_unique_manager (manager_staff_id),
+  KEY idx_fk_address_id (address_id),
+  CONSTRAINT fk_store_staff FOREIGN KEY (manager_staff_id) REFERENCES staff (staff_id) ON DELETE RESTRICT ON UPDATE CASCADE,
+  CONSTRAINT fk_store_address FOREIGN KEY (address_id) REFERENCES address (address_id) ON DELETE RESTRICT ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+--
+-- View structure for view `customer_list`
+--
+
+CREATE VIEW customer_list
+AS
+SELECT cu.customer_id AS ID, CONCAT(cu.first_name, _utf8mb4' ', cu.last_name) AS name, a.address AS address, a.postal_code AS `zip code`,
+	a.phone AS phone, city.city AS city, country.country AS country, IF(cu.active, _utf8mb4'active',_utf8mb4'') AS notes, cu.store_id AS SID
+FROM customer AS cu JOIN address AS a ON cu.address_id = a.address_id JOIN city ON a.city_id = city.city_id
+	JOIN country ON city.country_id = country.country_id;
+
+--
+-- View structure for view `film_list`
+--
+
+CREATE VIEW film_list
+AS
+SELECT film.film_id AS FID, film.title AS title, film.description AS description, category.name AS category, film.rental_rate AS price,
+	film.length AS length, film.rating AS rating, GROUP_CONCAT(CONCAT(actor.first_name, _utf8mb4' ', actor.last_name) SEPARATOR ', ') AS actors
+FROM category LEFT JOIN film_category ON category.category_id = film_category.category_id LEFT JOIN film ON film_category.film_id = film.film_id
+        JOIN film_actor ON film.film_id = film_actor.film_id
+	JOIN actor ON film_actor.actor_id = actor.actor_id
+GROUP BY film.film_id, category.name;
+
+--
+-- View structure for view `nicer_but_slower_film_list`
+--
+
+CREATE VIEW nicer_but_slower_film_list
+AS
+SELECT film.film_id AS FID, film.title AS title, film.description AS description, category.name AS category, film.rental_rate AS price,
+	film.length AS length, film.rating AS rating, GROUP_CONCAT(CONCAT(CONCAT(UCASE(SUBSTR(actor.first_name,1,1)),
+	LCASE(SUBSTR(actor.first_name,2,LENGTH(actor.first_name))),_utf8mb4' ',CONCAT(UCASE(SUBSTR(actor.last_name,1,1)),
+	LCASE(SUBSTR(actor.last_name,2,LENGTH(actor.last_name)))))) SEPARATOR ', ') AS actors
+FROM category LEFT JOIN film_category ON category.category_id = film_category.category_id LEFT JOIN film ON film_category.film_id = film.film_id
+        JOIN film_actor ON film.film_id = film_actor.film_id
+	JOIN actor ON film_actor.actor_id = actor.actor_id
+GROUP BY film.film_id, category.name;
+
+--
+-- View structure for view `staff_list`
+--
+
+CREATE VIEW staff_list
+AS
+SELECT s.staff_id AS ID, CONCAT(s.first_name, _utf8mb4' ', s.last_name) AS name, a.address AS address, a.postal_code AS `zip code`, a.phone AS phone,
+	city.city AS city, country.country AS country, s.store_id AS SID
+FROM staff AS s JOIN address AS a ON s.address_id = a.address_id JOIN city ON a.city_id = city.city_id
+	JOIN country ON city.country_id = country.country_id;
+
+--
+-- View structure for view `sales_by_store`
+--
+
+CREATE VIEW sales_by_store
+AS
+SELECT
+CONCAT(c.city, _utf8mb4',', cy.country) AS store
+, CONCAT(m.first_name, _utf8mb4' ', m.last_name) AS manager
+, SUM(p.amount) AS total_sales
+FROM payment AS p
+INNER JOIN rental AS r ON p.rental_id = r.rental_id
+INNER JOIN inventory AS i ON r.inventory_id = i.inventory_id
+INNER JOIN store AS s ON i.store_id = s.store_id
+INNER JOIN address AS a ON s.address_id = a.address_id
+INNER JOIN city AS c ON a.city_id = c.city_id
+INNER JOIN country AS cy ON c.country_id = cy.country_id
+INNER JOIN staff AS m ON s.manager_staff_id = m.staff_id
+GROUP BY s.store_id
+ORDER BY cy.country, c.city;
+
+--
+-- View structure for view `sales_by_film_category`
+--
+-- Note that total sales will add up to >100% because
+-- some titles belong to more than 1 category
+--
+
+CREATE VIEW sales_by_film_category
+AS
+SELECT
+c.name AS category
+, SUM(p.amount) AS total_sales
+FROM payment AS p
+INNER JOIN rental AS r ON p.rental_id = r.rental_id
+INNER JOIN inventory AS i ON r.inventory_id = i.inventory_id
+INNER JOIN film AS f ON i.film_id = f.film_id
+INNER JOIN film_category AS fc ON f.film_id = fc.film_id
+INNER JOIN category AS c ON fc.category_id = c.category_id
+GROUP BY c.name
+ORDER BY total_sales DESC;
+
+--
+-- View structure for view `actor_info`
+--
+
+CREATE DEFINER=CURRENT_USER SQL SECURITY INVOKER VIEW actor_info
+AS
+SELECT
+a.actor_id,
+a.first_name,
+a.last_name,
+GROUP_CONCAT(DISTINCT CONCAT(c.name, ': ',
+		(SELECT GROUP_CONCAT(f.title ORDER BY f.title SEPARATOR ', ')
+                    FROM sakila.film f
+                    INNER JOIN sakila.film_category fc
+                      ON f.film_id = fc.film_id
+                    INNER JOIN sakila.film_actor fa
+                      ON f.film_id = fa.film_id
+                    WHERE fc.category_id = c.category_id
+                    AND fa.actor_id = a.actor_id
+                 )
+             )
+             ORDER BY c.name SEPARATOR '; ')
+AS film_info
+FROM sakila.actor a
+LEFT JOIN sakila.film_actor fa
+  ON a.actor_id = fa.actor_id
+LEFT JOIN sakila.film_category fc
+  ON fa.film_id = fc.film_id
+LEFT JOIN sakila.category c
+  ON fc.category_id = c.category_id
+GROUP BY a.actor_id, a.first_name, a.last_name;
+
+--
+-- Procedure structure for procedure `rewards_report`
+--
+
+DELIMITER //
+
+CREATE PROCEDURE rewards_report (
+    IN min_monthly_purchases TINYINT UNSIGNED
+    , IN min_dollar_amount_purchased DECIMAL(10,2)
+    , OUT count_rewardees INT
+)
+LANGUAGE SQL
+NOT DETERMINISTIC
+READS SQL DATA
+SQL SECURITY DEFINER
+COMMENT 'Provides a customizable report on best customers'
+proc: BEGIN
+
+    DECLARE last_month_start DATE;
+    DECLARE last_month_end DATE;
+
+    /* Some sanity checks... */
+    IF min_monthly_purchases = 0 THEN
+        SELECT 'Minimum monthly purchases parameter must be > 0';
+        LEAVE proc;
+    END IF;
+    IF min_dollar_amount_purchased = 0.00 THEN
+        SELECT 'Minimum monthly dollar amount purchased parameter must be > $0.00';
+        LEAVE proc;
+    END IF;
+
+    /* Determine start and end time periods */
+    SET last_month_start = DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH);
+    SET last_month_start = STR_TO_DATE(CONCAT(YEAR(last_month_start),'-',MONTH(last_month_start),'-01'),'%Y-%m-%d');
+    SET last_month_end = LAST_DAY(last_month_start);
+
+    /*
+        Create a temporary storage area for
+        Customer IDs.
+    */
+    CREATE TEMPORARY TABLE tmpCustomer (customer_id SMALLINT UNSIGNED NOT NULL PRIMARY KEY);
+
+    /*
+        Find all customers meeting the
+        monthly purchase requirements
+    */
+    INSERT INTO tmpCustomer (customer_id)
+    SELECT p.customer_id
+    FROM payment AS p
+    WHERE DATE(p.payment_date) BETWEEN last_month_start AND last_month_end
+    GROUP BY customer_id
+    HAVING SUM(p.amount) > min_dollar_amount_purchased
+    AND COUNT(customer_id) > min_monthly_purchases;
+
+    /* Populate OUT parameter with count of found customers */
+    SELECT COUNT(*) FROM tmpCustomer INTO count_rewardees;
+
+    /*
+        Output ALL customer information of matching rewardees.
+        Customize output as needed.
+    */
+    SELECT c.*
+    FROM tmpCustomer AS t
+    INNER JOIN customer AS c ON t.customer_id = c.customer_id;
+
+    /* Clean up */
+    DROP TABLE tmpCustomer;
+END //
+
+DELIMITER ;
+
+DELIMITER $$
+
+CREATE FUNCTION get_customer_balance(p_customer_id INT, p_effective_date DATETIME) RETURNS DECIMAL(5,2)
+    DETERMINISTIC
+    READS SQL DATA
+BEGIN
+
+       #OK, WE NEED TO CALCULATE THE CURRENT BALANCE GIVEN A CUSTOMER_ID AND A DATE
+       #THAT WE WANT THE BALANCE TO BE EFFECTIVE FOR. THE BALANCE IS:
+       #   1) RENTAL FEES FOR ALL PREVIOUS RENTALS
+       #   2) ONE DOLLAR FOR EVERY DAY THE PREVIOUS RENTALS ARE OVERDUE
+       #   3) IF A FILM IS MORE THAN RENTAL_DURATION * 2 OVERDUE, CHARGE THE REPLACEMENT_COST
+       #   4) SUBTRACT ALL PAYMENTS MADE BEFORE THE DATE SPECIFIED
+
+  DECLARE v_rentfees DECIMAL(5,2); #FEES PAID TO RENT THE VIDEOS INITIALLY
+  DECLARE v_overfees INTEGER;      #LATE FEES FOR PRIOR RENTALS
+  DECLARE v_payments DECIMAL(5,2); #SUM OF PAYMENTS MADE PREVIOUSLY
+
+  SELECT IFNULL(SUM(film.rental_rate),0) INTO v_rentfees
+    FROM film, inventory, rental
+    WHERE film.film_id = inventory.film_id
+      AND inventory.inventory_id = rental.inventory_id
+      AND rental.rental_date <= p_effective_date
+      AND rental.customer_id = p_customer_id;
+
+  SELECT IFNULL(SUM(IF((TO_DAYS(rental.return_date) - TO_DAYS(rental.rental_date)) > film.rental_duration,
+        ((TO_DAYS(rental.return_date) - TO_DAYS(rental.rental_date)) - film.rental_duration),0)),0) INTO v_overfees
+    FROM rental, inventory, film
+    WHERE film.film_id = inventory.film_id
+      AND inventory.inventory_id = rental.inventory_id
+      AND rental.rental_date <= p_effective_date
+      AND rental.customer_id = p_customer_id;
+
+
+  SELECT IFNULL(SUM(payment.amount),0) INTO v_payments
+    FROM payment
+
+    WHERE payment.payment_date <= p_effective_date
+    AND payment.customer_id = p_customer_id;
+
+  RETURN v_rentfees + v_overfees - v_payments;
+END $$
+
+DELIMITER ;
+
+DELIMITER $$
+
+CREATE PROCEDURE film_in_stock(IN p_film_id INT, IN p_store_id INT, OUT p_film_count INT)
+READS SQL DATA
+BEGIN
+     SELECT inventory_id
+     FROM inventory
+     WHERE film_id = p_film_id
+     AND store_id = p_store_id
+     AND inventory_in_stock(inventory_id);
+
+     SELECT COUNT(*)
+     FROM inventory
+     WHERE film_id = p_film_id
+     AND store_id = p_store_id
+     AND inventory_in_stock(inventory_id)
+     INTO p_film_count;
+END $$
+
+DELIMITER ;
+
+DELIMITER $$
+
+CREATE PROCEDURE film_not_in_stock(IN p_film_id INT, IN p_store_id INT, OUT p_film_count INT)
+READS SQL DATA
+BEGIN
+     SELECT inventory_id
+     FROM inventory
+     WHERE film_id = p_film_id
+     AND store_id = p_store_id
+     AND NOT inventory_in_stock(inventory_id);
+
+     SELECT COUNT(*)
+     FROM inventory
+     WHERE film_id = p_film_id
+     AND store_id = p_store_id
+     AND NOT inventory_in_stock(inventory_id)
+     INTO p_film_count;
+END $$
+
+DELIMITER ;
+
+DELIMITER $$
+
+CREATE FUNCTION inventory_held_by_customer(p_inventory_id INT) RETURNS INT
+READS SQL DATA
+BEGIN
+  DECLARE v_customer_id INT;
+  DECLARE EXIT HANDLER FOR NOT FOUND RETURN NULL;
+
+  SELECT customer_id INTO v_customer_id
+  FROM rental
+  WHERE return_date IS NULL
+  AND inventory_id = p_inventory_id;
+
+  RETURN v_customer_id;
+END $$
+
+DELIMITER ;
+
+DELIMITER $$
+
+CREATE FUNCTION inventory_in_stock(p_inventory_id INT) RETURNS BOOLEAN
+READS SQL DATA
+BEGIN
+    DECLARE v_rentals INT;
+    DECLARE v_out     INT;
+
+    #AN ITEM IS IN-STOCK IF THERE ARE EITHER NO ROWS IN THE rental TABLE
+    #FOR THE ITEM OR ALL ROWS HAVE return_date POPULATED
+
+    SELECT COUNT(*) INTO v_rentals
+    FROM rental
+    WHERE inventory_id = p_inventory_id;
+
+    IF v_rentals = 0 THEN
+      RETURN TRUE;
+    END IF;
+
+    SELECT COUNT(rental_id) INTO v_out
+    FROM inventory LEFT JOIN rental USING(inventory_id)
+    WHERE inventory.inventory_id = p_inventory_id
+    AND rental.return_date IS NULL;
+
+    IF v_out > 0 THEN
+      RETURN FALSE;
+    ELSE
+      RETURN TRUE;
+    END IF;
+END $$
+
+DELIMITER ;
+
+SET SQL_MODE=@OLD_SQL_MODE;
+SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
+SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
+
+
diff --git a/testing/conversion/conversion_test.go b/testing/conversion/conversion_test.go
new file mode 100644
index 0000000..9b92c0f
--- /dev/null
+++ b/testing/conversion/conversion_test.go
@@ -0,0 +1,204 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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: Refactor this file and other integration tests by moving all common code
+// to remove redundancy.
+
+package conversion_test
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"log"
+	"os"
+	"sort"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/cloudspannerecosystem/harbourbridge/conversion"
+	"github.com/cloudspannerecosystem/harbourbridge/internal"
+	"github.com/cloudspannerecosystem/harbourbridge/spanner/ddl"
+	"github.com/stretchr/testify/assert"
+
+	database "cloud.google.com/go/spanner/admin/database/apiv1"
+	databasepb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
+)
+
+var (
+	projectID  string
+	instanceID string
+
+	databaseAdmin *database.DatabaseAdminClient
+)
+
+func TestMain(m *testing.M) {
+	cleanup := initTests()
+	res := m.Run()
+	cleanup()
+	os.Exit(res)
+}
+
+func initTests() (cleanup func()) {
+	projectID = os.Getenv("HARBOURBRIDGE_TESTS_GCLOUD_PROJECT_ID")
+	instanceID = os.Getenv("HARBOURBRIDGE_TESTS_GCLOUD_INSTANCE_ID")
+
+	ctx := context.Background()
+	flag.Parse() // Needed for testing.Short().
+	noop := func() {}
+
+	if testing.Short() {
+		log.Println("Unit test for UpdateDDLForeignKeys skipped in -short mode.")
+		return noop
+	}
+
+	if projectID == "" {
+		log.Println("Unit test for UpdateDDLForeignKeys skipped: HARBOURBRIDGE_TESTS_GCLOUD_PROJECT_ID is missing")
+		return noop
+	}
+
+	if instanceID == "" {
+		log.Println("Unit test for UpdateDDLForeignKeys skipped: HARBOURBRIDGE_TESTS_GCLOUD_INSTANCE_ID is missing")
+		return noop
+	}
+
+	var err error
+	databaseAdmin, err = database.NewDatabaseAdminClient(ctx)
+	if err != nil {
+		log.Fatalf("cannot create databaseAdmin client: %v", err)
+	}
+
+	return func() {
+		databaseAdmin.Close()
+	}
+}
+
+func dropDatabase(t *testing.T, dbPath string) {
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
+	defer cancel()
+	// Drop the testing database.
+	if err := databaseAdmin.DropDatabase(ctx, &databasepb.DropDatabaseRequest{Database: dbPath}); err != nil {
+		t.Fatalf("failed to drop testing database %v: %v", dbPath, err)
+	}
+}
+
+func BuildConv(t *testing.T, numCols, numFks int) *internal.Conv {
+	conv := internal.MakeConv()
+	colNames := []string{}
+	colDefs := map[string]ddl.ColumnDef{}
+	for i := 1; i <= numCols; i++ {
+		currColName := fmt.Sprintf("col%d", i)
+		colNames = append(colNames, currColName)
+		colDefs[currColName] = ddl.ColumnDef{Name: currColName, T: ddl.Type{Name: ddl.String, Len: int64(10)}}
+	}
+
+	var foreignKeys []ddl.Foreignkey
+	for i := 1; i <= numFks; i++ {
+		foreignKey := ddl.Foreignkey{
+			Name:         fmt.Sprintf("fk_%d", i),
+			Columns:      []string{fmt.Sprintf("col%d", i)},
+			ReferTable:   "table_b",
+			ReferColumns: []string{fmt.Sprintf("col%d", i)}}
+		foreignKeys = append(foreignKeys, foreignKey)
+	}
+
+	conv.SpSchema["table_a"] = ddl.CreateTable{
+		Name:     "table_a",
+		ColNames: colNames,
+		ColDefs:  colDefs,
+		Pks:      []ddl.IndexKey{ddl.IndexKey{Col: "col1"}},
+		Fks:      foreignKeys,
+	}
+	conv.SpSchema["table_b"] = ddl.CreateTable{
+		Name:     "table_b",
+		ColNames: colNames,
+		ColDefs:  colDefs,
+		Pks:      []ddl.IndexKey{ddl.IndexKey{Col: "col1"}},
+	}
+	return conv
+}
+
+func checkResults(t *testing.T, dbpath string, numFks int) {
+	ctx := context.Background()
+	resp, err := databaseAdmin.GetDatabaseDdl(ctx, &databasepb.GetDatabaseDdlRequest{Database: dbpath})
+	if err != nil {
+		t.Fatalf("Could not read DDL from database %s: %v", dbpath, err)
+	}
+
+	// Each statement in the response is the DDL for a whole table including column names, foreign key statements and primary keys.
+	// The data type is bytes. Sample resp.Statements:
+	// ["2021/07/16 13:45:14 CREATE TABLE table_a (\n  col1 STRING(10),\n  col2 STRING(10),\n) PRIMARY KEY(col1);"]
+	var stmta, stmtb string
+	if strings.Contains(resp.Statements[0], "CREATE TABLE table_a") {
+		stmta, stmtb = resp.Statements[0], resp.Statements[1]
+	} else {
+		stmta, stmtb = resp.Statements[1], resp.Statements[0]
+	}
+	assert.False(t, strings.Contains(stmtb, "FOREIGN KEY"))
+
+	wantFkStmts := []string{}
+	for i := 1; i <= numFks; i++ {
+		fkStmt := fmt.Sprintf("CONSTRAINT fk_%d FOREIGN KEY(col%d) REFERENCES table_b(col%d),", i, i, i)
+		wantFkStmts = append(wantFkStmts, fkStmt)
+	}
+	var gotFkStmts []string
+	// Filter out just the foreign key statements.
+	for _, Stmt := range strings.Split(stmta, "\n") {
+		if strings.Contains(Stmt, "FOREIGN KEY") {
+			gotFkStmts = append(gotFkStmts, strings.TrimSpace(Stmt))
+		}
+	}
+
+	sort.Strings(gotFkStmts)
+	sort.Strings(wantFkStmts)
+
+	assert.Equal(t, wantFkStmts, gotFkStmts)
+}
+
+func TestUpdateDDLForeignKeys(t *testing.T) {
+	onlyRunForEmulatorTest(t)
+	t.Parallel()
+	foreignKeyTests := []struct {
+		dbName     string
+		numCols    int // Number of columns in the table.
+		numFks     int // Number of foreign keys we want to add (ensure it is not greater than numCols).
+		numWorkers int // Number of concurrent workers (we set it as 1 for now since spanner emulator does not support concurrent schema updates yet).
+	}{
+		{"test-workers-five-fks", 10, 5, 1},
+		{"test-workers-ten-fks", 10, 10, 1},
+	}
+
+	for _, tc := range foreignKeyTests {
+		conv := BuildConv(t, tc.numCols, tc.numFks)
+		dbpath, err := conversion.CreateDatabase(projectID, instanceID, tc.dbName, conv, os.Stdout)
+		if err != nil {
+			t.Fatal(err)
+		}
+		conversion.MaxWorkers = tc.numWorkers
+		if err = conversion.UpdateDDLForeignKeys(projectID, instanceID, tc.dbName, conv, os.Stdout); err != nil {
+			t.Fatalf("\nCan't perform update operation on db %s with foreign keys: %v\n", tc.dbName, err)
+		}
+
+		checkResults(t, dbpath, tc.numFks)
+		// Drop the database later.
+		defer dropDatabase(t, dbpath)
+	}
+}
+
+func onlyRunForEmulatorTest(t *testing.T) {
+	if os.Getenv("SPANNER_EMULATOR_HOST") == "" {
+		t.Skip("Skipping tests only running against the emulator.")
+	}
+}
diff --git a/testing/dynamodb/integration_test.go b/testing/dynamodb/integration_test.go
new file mode 100644
index 0000000..6f51f9b
--- /dev/null
+++ b/testing/dynamodb/integration_test.go
@@ -0,0 +1,292 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 dynamodb_test
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"math/big"
+	"os"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/cloudspannerecosystem/harbourbridge/cmd"
+	"github.com/cloudspannerecosystem/harbourbridge/conversion"
+	"github.com/google/go-cmp/cmp"
+	"github.com/stretchr/testify/assert"
+
+	"cloud.google.com/go/spanner"
+	database "cloud.google.com/go/spanner/admin/database/apiv1"
+
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/session"
+	dydb "github.com/aws/aws-sdk-go/service/dynamodb"
+	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
+	"google.golang.org/api/iterator"
+	databasepb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
+)
+
+var (
+	projectID  string
+	instanceID string
+
+	databaseAdmin *database.DatabaseAdminClient
+	dydbClient    *dydb.DynamoDB
+)
+
+// Create struct to hold info about new item in dynamodb.
+type DydbRecord struct {
+	AttrString    string
+	AttrInt       int
+	AttrFloat     float64
+	AttrBool      bool
+	AttrBytes     []byte
+	AttrNumberSet []float64 `dynamodbav:",numberset"`
+	AttrByteSet   [][]byte
+	AttrStringSet []string `dynamodbav:",stringset"`
+	AttrList      []interface{}
+	AttrMap       map[string]int
+}
+
+type SpannerRecord struct {
+	AttrString    string
+	AttrInt       int64
+	AttrFloat     float64
+	AttrBool      bool
+	AttrBytes     []byte
+	AttrNumberSet []float64
+	AttrByteSet   [][]byte
+	AttrStringSet []string
+	AttrList      string
+	AttrMap       string
+}
+
+func TestMain(m *testing.M) {
+	cleanup := initIntegrationTests()
+	res := m.Run()
+	cleanup()
+	os.Exit(res)
+}
+
+func initIntegrationTests() (cleanup func()) {
+	projectID = os.Getenv("HARBOURBRIDGE_TESTS_GCLOUD_PROJECT_ID")
+	instanceID = os.Getenv("HARBOURBRIDGE_TESTS_GCLOUD_INSTANCE_ID")
+
+	ctx := context.Background()
+	flag.Parse() // Needed for testing.Short().
+	noop := func() {}
+
+	if testing.Short() {
+		log.Println("Integration tests skipped in -short mode.")
+		return noop
+	}
+
+	if projectID == "" {
+		log.Println("Integration tests skipped: HARBOURBRIDGE_TESTS_GCLOUD_PROJECT_ID is missing")
+		return noop
+	}
+
+	if instanceID == "" {
+		log.Println("Integration tests skipped: HARBOURBRIDGE_TESTS_GCLOUD_INSTANCE_ID is missing")
+		return noop
+	}
+
+	var err error
+	databaseAdmin, err = database.NewDatabaseAdminClient(ctx)
+	if err != nil {
+		log.Fatalf("cannot create databaseAdmin client: %v", err)
+	}
+
+	cfg := aws.Config{
+		Endpoint: aws.String("http://localhost:8000"),
+	}
+	sess := session.Must(session.NewSession())
+	dydbClient = dydb.New(sess, &cfg)
+
+	return func() {
+		databaseAdmin.Close()
+	}
+}
+
+func dropDatabase(t *testing.T, dbPath string) {
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
+	defer cancel()
+	// Drop the testing database.
+	if err := databaseAdmin.DropDatabase(ctx, &databasepb.DropDatabaseRequest{Database: dbPath}); err != nil {
+		t.Fatalf("failed to drop testing database %v: %v", dbPath, err)
+	}
+
+	deleteTableInput := &dydb.DeleteTableInput{
+		TableName: aws.String("table_test"),
+	}
+	dydbClient.DeleteTable(deleteTableInput)
+}
+
+func prepareIntegrationTest(t *testing.T) string {
+	if databaseAdmin == nil {
+		t.Skip("Integration tests skipped")
+	}
+	tmpdir, err := ioutil.TempDir(".", "int-test-")
+	if err != nil {
+		log.Fatal(err)
+	}
+	populateDynamoDB(t)
+	return tmpdir
+}
+
+func populateDynamoDB(t *testing.T) {
+	tableName := "table_test"
+	createTableInput := &dydb.CreateTableInput{
+		AttributeDefinitions: []*dydb.AttributeDefinition{
+			{
+				AttributeName: aws.String("AttrString"),
+				AttributeType: aws.String("S"),
+			},
+		},
+		KeySchema: []*dydb.KeySchemaElement{
+			{
+				AttributeName: aws.String("AttrString"),
+				KeyType:       aws.String("HASH"),
+			},
+		},
+		ProvisionedThroughput: &dydb.ProvisionedThroughput{
+			ReadCapacityUnits:  aws.Int64(10),
+			WriteCapacityUnits: aws.Int64(10),
+		},
+		TableName: aws.String(tableName),
+	}
+	_, err := dydbClient.CreateTable(createTableInput)
+	if err != nil {
+		t.Fatalf("Got error calling CreateTable: %s", err)
+	}
+	dydbRecord := DydbRecord{
+		AttrString:    "abcd",
+		AttrInt:       10,
+		AttrFloat:     14.5,
+		AttrBool:      true,
+		AttrBytes:     []byte{48, 49},
+		AttrNumberSet: []float64{1.5, 2.5, 3.5},
+		AttrByteSet:   [][]byte{[]byte{48, 49}, []byte{50, 51}},
+		AttrStringSet: []string{"abc", "xyz"},
+		AttrList:      []interface{}{"str-1", 12.34, true},
+		AttrMap:       map[string]int{"key": 100},
+	}
+	av, err := dynamodbattribute.MarshalMap(dydbRecord)
+	if err != nil {
+		t.Fatalf("Got error marshalling new movie item: %s", err)
+	}
+
+	putItemInput := &dydb.PutItemInput{
+		Item:      av,
+		TableName: aws.String(tableName),
+	}
+
+	_, err = dydbClient.PutItem(putItemInput)
+	if err != nil {
+		t.Fatalf("Got error calling PutItem: %s", err)
+	}
+	log.Println("Successfully created table and put item for dynamodb")
+}
+
+func TestIntegration_DYNAMODB_SimpleUse(t *testing.T) {
+	onlyRunForEmulatorTest(t)
+	t.Parallel()
+
+	tmpdir := prepareIntegrationTest(t)
+	defer os.RemoveAll(tmpdir)
+
+	now := time.Now()
+	dbName, _ := conversion.GetDatabaseName(conversion.DYNAMODB, now)
+	dbPath := fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectID, instanceID, dbName)
+	filePrefix := filepath.Join(tmpdir, dbName+".")
+
+	err := cmd.CommandLine(conversion.DYNAMODB, "spanner", projectID, instanceID, dbName, false, false, false, 0, "", &conversion.IOStreams{Out: os.Stdout}, filePrefix, now)
+	if err != nil {
+		t.Fatal(err)
+	}
+	// Drop the database later.
+	defer dropDatabase(t, dbPath)
+
+	checkResults(t, dbPath)
+}
+
+func checkResults(t *testing.T, dbPath string) {
+	// Make a query to check results.
+	ctx := context.Background()
+	client, err := spanner.NewClient(ctx, dbPath)
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer client.Close()
+
+	checkRow(ctx, t, client)
+}
+
+func checkRow(ctx context.Context, t *testing.T, client *spanner.Client) {
+	wantRecord := SpannerRecord{
+		AttrString:    "abcd",
+		AttrInt:       int64(10),
+		AttrFloat:     float64(14.5),
+		AttrBool:      true,
+		AttrBytes:     []byte{48, 49},
+		AttrNumberSet: []float64{1.5, 2.5, 3.5},
+		AttrByteSet:   [][]byte{[]byte{48, 49}, []byte{50, 51}},
+		AttrStringSet: []string{"abc", "xyz"},
+		AttrList:      "[\"str-1\",\"12.34\",true]",
+		AttrMap:       "{\"key\":\"100\"}",
+	}
+	gotRecord := SpannerRecord{}
+	stmt := spanner.Statement{SQL: `SELECT AttrString, AttrInt, AttrFloat, AttrBool, AttrBytes, AttrNumberSet, AttrByteSet, AttrStringSet, AttrList, AttrMap FROM table_test`}
+	iter := client.Single().Query(ctx, stmt)
+	defer iter.Stop()
+	for {
+		row, err := iter.Next()
+		if err == iterator.Done {
+			break
+		}
+		if err != nil {
+			log.Println("Error reading row: ", err)
+			t.Fatal(err)
+			break
+		}
+		var AttrInt, AttrFloat big.Rat
+		var AttrNumberSet []big.Rat
+		if err := row.Columns(&gotRecord.AttrString, &AttrInt, &AttrFloat, &gotRecord.AttrBool, &gotRecord.AttrBytes, &AttrNumberSet, &gotRecord.AttrByteSet, &gotRecord.AttrStringSet, &gotRecord.AttrList, &gotRecord.AttrMap); err != nil {
+			log.Println("Error reading into variables: ", err)
+			t.Fatal(err)
+			break
+		}
+		gotRecord.AttrFloat, _ = AttrFloat.Float64()
+		floatVal, _ := AttrInt.Float64()
+		gotRecord.AttrInt = int64(floatVal)
+		floatSet := []float64{}
+		for _, numericVal := range AttrNumberSet {
+			floatVal, _ = numericVal.Float64()
+			floatSet = append(floatSet, floatVal)
+		}
+		gotRecord.AttrNumberSet = floatSet
+	}
+	assert.True(t, cmp.Equal(wantRecord, gotRecord))
+}
+
+func onlyRunForEmulatorTest(t *testing.T) {
+	if os.Getenv("SPANNER_EMULATOR_HOST") == "" {
+		t.Skip("Skipping tests only running against the emulator.")
+	}
+}
diff --git a/testing/mysql/integration_test.go b/testing/mysql/integration_test.go
index 4606e7a..f833771 100644
--- a/testing/mysql/integration_test.go
+++ b/testing/mysql/integration_test.go
@@ -118,7 +118,7 @@ func TestIntegration_MYSQLDUMP_SimpleUse(t *testing.T) {
 	if err != nil {
 		t.Fatalf("failed to open the test data file: %v", err)
 	}
-	err = cmd.CommandLine(conversion.MYSQLDUMP, projectID, instanceID, dbName, false, false, 0, "", &conversion.IOStreams{In: f, Out: os.Stdout}, filePrefix, now)
+	err = cmd.CommandLine(conversion.MYSQLDUMP, "spanner", projectID, instanceID, dbName, false, false, false, 0, "", &conversion.IOStreams{In: f, Out: os.Stdout}, filePrefix, now)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -140,7 +140,7 @@ func TestIntegration_MYSQL_SimpleUse(t *testing.T) {
 	dbPath := fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectID, instanceID, dbName)
 	filePrefix := filepath.Join(tmpdir, dbName+".")
 
-	err := cmd.CommandLine(conversion.MYSQL, projectID, instanceID, dbName, false, false, 0, "", &conversion.IOStreams{Out: os.Stdout}, filePrefix, now)
+	err := cmd.CommandLine(conversion.MYSQL, "spanner", projectID, instanceID, dbName, false, false, false, 0, "", &conversion.IOStreams{Out: os.Stdout}, filePrefix, now)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/testing/postgres/integration_test.go b/testing/postgres/integration_test.go
index 536704f..ce25d94 100644
--- a/testing/postgres/integration_test.go
+++ b/testing/postgres/integration_test.go
@@ -120,7 +120,7 @@ func TestIntegration_PGDUMP_SimpleUse(t *testing.T) {
 	if err != nil {
 		t.Fatalf("failed to open the test data file: %v", err)
 	}
-	err = cmd.CommandLine(conversion.PGDUMP, projectID, instanceID, dbName, false, false, 0, "", &conversion.IOStreams{In: f, Out: os.Stdout}, filePrefix, now)
+	err = cmd.CommandLine(conversion.PGDUMP, "spanner", projectID, instanceID, dbName, false, false, false, 0, "", &conversion.IOStreams{In: f, Out: os.Stdout}, filePrefix, now)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -177,7 +177,7 @@ func TestIntegration_POSTGRES_SimpleUse(t *testing.T) {
 	dbPath := fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectID, instanceID, dbName)
 	filePrefix := filepath.Join(tmpdir, dbName+".")
 
-	err := cmd.CommandLine(conversion.POSTGRES, projectID, instanceID, dbName, false, false, 0, "", &conversion.IOStreams{Out: os.Stdout}, filePrefix, now)
+	err := cmd.CommandLine(conversion.POSTGRES, "spanner", projectID, instanceID, dbName, false, false, false, 0, "", &conversion.IOStreams{Out: os.Stdout}, filePrefix, now)
 	if err != nil {
 		t.Fatal(err)
 	}