#
tokens: 42890/50000 3/868 files (page 41/53)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 41 of 53. Use http://codebase.md/googleapis/genai-toolbox?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .ci
│   ├── continuous.release.cloudbuild.yaml
│   ├── generate_release_table.sh
│   ├── integration.cloudbuild.yaml
│   ├── quickstart_test
│   │   ├── go.integration.cloudbuild.yaml
│   │   ├── js.integration.cloudbuild.yaml
│   │   ├── py.integration.cloudbuild.yaml
│   │   ├── run_go_tests.sh
│   │   ├── run_js_tests.sh
│   │   ├── run_py_tests.sh
│   │   └── setup_hotels_sample.sql
│   ├── test_with_coverage.sh
│   └── versioned.release.cloudbuild.yaml
├── .github
│   ├── auto-label.yaml
│   ├── blunderbuss.yml
│   ├── CODEOWNERS
│   ├── header-checker-lint.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── feature_request.yml
│   │   └── question.yml
│   ├── label-sync.yml
│   ├── labels.yaml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── release-please.yml
│   ├── renovate.json5
│   ├── sync-repo-settings.yaml
│   └── workflows
│       ├── cloud_build_failure_reporter.yml
│       ├── deploy_dev_docs.yaml
│       ├── deploy_previous_version_docs.yaml
│       ├── deploy_versioned_docs.yaml
│       ├── docs_deploy.yaml
│       ├── docs_preview_clean.yaml
│       ├── docs_preview_deploy.yaml
│       ├── lint.yaml
│       ├── schedule_reporter.yml
│       ├── sync-labels.yaml
│       └── tests.yaml
├── .gitignore
├── .gitmodules
├── .golangci.yaml
├── .hugo
│   ├── archetypes
│   │   └── default.md
│   ├── assets
│   │   ├── icons
│   │   │   └── logo.svg
│   │   └── scss
│   │       ├── _styles_project.scss
│   │       └── _variables_project.scss
│   ├── go.mod
│   ├── go.sum
│   ├── hugo.toml
│   ├── layouts
│   │   ├── _default
│   │   │   └── home.releases.releases
│   │   ├── index.llms-full.txt
│   │   ├── index.llms.txt
│   │   ├── partials
│   │   │   ├── hooks
│   │   │   │   └── head-end.html
│   │   │   ├── navbar-version-selector.html
│   │   │   ├── page-meta-links.html
│   │   │   └── td
│   │   │       └── render-heading.html
│   │   ├── robot.txt
│   │   └── shortcodes
│   │       ├── include.html
│   │       ├── ipynb.html
│   │       └── regionInclude.html
│   ├── package-lock.json
│   ├── package.json
│   └── static
│       ├── favicons
│       │   ├── android-chrome-192x192.png
│       │   ├── android-chrome-512x512.png
│       │   ├── apple-touch-icon.png
│       │   ├── favicon-16x16.png
│       │   ├── favicon-32x32.png
│       │   └── favicon.ico
│       └── js
│           └── w3.js
├── CHANGELOG.md
├── cmd
│   ├── options_test.go
│   ├── options.go
│   ├── root_test.go
│   ├── root.go
│   └── version.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DEVELOPER.md
├── Dockerfile
├── docs
│   └── en
│       ├── _index.md
│       ├── about
│       │   ├── _index.md
│       │   └── faq.md
│       ├── concepts
│       │   ├── _index.md
│       │   └── telemetry
│       │       ├── index.md
│       │       ├── telemetry_flow.png
│       │       └── telemetry_traces.png
│       ├── getting-started
│       │   ├── _index.md
│       │   ├── colab_quickstart.ipynb
│       │   ├── configure.md
│       │   ├── introduction
│       │   │   ├── _index.md
│       │   │   └── architecture.png
│       │   ├── local_quickstart_go.md
│       │   ├── local_quickstart_js.md
│       │   ├── local_quickstart.md
│       │   ├── mcp_quickstart
│       │   │   ├── _index.md
│       │   │   ├── inspector_tools.png
│       │   │   └── inspector.png
│       │   └── quickstart
│       │       ├── go
│       │       │   ├── adkgo
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── genAI
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── genkit
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── langchain
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── openAI
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   └── quickstart_test.go
│       │       ├── golden.txt
│       │       ├── js
│       │       │   ├── genAI
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   ├── genkit
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   ├── langchain
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   ├── llamaindex
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   └── quickstart.test.js
│       │       ├── python
│       │       │   ├── __init__.py
│       │       │   ├── adk
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   ├── core
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   ├── langchain
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   ├── llamaindex
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   └── quickstart_test.py
│       │       └── shared
│       │           ├── cloud_setup.md
│       │           ├── configure_toolbox.md
│       │           └── database_setup.md
│       ├── how-to
│       │   ├── _index.md
│       │   ├── connect_via_geminicli.md
│       │   ├── connect_via_mcp.md
│       │   ├── connect-ide
│       │   │   ├── _index.md
│       │   │   ├── alloydb_pg_admin_mcp.md
│       │   │   ├── alloydb_pg_mcp.md
│       │   │   ├── bigquery_mcp.md
│       │   │   ├── cloud_sql_mssql_admin_mcp.md
│       │   │   ├── cloud_sql_mssql_mcp.md
│       │   │   ├── cloud_sql_mysql_admin_mcp.md
│       │   │   ├── cloud_sql_mysql_mcp.md
│       │   │   ├── cloud_sql_pg_admin_mcp.md
│       │   │   ├── cloud_sql_pg_mcp.md
│       │   │   ├── firestore_mcp.md
│       │   │   ├── looker_mcp.md
│       │   │   ├── mssql_mcp.md
│       │   │   ├── mysql_mcp.md
│       │   │   ├── neo4j_mcp.md
│       │   │   ├── postgres_mcp.md
│       │   │   ├── spanner_mcp.md
│       │   │   └── sqlite_mcp.md
│       │   ├── deploy_docker.md
│       │   ├── deploy_gke.md
│       │   ├── deploy_toolbox.md
│       │   ├── export_telemetry.md
│       │   └── toolbox-ui
│       │       ├── edit-headers.gif
│       │       ├── edit-headers.png
│       │       ├── index.md
│       │       ├── optional-param-checked.png
│       │       ├── optional-param-unchecked.png
│       │       ├── run-tool.gif
│       │       ├── tools.png
│       │       └── toolsets.png
│       ├── reference
│       │   ├── _index.md
│       │   ├── cli.md
│       │   └── prebuilt-tools.md
│       ├── resources
│       │   ├── _index.md
│       │   ├── authServices
│       │   │   ├── _index.md
│       │   │   └── google.md
│       │   ├── sources
│       │   │   ├── _index.md
│       │   │   ├── alloydb-admin.md
│       │   │   ├── alloydb-pg.md
│       │   │   ├── bigquery.md
│       │   │   ├── bigtable.md
│       │   │   ├── cassandra.md
│       │   │   ├── clickhouse.md
│       │   │   ├── cloud-monitoring.md
│       │   │   ├── cloud-sql-admin.md
│       │   │   ├── cloud-sql-mssql.md
│       │   │   ├── cloud-sql-mysql.md
│       │   │   ├── cloud-sql-pg.md
│       │   │   ├── couchbase.md
│       │   │   ├── dataplex.md
│       │   │   ├── dgraph.md
│       │   │   ├── elasticsearch.md
│       │   │   ├── firebird.md
│       │   │   ├── firestore.md
│       │   │   ├── http.md
│       │   │   ├── looker.md
│       │   │   ├── mindsdb.md
│       │   │   ├── mongodb.md
│       │   │   ├── mssql.md
│       │   │   ├── mysql.md
│       │   │   ├── neo4j.md
│       │   │   ├── oceanbase.md
│       │   │   ├── oracle.md
│       │   │   ├── postgres.md
│       │   │   ├── redis.md
│       │   │   ├── serverless-spark.md
│       │   │   ├── spanner.md
│       │   │   ├── sqlite.md
│       │   │   ├── tidb.md
│       │   │   ├── trino.md
│       │   │   ├── valkey.md
│       │   │   └── yugabytedb.md
│       │   └── tools
│       │       ├── _index.md
│       │       ├── alloydb
│       │       │   ├── _index.md
│       │       │   ├── alloydb-create-cluster.md
│       │       │   ├── alloydb-create-instance.md
│       │       │   ├── alloydb-create-user.md
│       │       │   ├── alloydb-get-cluster.md
│       │       │   ├── alloydb-get-instance.md
│       │       │   ├── alloydb-get-user.md
│       │       │   ├── alloydb-list-clusters.md
│       │       │   ├── alloydb-list-instances.md
│       │       │   ├── alloydb-list-users.md
│       │       │   └── alloydb-wait-for-operation.md
│       │       ├── alloydbainl
│       │       │   ├── _index.md
│       │       │   └── alloydb-ai-nl.md
│       │       ├── bigquery
│       │       │   ├── _index.md
│       │       │   ├── bigquery-analyze-contribution.md
│       │       │   ├── bigquery-conversational-analytics.md
│       │       │   ├── bigquery-execute-sql.md
│       │       │   ├── bigquery-forecast.md
│       │       │   ├── bigquery-get-dataset-info.md
│       │       │   ├── bigquery-get-table-info.md
│       │       │   ├── bigquery-list-dataset-ids.md
│       │       │   ├── bigquery-list-table-ids.md
│       │       │   ├── bigquery-search-catalog.md
│       │       │   └── bigquery-sql.md
│       │       ├── bigtable
│       │       │   ├── _index.md
│       │       │   └── bigtable-sql.md
│       │       ├── cassandra
│       │       │   ├── _index.md
│       │       │   └── cassandra-cql.md
│       │       ├── clickhouse
│       │       │   ├── _index.md
│       │       │   ├── clickhouse-execute-sql.md
│       │       │   ├── clickhouse-list-databases.md
│       │       │   ├── clickhouse-list-tables.md
│       │       │   └── clickhouse-sql.md
│       │       ├── cloudmonitoring
│       │       │   ├── _index.md
│       │       │   └── cloud-monitoring-query-prometheus.md
│       │       ├── cloudsql
│       │       │   ├── _index.md
│       │       │   ├── cloudsqlcreatedatabase.md
│       │       │   ├── cloudsqlcreateusers.md
│       │       │   ├── cloudsqlgetinstances.md
│       │       │   ├── cloudsqllistdatabases.md
│       │       │   ├── cloudsqllistinstances.md
│       │       │   ├── cloudsqlmssqlcreateinstance.md
│       │       │   ├── cloudsqlmysqlcreateinstance.md
│       │       │   ├── cloudsqlpgcreateinstances.md
│       │       │   └── cloudsqlwaitforoperation.md
│       │       ├── couchbase
│       │       │   ├── _index.md
│       │       │   └── couchbase-sql.md
│       │       ├── dataform
│       │       │   ├── _index.md
│       │       │   └── dataform-compile-local.md
│       │       ├── dataplex
│       │       │   ├── _index.md
│       │       │   ├── dataplex-lookup-entry.md
│       │       │   ├── dataplex-search-aspect-types.md
│       │       │   └── dataplex-search-entries.md
│       │       ├── dgraph
│       │       │   ├── _index.md
│       │       │   └── dgraph-dql.md
│       │       ├── elasticsearch
│       │       │   ├── _index.md
│       │       │   └── elasticsearch-esql.md
│       │       ├── firebird
│       │       │   ├── _index.md
│       │       │   ├── firebird-execute-sql.md
│       │       │   └── firebird-sql.md
│       │       ├── firestore
│       │       │   ├── _index.md
│       │       │   ├── firestore-add-documents.md
│       │       │   ├── firestore-delete-documents.md
│       │       │   ├── firestore-get-documents.md
│       │       │   ├── firestore-get-rules.md
│       │       │   ├── firestore-list-collections.md
│       │       │   ├── firestore-query-collection.md
│       │       │   ├── firestore-query.md
│       │       │   ├── firestore-update-document.md
│       │       │   └── firestore-validate-rules.md
│       │       ├── http
│       │       │   ├── _index.md
│       │       │   └── http.md
│       │       ├── looker
│       │       │   ├── _index.md
│       │       │   ├── looker-add-dashboard-element.md
│       │       │   ├── looker-conversational-analytics.md
│       │       │   ├── looker-create-project-file.md
│       │       │   ├── looker-delete-project-file.md
│       │       │   ├── looker-dev-mode.md
│       │       │   ├── looker-get-connection-databases.md
│       │       │   ├── looker-get-connection-schemas.md
│       │       │   ├── looker-get-connection-table-columns.md
│       │       │   ├── looker-get-connection-tables.md
│       │       │   ├── looker-get-connections.md
│       │       │   ├── looker-get-dashboards.md
│       │       │   ├── looker-get-dimensions.md
│       │       │   ├── looker-get-explores.md
│       │       │   ├── looker-get-filters.md
│       │       │   ├── looker-get-looks.md
│       │       │   ├── looker-get-measures.md
│       │       │   ├── looker-get-models.md
│       │       │   ├── looker-get-parameters.md
│       │       │   ├── looker-get-project-file.md
│       │       │   ├── looker-get-project-files.md
│       │       │   ├── looker-get-projects.md
│       │       │   ├── looker-health-analyze.md
│       │       │   ├── looker-health-pulse.md
│       │       │   ├── looker-health-vacuum.md
│       │       │   ├── looker-make-dashboard.md
│       │       │   ├── looker-make-look.md
│       │       │   ├── looker-query-sql.md
│       │       │   ├── looker-query-url.md
│       │       │   ├── looker-query.md
│       │       │   ├── looker-run-look.md
│       │       │   └── looker-update-project-file.md
│       │       ├── mindsdb
│       │       │   ├── _index.md
│       │       │   ├── mindsdb-execute-sql.md
│       │       │   └── mindsdb-sql.md
│       │       ├── mongodb
│       │       │   ├── _index.md
│       │       │   ├── mongodb-aggregate.md
│       │       │   ├── mongodb-delete-many.md
│       │       │   ├── mongodb-delete-one.md
│       │       │   ├── mongodb-find-one.md
│       │       │   ├── mongodb-find.md
│       │       │   ├── mongodb-insert-many.md
│       │       │   ├── mongodb-insert-one.md
│       │       │   ├── mongodb-update-many.md
│       │       │   └── mongodb-update-one.md
│       │       ├── mssql
│       │       │   ├── _index.md
│       │       │   ├── mssql-execute-sql.md
│       │       │   ├── mssql-list-tables.md
│       │       │   └── mssql-sql.md
│       │       ├── mysql
│       │       │   ├── _index.md
│       │       │   ├── mysql-execute-sql.md
│       │       │   ├── mysql-list-active-queries.md
│       │       │   ├── mysql-list-table-fragmentation.md
│       │       │   ├── mysql-list-tables-missing-unique-indexes.md
│       │       │   ├── mysql-list-tables.md
│       │       │   └── mysql-sql.md
│       │       ├── neo4j
│       │       │   ├── _index.md
│       │       │   ├── neo4j-cypher.md
│       │       │   ├── neo4j-execute-cypher.md
│       │       │   └── neo4j-schema.md
│       │       ├── oceanbase
│       │       │   ├── _index.md
│       │       │   ├── oceanbase-execute-sql.md
│       │       │   └── oceanbase-sql.md
│       │       ├── oracle
│       │       │   ├── _index.md
│       │       │   ├── oracle-execute-sql.md
│       │       │   └── oracle-sql.md
│       │       ├── postgres
│       │       │   ├── _index.md
│       │       │   ├── postgres-execute-sql.md
│       │       │   ├── postgres-list-active-queries.md
│       │       │   ├── postgres-list-available-extensions.md
│       │       │   ├── postgres-list-installed-extensions.md
│       │       │   ├── postgres-list-schemas.md
│       │       │   ├── postgres-list-tables.md
│       │       │   ├── postgres-list-views.md
│       │       │   └── postgres-sql.md
│       │       ├── redis
│       │       │   ├── _index.md
│       │       │   └── redis.md
│       │       ├── serverless-spark
│       │       │   ├── _index.md
│       │       │   ├── serverless-spark-cancel-batch.md
│       │       │   ├── serverless-spark-get-batch.md
│       │       │   └── serverless-spark-list-batches.md
│       │       ├── spanner
│       │       │   ├── _index.md
│       │       │   ├── spanner-execute-sql.md
│       │       │   ├── spanner-list-tables.md
│       │       │   └── spanner-sql.md
│       │       ├── sqlite
│       │       │   ├── _index.md
│       │       │   ├── sqlite-execute-sql.md
│       │       │   └── sqlite-sql.md
│       │       ├── tidb
│       │       │   ├── _index.md
│       │       │   ├── tidb-execute-sql.md
│       │       │   └── tidb-sql.md
│       │       ├── trino
│       │       │   ├── _index.md
│       │       │   ├── trino-execute-sql.md
│       │       │   └── trino-sql.md
│       │       ├── utility
│       │       │   ├── _index.md
│       │       │   └── wait.md
│       │       ├── valkey
│       │       │   ├── _index.md
│       │       │   └── valkey.md
│       │       └── yuagbytedb
│       │           ├── _index.md
│       │           └── yugabytedb-sql.md
│       ├── samples
│       │   ├── _index.md
│       │   ├── alloydb
│       │   │   ├── _index.md
│       │   │   ├── ai-nl
│       │   │   │   ├── alloydb_ai_nl.ipynb
│       │   │   │   └── index.md
│       │   │   └── mcp_quickstart.md
│       │   ├── bigquery
│       │   │   ├── _index.md
│       │   │   ├── colab_quickstart_bigquery.ipynb
│       │   │   ├── local_quickstart.md
│       │   │   └── mcp_quickstart
│       │   │       ├── _index.md
│       │   │       ├── inspector_tools.png
│       │   │       └── inspector.png
│       │   └── looker
│       │       ├── _index.md
│       │       ├── looker_gemini_oauth
│       │       │   ├── _index.md
│       │       │   ├── authenticated.png
│       │       │   ├── authorize.png
│       │       │   └── registration.png
│       │       ├── looker_gemini.md
│       │       └── looker_mcp_inspector
│       │           ├── _index.md
│       │           ├── inspector_tools.png
│       │           └── inspector.png
│       └── sdks
│           ├── _index.md
│           ├── go-sdk.md
│           ├── js-sdk.md
│           └── python-sdk.md
├── gemini-extension.json
├── go.mod
├── go.sum
├── internal
│   ├── auth
│   │   ├── auth.go
│   │   └── google
│   │       └── google.go
│   ├── log
│   │   ├── handler.go
│   │   ├── log_test.go
│   │   ├── log.go
│   │   └── logger.go
│   ├── prebuiltconfigs
│   │   ├── prebuiltconfigs_test.go
│   │   ├── prebuiltconfigs.go
│   │   └── tools
│   │       ├── alloydb-postgres-admin.yaml
│   │       ├── alloydb-postgres-observability.yaml
│   │       ├── alloydb-postgres.yaml
│   │       ├── bigquery.yaml
│   │       ├── clickhouse.yaml
│   │       ├── cloud-sql-mssql-admin.yaml
│   │       ├── cloud-sql-mssql-observability.yaml
│   │       ├── cloud-sql-mssql.yaml
│   │       ├── cloud-sql-mysql-admin.yaml
│   │       ├── cloud-sql-mysql-observability.yaml
│   │       ├── cloud-sql-mysql.yaml
│   │       ├── cloud-sql-postgres-admin.yaml
│   │       ├── cloud-sql-postgres-observability.yaml
│   │       ├── cloud-sql-postgres.yaml
│   │       ├── dataplex.yaml
│   │       ├── elasticsearch.yaml
│   │       ├── firestore.yaml
│   │       ├── looker-conversational-analytics.yaml
│   │       ├── looker.yaml
│   │       ├── mindsdb.yaml
│   │       ├── mssql.yaml
│   │       ├── mysql.yaml
│   │       ├── neo4j.yaml
│   │       ├── oceanbase.yaml
│   │       ├── postgres.yaml
│   │       ├── serverless-spark.yaml
│   │       ├── spanner-postgres.yaml
│   │       ├── spanner.yaml
│   │       └── sqlite.yaml
│   ├── server
│   │   ├── api_test.go
│   │   ├── api.go
│   │   ├── common_test.go
│   │   ├── config.go
│   │   ├── mcp
│   │   │   ├── jsonrpc
│   │   │   │   ├── jsonrpc_test.go
│   │   │   │   └── jsonrpc.go
│   │   │   ├── mcp.go
│   │   │   ├── util
│   │   │   │   └── lifecycle.go
│   │   │   ├── v20241105
│   │   │   │   ├── method.go
│   │   │   │   └── types.go
│   │   │   ├── v20250326
│   │   │   │   ├── method.go
│   │   │   │   └── types.go
│   │   │   └── v20250618
│   │   │       ├── method.go
│   │   │       └── types.go
│   │   ├── mcp_test.go
│   │   ├── mcp.go
│   │   ├── server_test.go
│   │   ├── server.go
│   │   ├── static
│   │   │   ├── assets
│   │   │   │   └── mcptoolboxlogo.png
│   │   │   ├── css
│   │   │   │   └── style.css
│   │   │   ├── index.html
│   │   │   ├── js
│   │   │   │   ├── auth.js
│   │   │   │   ├── loadTools.js
│   │   │   │   ├── mainContent.js
│   │   │   │   ├── navbar.js
│   │   │   │   ├── runTool.js
│   │   │   │   ├── toolDisplay.js
│   │   │   │   ├── tools.js
│   │   │   │   └── toolsets.js
│   │   │   ├── tools.html
│   │   │   └── toolsets.html
│   │   ├── web_test.go
│   │   └── web.go
│   ├── sources
│   │   ├── alloydbadmin
│   │   │   ├── alloydbadmin_test.go
│   │   │   └── alloydbadmin.go
│   │   ├── alloydbpg
│   │   │   ├── alloydb_pg_test.go
│   │   │   └── alloydb_pg.go
│   │   ├── bigquery
│   │   │   ├── bigquery_test.go
│   │   │   ├── bigquery.go
│   │   │   └── cache.go
│   │   ├── bigtable
│   │   │   ├── bigtable_test.go
│   │   │   └── bigtable.go
│   │   ├── cassandra
│   │   │   ├── cassandra_test.go
│   │   │   └── cassandra.go
│   │   ├── clickhouse
│   │   │   ├── clickhouse_test.go
│   │   │   └── clickhouse.go
│   │   ├── cloudmonitoring
│   │   │   ├── cloud_monitoring_test.go
│   │   │   └── cloud_monitoring.go
│   │   ├── cloudsqladmin
│   │   │   ├── cloud_sql_admin_test.go
│   │   │   └── cloud_sql_admin.go
│   │   ├── cloudsqlmssql
│   │   │   ├── cloud_sql_mssql_test.go
│   │   │   └── cloud_sql_mssql.go
│   │   ├── cloudsqlmysql
│   │   │   ├── cloud_sql_mysql_test.go
│   │   │   └── cloud_sql_mysql.go
│   │   ├── cloudsqlpg
│   │   │   ├── cloud_sql_pg_test.go
│   │   │   └── cloud_sql_pg.go
│   │   ├── couchbase
│   │   │   ├── couchbase_test.go
│   │   │   └── couchbase.go
│   │   ├── dataplex
│   │   │   ├── dataplex_test.go
│   │   │   └── dataplex.go
│   │   ├── dgraph
│   │   │   ├── dgraph_test.go
│   │   │   └── dgraph.go
│   │   ├── dialect.go
│   │   ├── elasticsearch
│   │   │   ├── elasticsearch_test.go
│   │   │   └── elasticsearch.go
│   │   ├── firebird
│   │   │   ├── firebird_test.go
│   │   │   └── firebird.go
│   │   ├── firestore
│   │   │   ├── firestore_test.go
│   │   │   └── firestore.go
│   │   ├── http
│   │   │   ├── http_test.go
│   │   │   └── http.go
│   │   ├── ip_type.go
│   │   ├── looker
│   │   │   ├── looker_test.go
│   │   │   └── looker.go
│   │   ├── mindsdb
│   │   │   ├── mindsdb_test.go
│   │   │   └── mindsdb.go
│   │   ├── mongodb
│   │   │   ├── mongodb_test.go
│   │   │   └── mongodb.go
│   │   ├── mssql
│   │   │   ├── mssql_test.go
│   │   │   └── mssql.go
│   │   ├── mysql
│   │   │   ├── mysql_test.go
│   │   │   └── mysql.go
│   │   ├── neo4j
│   │   │   ├── neo4j_test.go
│   │   │   └── neo4j.go
│   │   ├── oceanbase
│   │   │   ├── oceanbase_test.go
│   │   │   └── oceanbase.go
│   │   ├── oracle
│   │   │   └── oracle.go
│   │   ├── postgres
│   │   │   ├── postgres_test.go
│   │   │   └── postgres.go
│   │   ├── redis
│   │   │   ├── redis_test.go
│   │   │   └── redis.go
│   │   ├── serverlessspark
│   │   │   ├── serverlessspark_test.go
│   │   │   └── serverlessspark.go
│   │   ├── sources.go
│   │   ├── spanner
│   │   │   ├── spanner_test.go
│   │   │   └── spanner.go
│   │   ├── sqlite
│   │   │   ├── sqlite_test.go
│   │   │   └── sqlite.go
│   │   ├── tidb
│   │   │   ├── tidb_test.go
│   │   │   └── tidb.go
│   │   ├── trino
│   │   │   ├── trino_test.go
│   │   │   └── trino.go
│   │   ├── util.go
│   │   ├── valkey
│   │   │   ├── valkey_test.go
│   │   │   └── valkey.go
│   │   └── yugabytedb
│   │       ├── yugabytedb_test.go
│   │       └── yugabytedb.go
│   ├── telemetry
│   │   ├── instrumentation.go
│   │   └── telemetry.go
│   ├── testutils
│   │   └── testutils.go
│   ├── tools
│   │   ├── alloydb
│   │   │   ├── alloydbcreatecluster
│   │   │   │   ├── alloydbcreatecluster_test.go
│   │   │   │   └── alloydbcreatecluster.go
│   │   │   ├── alloydbcreateinstance
│   │   │   │   ├── alloydbcreateinstance_test.go
│   │   │   │   └── alloydbcreateinstance.go
│   │   │   ├── alloydbcreateuser
│   │   │   │   ├── alloydbcreateuser_test.go
│   │   │   │   └── alloydbcreateuser.go
│   │   │   ├── alloydbgetcluster
│   │   │   │   ├── alloydbgetcluster_test.go
│   │   │   │   └── alloydbgetcluster.go
│   │   │   ├── alloydbgetinstance
│   │   │   │   ├── alloydbgetinstance_test.go
│   │   │   │   └── alloydbgetinstance.go
│   │   │   ├── alloydbgetuser
│   │   │   │   ├── alloydbgetuser_test.go
│   │   │   │   └── alloydbgetuser.go
│   │   │   ├── alloydblistclusters
│   │   │   │   ├── alloydblistclusters_test.go
│   │   │   │   └── alloydblistclusters.go
│   │   │   ├── alloydblistinstances
│   │   │   │   ├── alloydblistinstances_test.go
│   │   │   │   └── alloydblistinstances.go
│   │   │   ├── alloydblistusers
│   │   │   │   ├── alloydblistusers_test.go
│   │   │   │   └── alloydblistusers.go
│   │   │   └── alloydbwaitforoperation
│   │   │       ├── alloydbwaitforoperation_test.go
│   │   │       └── alloydbwaitforoperation.go
│   │   ├── alloydbainl
│   │   │   ├── alloydbainl_test.go
│   │   │   └── alloydbainl.go
│   │   ├── bigquery
│   │   │   ├── bigqueryanalyzecontribution
│   │   │   │   ├── bigqueryanalyzecontribution_test.go
│   │   │   │   └── bigqueryanalyzecontribution.go
│   │   │   ├── bigquerycommon
│   │   │   │   ├── table_name_parser_test.go
│   │   │   │   ├── table_name_parser.go
│   │   │   │   └── util.go
│   │   │   ├── bigqueryconversationalanalytics
│   │   │   │   ├── bigqueryconversationalanalytics_test.go
│   │   │   │   └── bigqueryconversationalanalytics.go
│   │   │   ├── bigqueryexecutesql
│   │   │   │   ├── bigqueryexecutesql_test.go
│   │   │   │   └── bigqueryexecutesql.go
│   │   │   ├── bigqueryforecast
│   │   │   │   ├── bigqueryforecast_test.go
│   │   │   │   └── bigqueryforecast.go
│   │   │   ├── bigquerygetdatasetinfo
│   │   │   │   ├── bigquerygetdatasetinfo_test.go
│   │   │   │   └── bigquerygetdatasetinfo.go
│   │   │   ├── bigquerygettableinfo
│   │   │   │   ├── bigquerygettableinfo_test.go
│   │   │   │   └── bigquerygettableinfo.go
│   │   │   ├── bigquerylistdatasetids
│   │   │   │   ├── bigquerylistdatasetids_test.go
│   │   │   │   └── bigquerylistdatasetids.go
│   │   │   ├── bigquerylisttableids
│   │   │   │   ├── bigquerylisttableids_test.go
│   │   │   │   └── bigquerylisttableids.go
│   │   │   ├── bigquerysearchcatalog
│   │   │   │   ├── bigquerysearchcatalog_test.go
│   │   │   │   └── bigquerysearchcatalog.go
│   │   │   └── bigquerysql
│   │   │       ├── bigquerysql_test.go
│   │   │       └── bigquerysql.go
│   │   ├── bigtable
│   │   │   ├── bigtable_test.go
│   │   │   └── bigtable.go
│   │   ├── cassandra
│   │   │   └── cassandracql
│   │   │       ├── cassandracql_test.go
│   │   │       └── cassandracql.go
│   │   ├── clickhouse
│   │   │   ├── clickhouseexecutesql
│   │   │   │   ├── clickhouseexecutesql_test.go
│   │   │   │   └── clickhouseexecutesql.go
│   │   │   ├── clickhouselistdatabases
│   │   │   │   ├── clickhouselistdatabases_test.go
│   │   │   │   └── clickhouselistdatabases.go
│   │   │   ├── clickhouselisttables
│   │   │   │   ├── clickhouselisttables_test.go
│   │   │   │   └── clickhouselisttables.go
│   │   │   └── clickhousesql
│   │   │       ├── clickhousesql_test.go
│   │   │       └── clickhousesql.go
│   │   ├── cloudmonitoring
│   │   │   ├── cloudmonitoring_test.go
│   │   │   └── cloudmonitoring.go
│   │   ├── cloudsql
│   │   │   ├── cloudsqlcreatedatabase
│   │   │   │   ├── cloudsqlcreatedatabase_test.go
│   │   │   │   └── cloudsqlcreatedatabase.go
│   │   │   ├── cloudsqlcreateusers
│   │   │   │   ├── cloudsqlcreateusers_test.go
│   │   │   │   └── cloudsqlcreateusers.go
│   │   │   ├── cloudsqlgetinstances
│   │   │   │   ├── cloudsqlgetinstances_test.go
│   │   │   │   └── cloudsqlgetinstances.go
│   │   │   ├── cloudsqllistdatabases
│   │   │   │   ├── cloudsqllistdatabases_test.go
│   │   │   │   └── cloudsqllistdatabases.go
│   │   │   ├── cloudsqllistinstances
│   │   │   │   ├── cloudsqllistinstances_test.go
│   │   │   │   └── cloudsqllistinstances.go
│   │   │   └── cloudsqlwaitforoperation
│   │   │       ├── cloudsqlwaitforoperation_test.go
│   │   │       └── cloudsqlwaitforoperation.go
│   │   ├── cloudsqlmssql
│   │   │   └── cloudsqlmssqlcreateinstance
│   │   │       ├── cloudsqlmssqlcreateinstance_test.go
│   │   │       └── cloudsqlmssqlcreateinstance.go
│   │   ├── cloudsqlmysql
│   │   │   └── cloudsqlmysqlcreateinstance
│   │   │       ├── cloudsqlmysqlcreateinstance_test.go
│   │   │       └── cloudsqlmysqlcreateinstance.go
│   │   ├── cloudsqlpg
│   │   │   └── cloudsqlpgcreateinstances
│   │   │       ├── cloudsqlpgcreateinstances_test.go
│   │   │       └── cloudsqlpgcreateinstances.go
│   │   ├── common_test.go
│   │   ├── common.go
│   │   ├── couchbase
│   │   │   ├── couchbase_test.go
│   │   │   └── couchbase.go
│   │   ├── dataform
│   │   │   └── dataformcompilelocal
│   │   │       ├── dataformcompilelocal_test.go
│   │   │       └── dataformcompilelocal.go
│   │   ├── dataplex
│   │   │   ├── dataplexlookupentry
│   │   │   │   ├── dataplexlookupentry_test.go
│   │   │   │   └── dataplexlookupentry.go
│   │   │   ├── dataplexsearchaspecttypes
│   │   │   │   ├── dataplexsearchaspecttypes_test.go
│   │   │   │   └── dataplexsearchaspecttypes.go
│   │   │   └── dataplexsearchentries
│   │   │       ├── dataplexsearchentries_test.go
│   │   │       └── dataplexsearchentries.go
│   │   ├── dgraph
│   │   │   ├── dgraph_test.go
│   │   │   └── dgraph.go
│   │   ├── elasticsearch
│   │   │   └── elasticsearchesql
│   │   │       ├── elasticsearchesql_test.go
│   │   │       └── elasticsearchesql.go
│   │   ├── firebird
│   │   │   ├── firebirdexecutesql
│   │   │   │   ├── firebirdexecutesql_test.go
│   │   │   │   └── firebirdexecutesql.go
│   │   │   └── firebirdsql
│   │   │       ├── firebirdsql_test.go
│   │   │       └── firebirdsql.go
│   │   ├── firestore
│   │   │   ├── firestoreadddocuments
│   │   │   │   ├── firestoreadddocuments_test.go
│   │   │   │   └── firestoreadddocuments.go
│   │   │   ├── firestoredeletedocuments
│   │   │   │   ├── firestoredeletedocuments_test.go
│   │   │   │   └── firestoredeletedocuments.go
│   │   │   ├── firestoregetdocuments
│   │   │   │   ├── firestoregetdocuments_test.go
│   │   │   │   └── firestoregetdocuments.go
│   │   │   ├── firestoregetrules
│   │   │   │   ├── firestoregetrules_test.go
│   │   │   │   └── firestoregetrules.go
│   │   │   ├── firestorelistcollections
│   │   │   │   ├── firestorelistcollections_test.go
│   │   │   │   └── firestorelistcollections.go
│   │   │   ├── firestorequery
│   │   │   │   ├── firestorequery_test.go
│   │   │   │   └── firestorequery.go
│   │   │   ├── firestorequerycollection
│   │   │   │   ├── firestorequerycollection_test.go
│   │   │   │   └── firestorequerycollection.go
│   │   │   ├── firestoreupdatedocument
│   │   │   │   ├── firestoreupdatedocument_test.go
│   │   │   │   └── firestoreupdatedocument.go
│   │   │   ├── firestorevalidaterules
│   │   │   │   ├── firestorevalidaterules_test.go
│   │   │   │   └── firestorevalidaterules.go
│   │   │   └── util
│   │   │       ├── converter_test.go
│   │   │       ├── converter.go
│   │   │       ├── validator_test.go
│   │   │       └── validator.go
│   │   ├── http
│   │   │   ├── http_test.go
│   │   │   └── http.go
│   │   ├── http_method.go
│   │   ├── looker
│   │   │   ├── lookeradddashboardelement
│   │   │   │   ├── lookeradddashboardelement_test.go
│   │   │   │   └── lookeradddashboardelement.go
│   │   │   ├── lookercommon
│   │   │   │   ├── lookercommon_test.go
│   │   │   │   └── lookercommon.go
│   │   │   ├── lookerconversationalanalytics
│   │   │   │   ├── lookerconversationalanalytics_test.go
│   │   │   │   └── lookerconversationalanalytics.go
│   │   │   ├── lookercreateprojectfile
│   │   │   │   ├── lookercreateprojectfile_test.go
│   │   │   │   └── lookercreateprojectfile.go
│   │   │   ├── lookerdeleteprojectfile
│   │   │   │   ├── lookerdeleteprojectfile_test.go
│   │   │   │   └── lookerdeleteprojectfile.go
│   │   │   ├── lookerdevmode
│   │   │   │   ├── lookerdevmode_test.go
│   │   │   │   └── lookerdevmode.go
│   │   │   ├── lookergetconnectiondatabases
│   │   │   │   ├── lookergetconnectiondatabases_test.go
│   │   │   │   └── lookergetconnectiondatabases.go
│   │   │   ├── lookergetconnections
│   │   │   │   ├── lookergetconnections_test.go
│   │   │   │   └── lookergetconnections.go
│   │   │   ├── lookergetconnectionschemas
│   │   │   │   ├── lookergetconnectionschemas_test.go
│   │   │   │   └── lookergetconnectionschemas.go
│   │   │   ├── lookergetconnectiontablecolumns
│   │   │   │   ├── lookergetconnectiontablecolumns_test.go
│   │   │   │   └── lookergetconnectiontablecolumns.go
│   │   │   ├── lookergetconnectiontables
│   │   │   │   ├── lookergetconnectiontables_test.go
│   │   │   │   └── lookergetconnectiontables.go
│   │   │   ├── lookergetdashboards
│   │   │   │   ├── lookergetdashboards_test.go
│   │   │   │   └── lookergetdashboards.go
│   │   │   ├── lookergetdimensions
│   │   │   │   ├── lookergetdimensions_test.go
│   │   │   │   └── lookergetdimensions.go
│   │   │   ├── lookergetexplores
│   │   │   │   ├── lookergetexplores_test.go
│   │   │   │   └── lookergetexplores.go
│   │   │   ├── lookergetfilters
│   │   │   │   ├── lookergetfilters_test.go
│   │   │   │   └── lookergetfilters.go
│   │   │   ├── lookergetlooks
│   │   │   │   ├── lookergetlooks_test.go
│   │   │   │   └── lookergetlooks.go
│   │   │   ├── lookergetmeasures
│   │   │   │   ├── lookergetmeasures_test.go
│   │   │   │   └── lookergetmeasures.go
│   │   │   ├── lookergetmodels
│   │   │   │   ├── lookergetmodels_test.go
│   │   │   │   └── lookergetmodels.go
│   │   │   ├── lookergetparameters
│   │   │   │   ├── lookergetparameters_test.go
│   │   │   │   └── lookergetparameters.go
│   │   │   ├── lookergetprojectfile
│   │   │   │   ├── lookergetprojectfile_test.go
│   │   │   │   └── lookergetprojectfile.go
│   │   │   ├── lookergetprojectfiles
│   │   │   │   ├── lookergetprojectfiles_test.go
│   │   │   │   └── lookergetprojectfiles.go
│   │   │   ├── lookergetprojects
│   │   │   │   ├── lookergetprojects_test.go
│   │   │   │   └── lookergetprojects.go
│   │   │   ├── lookerhealthanalyze
│   │   │   │   ├── lookerhealthanalyze_test.go
│   │   │   │   └── lookerhealthanalyze.go
│   │   │   ├── lookerhealthpulse
│   │   │   │   ├── lookerhealthpulse_test.go
│   │   │   │   └── lookerhealthpulse.go
│   │   │   ├── lookerhealthvacuum
│   │   │   │   ├── lookerhealthvacuum_test.go
│   │   │   │   └── lookerhealthvacuum.go
│   │   │   ├── lookermakedashboard
│   │   │   │   ├── lookermakedashboard_test.go
│   │   │   │   └── lookermakedashboard.go
│   │   │   ├── lookermakelook
│   │   │   │   ├── lookermakelook_test.go
│   │   │   │   └── lookermakelook.go
│   │   │   ├── lookerquery
│   │   │   │   ├── lookerquery_test.go
│   │   │   │   └── lookerquery.go
│   │   │   ├── lookerquerysql
│   │   │   │   ├── lookerquerysql_test.go
│   │   │   │   └── lookerquerysql.go
│   │   │   ├── lookerqueryurl
│   │   │   │   ├── lookerqueryurl_test.go
│   │   │   │   └── lookerqueryurl.go
│   │   │   ├── lookerrunlook
│   │   │   │   ├── lookerrunlook_test.go
│   │   │   │   └── lookerrunlook.go
│   │   │   └── lookerupdateprojectfile
│   │   │       ├── lookerupdateprojectfile_test.go
│   │   │       └── lookerupdateprojectfile.go
│   │   ├── mindsdb
│   │   │   ├── mindsdbexecutesql
│   │   │   │   ├── mindsdbexecutesql_test.go
│   │   │   │   └── mindsdbexecutesql.go
│   │   │   └── mindsdbsql
│   │   │       ├── mindsdbsql_test.go
│   │   │       └── mindsdbsql.go
│   │   ├── mongodb
│   │   │   ├── mongodbaggregate
│   │   │   │   ├── mongodbaggregate_test.go
│   │   │   │   └── mongodbaggregate.go
│   │   │   ├── mongodbdeletemany
│   │   │   │   ├── mongodbdeletemany_test.go
│   │   │   │   └── mongodbdeletemany.go
│   │   │   ├── mongodbdeleteone
│   │   │   │   ├── mongodbdeleteone_test.go
│   │   │   │   └── mongodbdeleteone.go
│   │   │   ├── mongodbfind
│   │   │   │   ├── mongodbfind_test.go
│   │   │   │   └── mongodbfind.go
│   │   │   ├── mongodbfindone
│   │   │   │   ├── mongodbfindone_test.go
│   │   │   │   └── mongodbfindone.go
│   │   │   ├── mongodbinsertmany
│   │   │   │   ├── mongodbinsertmany_test.go
│   │   │   │   └── mongodbinsertmany.go
│   │   │   ├── mongodbinsertone
│   │   │   │   ├── mongodbinsertone_test.go
│   │   │   │   └── mongodbinsertone.go
│   │   │   ├── mongodbupdatemany
│   │   │   │   ├── mongodbupdatemany_test.go
│   │   │   │   └── mongodbupdatemany.go
│   │   │   └── mongodbupdateone
│   │   │       ├── mongodbupdateone_test.go
│   │   │       └── mongodbupdateone.go
│   │   ├── mssql
│   │   │   ├── mssqlexecutesql
│   │   │   │   ├── mssqlexecutesql_test.go
│   │   │   │   └── mssqlexecutesql.go
│   │   │   ├── mssqllisttables
│   │   │   │   ├── mssqllisttables_test.go
│   │   │   │   └── mssqllisttables.go
│   │   │   └── mssqlsql
│   │   │       ├── mssqlsql_test.go
│   │   │       └── mssqlsql.go
│   │   ├── mysql
│   │   │   ├── mysqlcommon
│   │   │   │   └── mysqlcommon.go
│   │   │   ├── mysqlexecutesql
│   │   │   │   ├── mysqlexecutesql_test.go
│   │   │   │   └── mysqlexecutesql.go
│   │   │   ├── mysqllistactivequeries
│   │   │   │   ├── mysqllistactivequeries_test.go
│   │   │   │   └── mysqllistactivequeries.go
│   │   │   ├── mysqllisttablefragmentation
│   │   │   │   ├── mysqllisttablefragmentation_test.go
│   │   │   │   └── mysqllisttablefragmentation.go
│   │   │   ├── mysqllisttables
│   │   │   │   ├── mysqllisttables_test.go
│   │   │   │   └── mysqllisttables.go
│   │   │   ├── mysqllisttablesmissinguniqueindexes
│   │   │   │   ├── mysqllisttablesmissinguniqueindexes_test.go
│   │   │   │   └── mysqllisttablesmissinguniqueindexes.go
│   │   │   └── mysqlsql
│   │   │       ├── mysqlsql_test.go
│   │   │       └── mysqlsql.go
│   │   ├── neo4j
│   │   │   ├── neo4jcypher
│   │   │   │   ├── neo4jcypher_test.go
│   │   │   │   └── neo4jcypher.go
│   │   │   ├── neo4jexecutecypher
│   │   │   │   ├── classifier
│   │   │   │   │   ├── classifier_test.go
│   │   │   │   │   └── classifier.go
│   │   │   │   ├── neo4jexecutecypher_test.go
│   │   │   │   └── neo4jexecutecypher.go
│   │   │   └── neo4jschema
│   │   │       ├── cache
│   │   │       │   ├── cache_test.go
│   │   │       │   └── cache.go
│   │   │       ├── helpers
│   │   │       │   ├── helpers_test.go
│   │   │       │   └── helpers.go
│   │   │       ├── neo4jschema_test.go
│   │   │       ├── neo4jschema.go
│   │   │       └── types
│   │   │           └── types.go
│   │   ├── oceanbase
│   │   │   ├── oceanbaseexecutesql
│   │   │   │   ├── oceanbaseexecutesql_test.go
│   │   │   │   └── oceanbaseexecutesql.go
│   │   │   └── oceanbasesql
│   │   │       ├── oceanbasesql_test.go
│   │   │       └── oceanbasesql.go
│   │   ├── oracle
│   │   │   ├── oracleexecutesql
│   │   │   │   └── oracleexecutesql.go
│   │   │   └── oraclesql
│   │   │       └── oraclesql.go
│   │   ├── parameters_test.go
│   │   ├── parameters.go
│   │   ├── postgres
│   │   │   ├── postgresexecutesql
│   │   │   │   ├── postgresexecutesql_test.go
│   │   │   │   └── postgresexecutesql.go
│   │   │   ├── postgreslistactivequeries
│   │   │   │   ├── postgreslistactivequeries_test.go
│   │   │   │   └── postgreslistactivequeries.go
│   │   │   ├── postgreslistavailableextensions
│   │   │   │   ├── postgreslistavailableextensions_test.go
│   │   │   │   └── postgreslistavailableextensions.go
│   │   │   ├── postgreslistinstalledextensions
│   │   │   │   ├── postgreslistinstalledextensions_test.go
│   │   │   │   └── postgreslistinstalledextensions.go
│   │   │   ├── postgreslistschemas
│   │   │   │   ├── postgreslistschemas_test.go
│   │   │   │   └── postgreslistschemas.go
│   │   │   ├── postgreslisttables
│   │   │   │   ├── postgreslisttables_test.go
│   │   │   │   └── postgreslisttables.go
│   │   │   ├── postgreslistviews
│   │   │   │   ├── postgreslistviews_test.go
│   │   │   │   └── postgreslistviews.go
│   │   │   └── postgressql
│   │   │       ├── postgressql_test.go
│   │   │       └── postgressql.go
│   │   ├── redis
│   │   │   ├── redis_test.go
│   │   │   └── redis.go
│   │   ├── serverlessspark
│   │   │   ├── serverlesssparkcancelbatch
│   │   │   │   ├── serverlesssparkcancelbatch_test.go
│   │   │   │   └── serverlesssparkcancelbatch.go
│   │   │   ├── serverlesssparkgetbatch
│   │   │   │   ├── serverlesssparkgetbatch_test.go
│   │   │   │   └── serverlesssparkgetbatch.go
│   │   │   └── serverlesssparklistbatches
│   │   │       ├── serverlesssparklistbatches_test.go
│   │   │       └── serverlesssparklistbatches.go
│   │   ├── spanner
│   │   │   ├── spannerexecutesql
│   │   │   │   ├── spannerexecutesql_test.go
│   │   │   │   └── spannerexecutesql.go
│   │   │   ├── spannerlisttables
│   │   │   │   ├── spannerlisttables_test.go
│   │   │   │   └── spannerlisttables.go
│   │   │   └── spannersql
│   │   │       ├── spanner_test.go
│   │   │       └── spannersql.go
│   │   ├── sqlite
│   │   │   ├── sqliteexecutesql
│   │   │   │   ├── sqliteexecutesql_test.go
│   │   │   │   └── sqliteexecutesql.go
│   │   │   └── sqlitesql
│   │   │       ├── sqlitesql_test.go
│   │   │       └── sqlitesql.go
│   │   ├── tidb
│   │   │   ├── tidbexecutesql
│   │   │   │   ├── tidbexecutesql_test.go
│   │   │   │   └── tidbexecutesql.go
│   │   │   └── tidbsql
│   │   │       ├── tidbsql_test.go
│   │   │       └── tidbsql.go
│   │   ├── tools_test.go
│   │   ├── tools.go
│   │   ├── toolsets.go
│   │   ├── trino
│   │   │   ├── trinoexecutesql
│   │   │   │   ├── trinoexecutesql_test.go
│   │   │   │   └── trinoexecutesql.go
│   │   │   └── trinosql
│   │   │       ├── trinosql_test.go
│   │   │       └── trinosql.go
│   │   ├── utility
│   │   │   └── wait
│   │   │       ├── wait_test.go
│   │   │       └── wait.go
│   │   ├── valkey
│   │   │   ├── valkey_test.go
│   │   │   └── valkey.go
│   │   └── yugabytedbsql
│   │       ├── yugabytedbsql_test.go
│   │       └── yugabytedbsql.go
│   └── util
│       ├── orderedmap
│       │   ├── orderedmap_test.go
│       │   └── orderedmap.go
│       └── util.go
├── LICENSE
├── logo.png
├── main.go
├── MCP-TOOLBOX-EXTENSION.md
├── README.md
└── tests
    ├── alloydb
    │   ├── alloydb_integration_test.go
    │   └── alloydb_wait_for_operation_test.go
    ├── alloydbainl
    │   └── alloydb_ai_nl_integration_test.go
    ├── alloydbpg
    │   └── alloydb_pg_integration_test.go
    ├── auth.go
    ├── bigquery
    │   └── bigquery_integration_test.go
    ├── bigtable
    │   └── bigtable_integration_test.go
    ├── cassandra
    │   └── cassandra_integration_test.go
    ├── clickhouse
    │   └── clickhouse_integration_test.go
    ├── cloudmonitoring
    │   └── cloud_monitoring_integration_test.go
    ├── cloudsql
    │   ├── cloud_sql_create_database_test.go
    │   ├── cloud_sql_create_users_test.go
    │   ├── cloud_sql_get_instances_test.go
    │   ├── cloud_sql_list_databases_test.go
    │   ├── cloudsql_list_instances_test.go
    │   └── cloudsql_wait_for_operation_test.go
    ├── cloudsqlmssql
    │   ├── cloud_sql_mssql_create_instance_integration_test.go
    │   └── cloud_sql_mssql_integration_test.go
    ├── cloudsqlmysql
    │   ├── cloud_sql_mysql_create_instance_integration_test.go
    │   └── cloud_sql_mysql_integration_test.go
    ├── cloudsqlpg
    │   ├── cloud_sql_pg_create_instances_test.go
    │   └── cloud_sql_pg_integration_test.go
    ├── common.go
    ├── couchbase
    │   └── couchbase_integration_test.go
    ├── dataform
    │   └── dataform_integration_test.go
    ├── dataplex
    │   └── dataplex_integration_test.go
    ├── dgraph
    │   └── dgraph_integration_test.go
    ├── elasticsearch
    │   └── elasticsearch_integration_test.go
    ├── firebird
    │   └── firebird_integration_test.go
    ├── firestore
    │   └── firestore_integration_test.go
    ├── http
    │   └── http_integration_test.go
    ├── looker
    │   └── looker_integration_test.go
    ├── mindsdb
    │   └── mindsdb_integration_test.go
    ├── mongodb
    │   └── mongodb_integration_test.go
    ├── mssql
    │   └── mssql_integration_test.go
    ├── mysql
    │   └── mysql_integration_test.go
    ├── neo4j
    │   └── neo4j_integration_test.go
    ├── oceanbase
    │   └── oceanbase_integration_test.go
    ├── option.go
    ├── oracle
    │   └── oracle_integration_test.go
    ├── postgres
    │   └── postgres_integration_test.go
    ├── redis
    │   └── redis_test.go
    ├── server.go
    ├── serverlessspark
    │   └── serverless_spark_integration_test.go
    ├── source.go
    ├── spanner
    │   └── spanner_integration_test.go
    ├── sqlite
    │   └── sqlite_integration_test.go
    ├── tidb
    │   └── tidb_integration_test.go
    ├── tool.go
    ├── trino
    │   └── trino_integration_test.go
    ├── utility
    │   └── wait_integration_test.go
    ├── valkey
    │   └── valkey_test.go
    └── yugabytedb
        └── yugabytedb_integration_test.go
```

# Files

--------------------------------------------------------------------------------
/docs/en/reference/prebuilt-tools.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: "Prebuilt Tools"
  3 | type: docs
  4 | weight: 1
  5 | description: >
  6 |     This page lists all the prebuilt tools available.
  7 | ---
  8 | 
  9 | Prebuilt tools are reusable, pre-packaged toolsets that are designed to extend
 10 | the capabilities of agents. These tools are built to be generic and adaptable,
 11 | allowing developers to interact with and take action on databases.
 12 | 
 13 | See guides, [Connect from your IDE](../how-to/connect-ide/_index.md), for
 14 | details on how to connect your AI tools (IDEs) to databases via Toolbox and MCP.
 15 | 
 16 | ## AlloyDB Postgres
 17 | 
 18 | *   `--prebuilt` value: `alloydb-postgres`
 19 | *   **Environment Variables:**
 20 |     *   `ALLOYDB_POSTGRES_PROJECT`: The GCP project ID.
 21 |     *   `ALLOYDB_POSTGRES_REGION`: The region of your AlloyDB instance.
 22 |     *   `ALLOYDB_POSTGRES_CLUSTER`: The ID of your AlloyDB cluster.
 23 |     *   `ALLOYDB_POSTGRES_INSTANCE`: The ID of your AlloyDB instance.
 24 |     *   `ALLOYDB_POSTGRES_DATABASE`: The name of the database to connect to.
 25 |     *   `ALLOYDB_POSTGRES_USER`: (Optional) The database username. Defaults to
 26 |         IAM authentication if unspecified.
 27 |     *   `ALLOYDB_POSTGRES_PASSWORD`: (Optional) The password for the database
 28 |         user. Defaults to IAM authentication if unspecified.
 29 |     *   `ALLOYDB_POSTGRES_IP_TYPE`: (Optional) The IP type i.e. "Public" or
 30 |         "Private" (Default: Public).
 31 | *   **Permissions:**
 32 |     *   **AlloyDB Client** (`roles/alloydb.client`) to connect to the instance.
 33 |     *   Database-level permissions (e.g., `SELECT`, `INSERT`) are required to
 34 |         execute queries.
 35 | *   **Tools:**
 36 |     *   `execute_sql`: Executes a SQL query.
 37 |     *   `list_tables`: Lists tables in the database.
 38 |     *   `list_autovacuum_configurations`: Lists autovacuum configurations in the
 39 |         database.
 40 |     *   `list_memory_configurations`: Lists memory-related configurations in the
 41 |         database.
 42 |     *   `list_top_bloated_tables`: List top bloated tables in the database.
 43 |     *   `list_replication_slots`: Lists replication slots in the database.
 44 |     *   `list_invalid_indexes`: Lists invalid indexes in the database.
 45 |     *   `get_query_plan`: Generate the execution plan of a statement.
 46 |     *   `list_views`: Lists views in the database from pg_views with a default
 47 |         limit of 50 rows. Returns schemaname, viewname and the ownername.
 48 |     *   `list_schemas`: Lists schemas in the database. 
 49 | 
 50 | ## AlloyDB Postgres Admin
 51 | 
 52 | * `--prebuilt` value: `alloydb-postgres-admin`
 53 | *   **Permissions:**
 54 |     *   **AlloyDB Viewer** (`roles/alloydb.viewer`) is required for `list` and
 55 |         `get` tools.
 56 |     *   **AlloyDB Admin** (`roles/alloydb.admin`) is required for `create` tools.
 57 | *   **Tools:**
 58 |     *   `create_cluster`: Creates a new AlloyDB cluster.
 59 |     *   `list_clusters`: Lists all AlloyDB clusters in a project.
 60 |     *   `get_cluster`: Gets information about a specified AlloyDB cluster.
 61 |     *   `create_instance`: Creates a new AlloyDB instance within a cluster.
 62 |     *   `list_instances`: Lists all instances within an AlloyDB cluster.
 63 |     *   `get_instance`: Gets information about a specified AlloyDB instance.
 64 |     *   `create_user`: Creates a new database user in an AlloyDB cluster.
 65 |     *   `list_users`: Lists all database users within an AlloyDB cluster.
 66 |     *   `get_user`: Gets information about a specified database user in an
 67 |         AlloyDB cluster.
 68 |     *   `wait_for_operation`: Polls the operations API to track the status of
 69 |         long-running operations.
 70 | 
 71 | ## AlloyDB Postgres Observability
 72 | 
 73 | *   `--prebuilt` value: `alloydb-postgres-observability`
 74 | *   **Permissions:**
 75 |     *   **Monitoring Viewer** (`roles/monitoring.viewer`) is required on the
 76 |         project to view monitoring data.
 77 | *   **Tools:**
 78 |     *   `get_system_metrics`: Fetches system level cloud monitoring data
 79 |         (timeseries metrics) for an AlloyDB instance using a PromQL query.
 80 |     *   `get_query_metrics`: Fetches query level cloud monitoring data
 81 |         (timeseries metrics) for queries running in an AlloyDB instance using a
 82 |         PromQL query.
 83 | 
 84 | ## BigQuery
 85 | 
 86 | *   `--prebuilt` value: `bigquery`
 87 | *   **Environment Variables:**
 88 |     *   `BIGQUERY_PROJECT`: The GCP project ID.
 89 |     *   `BIGQUERY_LOCATION`: (Optional) The dataset location.
 90 |     *   `BIGQUERY_USE_CLIENT_OAUTH`: (Optional) If `true`, forwards the client's
 91 |         OAuth access token for authentication. Defaults to `false`.
 92 | *   **Permissions:**
 93 |     *   **BigQuery User** (`roles/bigquery.user`) to execute queries and view
 94 |         metadata.
 95 |     *   **BigQuery Metadata Viewer** (`roles/bigquery.metadataViewer`) to view
 96 |         all datasets.
 97 |     *   **BigQuery Data Editor** (`roles/bigquery.dataEditor`) to create or
 98 |         modify datasets and tables.
 99 |     *   **Gemini for Google Cloud** (`roles/cloudaicompanion.user`) to use the
100 |         conversational analytics API.
101 | *   **Tools:**
102 |     *   `analyze_contribution`: Use this tool to perform contribution analysis,
103 |         also called key driver analysis.
104 |     *   `ask_data_insights`: Use this tool to perform data analysis, get
105 |         insights, or answer complex questions about the contents of specific
106 |         BigQuery tables. For more information on required roles, API setup, and
107 |         IAM configuration, see the setup and authentication section of the
108 |         [Conversational Analytics API
109 |         documentation](https://cloud.google.com/gemini/docs/conversational-analytics-api/overview).
110 |     *   `execute_sql`: Executes a SQL statement.
111 |     *   `forecast`: Use this tool to forecast time series data.
112 |     *   `get_dataset_info`: Gets dataset metadata.
113 |     *   `get_table_info`: Gets table metadata.
114 |     *   `list_dataset_ids`: Lists datasets.
115 |     *   `list_table_ids`: Lists tables.
116 |     *   `search_catalog`: Search for entries based on the provided query.
117 | 
118 | ## Cloud SQL for MySQL
119 | 
120 | *   `--prebuilt` value: `cloud-sql-mysql`
121 | *   **Environment Variables:**
122 |     *   `CLOUD_SQL_MYSQL_PROJECT`: The GCP project ID.
123 |     *   `CLOUD_SQL_MYSQL_REGION`: The region of your Cloud SQL instance.
124 |     *   `CLOUD_SQL_MYSQL_INSTANCE`: The ID of your Cloud SQL instance.
125 |     *   `CLOUD_SQL_MYSQL_DATABASE`: The name of the database to connect to.
126 |     *   `CLOUD_SQL_MYSQL_USER`: The database username.
127 |     *   `CLOUD_SQL_MYSQL_PASSWORD`: The password for the database user.
128 |     *   `CLOUD_SQL_MYSQL_IP_TYPE`: The IP type i.e. "Public
129 |      or "Private" (Default: Public).
130 | *   **Permissions:**
131 |     *   **Cloud SQL Client** (`roles/cloudsql.client`) to connect to the
132 |         instance.
133 |     *   Database-level permissions (e.g., `SELECT`, `INSERT`) are required to
134 |         execute queries.
135 | *   **Tools:**
136 |     *   `execute_sql`: Executes a SQL query.
137 |     *   `list_tables`: Lists tables in the database.
138 |     *   `get_query_plan`: Provides information about how MySQL executes a SQL
139 |         statement.
140 |     *   `list_active_queries`: Lists ongoing queries.
141 |     *   `list_tables_missing_unique_indexes`: Looks for tables that do not have
142 |         primary or unique key contraint.
143 |     *   `list_table_fragmentation`: Displays table fragmentation in MySQL.
144 | 
145 | ## Cloud SQL for MySQL Observability
146 | 
147 | *   `--prebuilt` value: `cloud-sql-mysql-observability`
148 | *   **Permissions:**
149 |     *   **Monitoring Viewer** (`roles/monitoring.viewer`) is required on the
150 |         project to view monitoring data.
151 | *   **Tools:**
152 |     *   `get_system_metrics`: Fetches system level cloud monitoring data
153 |         (timeseries metrics) for a MySQL instance using a PromQL query.
154 |     *   `get_query_metrics`: Fetches query level cloud monitoring data
155 |         (timeseries metrics) for queries running in a MySQL instance using a
156 |         PromQL query.
157 | 
158 | ## Cloud SQL for MySQL Admin
159 | 
160 | *   `--prebuilt` value: `cloud-sql-mysql-admin`
161 | *   **Permissions:**
162 |     *   **Cloud SQL Viewer** (`roles/cloudsql.viewer`): Provides read-only
163 |         access to resources.
164 |         * `get_instance`
165 |         * `list_instances`
166 |         * `list_databases`
167 |         * `wait_for_operation`
168 |     *   **Cloud SQL Editor** (`roles/cloudsql.editor`): Provides permissions to
169 |         manage existing resources.
170 |         * All `viewer` tools
171 |         * `create_database`
172 |     *   **Cloud SQL Admin** (`roles/cloudsql.admin`): Provides full control over
173 |         all resources.
174 |         * All `editor` and `viewer` tools
175 |         * `create_instance`
176 |         * `create_user`
177 | *   **Tools:**
178 |     *   `create_instance`: Creates a new Cloud SQL for MySQL instance.
179 |     *   `get_instance`: Gets information about a Cloud SQL instance.
180 |     *   `list_instances`: Lists Cloud SQL instances in a project.
181 |     *   `create_database`: Creates a new database in a Cloud SQL instance.
182 |     *   `list_databases`: Lists all databases for a Cloud SQL instance.
183 |     *   `create_user`: Creates a new user in a Cloud SQL instance.
184 |     *   `wait_for_operation`: Waits for a Cloud SQL operation to complete.
185 | 
186 | ## Cloud SQL for PostgreSQL
187 | 
188 | *   `--prebuilt` value: `cloud-sql-postgres`
189 | *   **Environment Variables:**
190 |     *   `CLOUD_SQL_POSTGRES_PROJECT`: The GCP project ID.
191 |     *   `CLOUD_SQL_POSTGRES_REGION`: The region of your Cloud SQL instance.
192 |     *   `CLOUD_SQL_POSTGRES_INSTANCE`: The ID of your Cloud SQL instance.
193 |     *   `CLOUD_SQL_POSTGRES_DATABASE`: The name of the database to connect to.
194 |     *   `CLOUD_SQL_POSTGRES_USER`: (Optional) The database username. Defaults to
195 |         IAM authentication if unspecified.
196 |     *   `CLOUD_SQL_POSTGRES_PASSWORD`: (Optional) The password for the database
197 |         user. Defaults to IAM authentication if unspecified.
198 |     *   `CLOUD_SQL_POSTGRES_IP_TYPE`: (Optional) The IP type i.e. "Public" or
199 |         "Private" (Default: Public).
200 | *   **Permissions:**
201 |     *   **Cloud SQL Client** (`roles/cloudsql.client`) to connect to the
202 |         instance.
203 |     *   Database-level permissions (e.g., `SELECT`, `INSERT`) are required to
204 |         execute queries.
205 | *   **Tools:**
206 |     *   `execute_sql`: Executes a SQL query.
207 |     *   `list_tables`: Lists tables in the database.
208 |     *   `list_autovacuum_configurations`: Lists autovacuum configurations in the
209 |         database.
210 |     *   `list_memory_configurations`: Lists memory-related configurations in the
211 |         database.
212 |     *   `list_top_bloated_tables`: List top bloated tables in the database.
213 |     *   `list_replication_slots`: Lists replication slots in the database.
214 |     *   `list_invalid_indexes`: Lists invalid indexes in the database.
215 |     *   `get_query_plan`: Generate the execution plan of a statement.
216 |     *   `list_views`: Lists views in the database from pg_views with a default
217 |         limit of 50 rows. Returns schemaname, viewname and the ownername.
218 |     *   `list_schemas`: Lists schemas in the database. 
219 | 
220 | ## Cloud SQL for PostgreSQL Observability
221 | 
222 | *   `--prebuilt` value: `cloud-sql-postgres-observability`
223 | *   **Permissions:**
224 |     *   **Monitoring Viewer** (`roles/monitoring.viewer`) is required on the
225 |         project to view monitoring data.
226 | *   **Tools:**
227 |     *   `get_system_metrics`: Fetches system level cloud monitoring data
228 |         (timeseries metrics) for a Postgres instance using a PromQL query.
229 |     *   `get_query_metrics`: Fetches query level cloud monitoring data
230 |         (timeseries metrics) for queries running in Postgres instance using a
231 |         PromQL query.
232 | 
233 | ## Cloud SQL for PostgreSQL Admin
234 | 
235 | *   `--prebuilt` value: `cloud-sql-postgres-admin`
236 | *   **Permissions:**
237 |     *   **Cloud SQL Viewer** (`roles/cloudsql.viewer`): Provides read-only
238 |         access to resources.
239 |         * `get_instance`
240 |         * `list_instances`
241 |         * `list_databases`
242 |         * `wait_for_operation`
243 |     *   **Cloud SQL Editor** (`roles/cloudsql.editor`): Provides permissions to
244 |         manage existing resources.
245 |         * All `viewer` tools
246 |         * `create_database`
247 |     *   **Cloud SQL Admin** (`roles/cloudsql.admin`): Provides full control over
248 |         all resources.
249 |         * All `editor` and `viewer` tools
250 |         * `create_instance`
251 |         * `create_user`
252 | *   **Tools:**
253 |     *   `create_instance`: Creates a new Cloud SQL for PostgreSQL instance.
254 |     *   `get_instance`: Gets information about a Cloud SQL instance.
255 |     *   `list_instances`: Lists Cloud SQL instances in a project.
256 |     *   `create_database`: Creates a new database in a Cloud SQL instance.
257 |     *   `list_databases`: Lists all databases for a Cloud SQL instance.
258 |     *   `create_user`: Creates a new user in a Cloud SQL instance.
259 |     *   `wait_for_operation`: Waits for a Cloud SQL operation to complete.
260 | 
261 | ## Cloud SQL for SQL Server
262 | 
263 | *   `--prebuilt` value: `cloud-sql-mssql`
264 | *   **Environment Variables:**
265 |     *   `CLOUD_SQL_MSSQL_PROJECT`: The GCP project ID.
266 |     *   `CLOUD_SQL_MSSQL_REGION`: The region of your Cloud SQL instance.
267 |     *   `CLOUD_SQL_MSSQL_INSTANCE`: The ID of your Cloud SQL instance.
268 |     *   `CLOUD_SQL_MSSQL_DATABASE`: The name of the database to connect to.
269 |     *   `CLOUD_SQL_MSSQL_USER`: The database username.
270 |     *   `CLOUD_SQL_MSSQL_PASSWORD`: The password for the database user.
271 |     *   `CLOUD_SQL_MSSQL_IP_TYPE`: (Optional) The IP type i.e. "Public" or
272 |         "Private" (Default: Public).
273 | *   **Permissions:**
274 |     *   **Cloud SQL Client** (`roles/cloudsql.client`) to connect to the
275 |         instance.
276 |     *   Database-level permissions (e.g., `SELECT`, `INSERT`) are required to
277 |         execute queries.
278 | *   **Tools:**
279 |     *   `execute_sql`: Executes a SQL query.
280 |     *   `list_tables`: Lists tables in the database.
281 | 
282 | ## Cloud SQL for SQL Server Observability
283 | 
284 | *   `--prebuilt` value: `cloud-sql-mssql-observability`
285 | *   **Permissions:**
286 |     *   **Monitoring Viewer** (`roles/monitoring.viewer`) is required on the
287 |         project to view monitoring data.
288 | *   **Tools:**
289 |     *   `get_system_metrics`: Fetches system level cloud monitoring data
290 |         (timeseries metrics) for a SQL Server instance using a PromQL query.
291 | 
292 | ## Cloud SQL for SQL Server Admin
293 | 
294 | *   `--prebuilt` value: `cloud-sql-mssql-admin`
295 | *   **Permissions:**
296 |     *   **Cloud SQL Viewer** (`roles/cloudsql.viewer`): Provides read-only
297 |         access to resources.
298 |         * `get_instance`
299 |         * `list_instances`
300 |         * `list_databases`
301 |         * `wait_for_operation`
302 |     *   **Cloud SQL Editor** (`roles/cloudsql.editor`): Provides permissions to
303 |         manage existing resources.
304 |         * All `viewer` tools
305 |         * `create_database`
306 |     *   **Cloud SQL Admin** (`roles/cloudsql.admin`): Provides full control over
307 |         all resources.
308 |         * All `editor` and `viewer` tools
309 |         * `create_instance`
310 |         * `create_user`
311 | *   **Tools:**
312 |     *   `create_instance`: Creates a new Cloud SQL for SQL Server instance.
313 |     *   `get_instance`: Gets information about a Cloud SQL instance.
314 |     *   `list_instances`: Lists Cloud SQL instances in a project.
315 |     *   `create_database`: Creates a new database in a Cloud SQL instance.
316 |     *   `list_databases`: Lists all databases for a Cloud SQL instance.
317 |     *   `create_user`: Creates a new user in a Cloud SQL instance.
318 |     *   `wait_for_operation`: Waits for a Cloud SQL operation to complete.
319 | 
320 | ## Dataplex
321 | 
322 | *   `--prebuilt` value: `dataplex`
323 | *   **Environment Variables:**
324 |     *   `DATAPLEX_PROJECT`: The GCP project ID.
325 | *   **Permissions:**
326 |     *   **Dataplex Reader** (`roles/dataplex.viewer`) to search and look up
327 |         entries.
328 |     *   **Dataplex Editor** (`roles/dataplex.editor`) to modify entries.
329 | *   **Tools:**
330 |     *   `dataplex_search_entries`: Searches for entries in Dataplex Catalog.
331 |     *   `dataplex_lookup_entry`: Retrieves a specific entry from Dataplex
332 |         Catalog.
333 |     *   `dataplex_search_aspect_types`: Finds aspect types relevant to the
334 |         query.
335 | 
336 | ## Firestore
337 | 
338 | *   `--prebuilt` value: `firestore`
339 | *   **Environment Variables:**
340 |     *   `FIRESTORE_PROJECT`: The GCP project ID.
341 |     *   `FIRESTORE_DATABASE`: (Optional) The Firestore database ID. Defaults to
342 |         "(default)".
343 | *   **Permissions:**
344 |     *   **Cloud Datastore User** (`roles/datastore.user`) to get documents, list
345 |         collections, and query collections.
346 |     *   **Firebase Rules Viewer** (`roles/firebaserules.viewer`) to get and
347 |         validate Firestore rules.
348 | *   **Tools:**
349 |     *   `get_documents`: Gets multiple documents from Firestore by their paths.
350 |     *   `add_documents`: Adds a new document to a Firestore collection.
351 |     *   `update_document`: Updates an existing document in Firestore.
352 |     *   `list_collections`: Lists Firestore collections for a given parent path.
353 |     *   `delete_documents`: Deletes multiple documents from Firestore.
354 |     *   `query_collection`: Retrieves one or more Firestore documents from a
355 |         collection.
356 |     *   `get_rules`: Retrieves the active Firestore security rules.
357 |     *   `validate_rules`: Checks the provided Firestore Rules source for syntax
358 |         and validation errors.
359 | 
360 | ## Looker
361 | 
362 | *   `--prebuilt` value: `looker`
363 | *   **Environment Variables:**
364 |     *   `LOOKER_BASE_URL`: The URL of your Looker instance.
365 |     *   `LOOKER_CLIENT_ID`: The client ID for the Looker API.
366 |     *   `LOOKER_CLIENT_SECRET`: The client secret for the Looker API.
367 |     *   `LOOKER_VERIFY_SSL`: Whether to verify SSL certificates.
368 |     *   `LOOKER_USE_CLIENT_OAUTH`: Whether to use OAuth for authentication.
369 |     *   `LOOKER_SHOW_HIDDEN_MODELS`: Whether to show hidden models.
370 |     *   `LOOKER_SHOW_HIDDEN_EXPLORES`: Whether to show hidden explores.
371 |     *   `LOOKER_SHOW_HIDDEN_FIELDS`: Whether to show hidden fields.
372 | *   **Permissions:**
373 |     *   A Looker account with permissions to access the desired models,
374 |         explores, and data is required.
375 | *   **Tools:**
376 |     *   `get_models`: Retrieves the list of LookML models.
377 |     *   `get_explores`: Retrieves the list of explores in a model.
378 |     *   `get_dimensions`: Retrieves the list of dimensions in an explore.
379 |     *   `get_measures`: Retrieves the list of measures in an explore.
380 |     *   `get_filters`: Retrieves the list of filters in an explore.
381 |     *   `get_parameters`: Retrieves the list of parameters in an explore.
382 |     *   `query`: Runs a query against the LookML model.
383 |     *   `query_sql`: Generates the SQL for a query.
384 |     *   `query_url`: Generates a URL for a query in Looker.
385 |     *   `get_looks`: Searches for saved looks.
386 |     *   `run_look`: Runs the query associated with a look.
387 |     *   `make_look`: Creates a new look.
388 |     *   `get_dashboards`: Searches for saved dashboards.
389 |     *   `make_dashboard`: Creates a new dashboard.
390 |     *   `add_dashboard_element`: Adds a tile to a dashboard.
391 |     *   `health_pulse`: Test the health of a Looker instance.
392 |     *   `health_analyze`: Analyze the LookML usage of a Looker instance.
393 |     *   `health_vacuum`: Suggest LookML elements that can be removed.
394 |     *   `dev_mode`: Activate developer mode.
395 |     *   `get_projects`: Get the LookML projects in a Looker instance.
396 |     *   `get_project_files`: List the project files in a project.
397 |     *   `get_project_file`: Get the content of a LookML file.
398 |     *   `create_project_file`: Create a new LookML file.
399 |     *   `update_project_file`: Update an existing LookML file.
400 |     *   `delete_project_file`: Delete a LookML file.
401 |     *   `get_connections`: Get the available connections in a Looker instance.
402 |     *   `get_connection_schemas`: Get the available schemas in a connection.
403 |     *   `get_connection_databases`: Get the available databases in a connection.
404 |     *   `get_connection_tables`: Get the available tables in a connection.
405 |     *   `get_connection_table_columns`: Get the available columns for a table.
406 | 
407 | ## Looker Conversational Analytics
408 | 
409 | *   `--prebuilt` value: `looker-conversational-analytics`
410 | *   **Environment Variables:**
411 |     *   `LOOKER_BASE_URL`: The URL of your Looker instance.
412 |     *   `LOOKER_CLIENT_ID`: The client ID for the Looker API.
413 |     *   `LOOKER_CLIENT_SECRET`: The client secret for the Looker API.
414 |     *   `LOOKER_VERIFY_SSL`: Whether to verify SSL certificates.
415 |     *   `LOOKER_USE_CLIENT_OAUTH`: Whether to use OAuth for authentication.
416 |     *   `LOOKER_PROJECT`: The GCP Project to use for Conversational Analytics.
417 |     *   `LOOKER_LOCATION`: The GCP Location to use for Conversational Analytics.
418 | *   **Permissions:**
419 |     *   A Looker account with permissions to access the desired models,
420 |         explores, and data is required.
421 |     *   **Looker Instance User** (`roles/looker.instanceUser`): IAM role to
422 |         access Looker.
423 |     *   **Gemini for Google Cloud User** (`roles/cloudaicompanion.user`): IAM
424 |         role to access Conversational Analytics.
425 |     *   **Gemini Data Analytics Stateless Chat User (Beta)**
426 |         (`roles/geminidataanalytics.dataAgentStatelessUser`): IAM role to
427 |         access Conversational Analytics.
428 | *   **Tools:**
429 |     *   `ask_data_insights`: Ask a question of the data.
430 |     *   `get_models`: Retrieves the list of LookML models.
431 |     *   `get_explores`: Retrieves the list of explores in a model.
432 | 
433 | ## Microsoft SQL Server
434 | 
435 | *   `--prebuilt` value: `mssql`
436 | *   **Environment Variables:**
437 |     *   `MSSQL_HOST`: The hostname or IP address of the SQL Server instance.
438 |     *   `MSSQL_PORT`: The port number for the SQL Server instance.
439 |     *   `MSSQL_DATABASE`: The name of the database to connect to.
440 |     *   `MSSQL_USER`: The database username.
441 |     *   `MSSQL_PASSWORD`: The password for the database user.
442 | *   **Permissions:**
443 |     *   Database-level permissions (e.g., `SELECT`, `INSERT`) are required to
444 |         execute queries.
445 | *   **Tools:**
446 |     *   `execute_sql`: Executes a SQL query.
447 |     *   `list_tables`: Lists tables in the database.
448 | 
449 | ## MySQL
450 | 
451 | *   `--prebuilt` value: `mysql`
452 | *   **Environment Variables:**
453 |     *   `MYSQL_HOST`: The hostname or IP address of the MySQL server.
454 |     *   `MYSQL_PORT`: The port number for the MySQL server.
455 |     *   `MYSQL_DATABASE`: The name of the database to connect to.
456 |     *   `MYSQL_USER`: The database username.
457 |     *   `MYSQL_PASSWORD`: The password for the database user.
458 | *   **Permissions:**
459 |     *   Database-level permissions (e.g., `SELECT`, `INSERT`) are required to
460 |         execute queries.
461 | *   **Tools:**
462 |     *   `execute_sql`: Executes a SQL query.
463 |     *   `list_tables`: Lists tables in the database.
464 |     *   `get_query_plan`: Provides information about how MySQL executes a SQL
465 |         statement.
466 |     *   `list_active_queries`: Lists ongoing queries.
467 |     *   `list_tables_missing_unique_indexes`: Looks for tables that do not have
468 |         primary or unique key contraint.
469 |     *   `list_table_fragmentation`: Displays table fragmentation in MySQL.
470 | 
471 | ## OceanBase
472 | 
473 | *   `--prebuilt` value: `oceanbase`
474 | *   **Environment Variables:**
475 |     *   `OCEANBASE_HOST`: The hostname or IP address of the OceanBase server.
476 |     *   `OCEANBASE_PORT`: The port number for the OceanBase server.
477 |     *   `OCEANBASE_DATABASE`: The name of the database to connect to.
478 |     *   `OCEANBASE_USER`: The database username.
479 |     *   `OCEANBASE_PASSWORD`: The password for the database user.
480 | *   **Permissions:**
481 |     *   Database-level permissions (e.g., `SELECT`, `INSERT`) are required to
482 |         execute queries.
483 | *   **Tools:**
484 |     *   `execute_sql`: Executes a SQL query.
485 |     *   `list_tables`: Lists tables in the database.
486 | 
487 | ## PostgreSQL
488 | 
489 | *   `--prebuilt` value: `postgres`
490 | *   **Environment Variables:**
491 |     *   `POSTGRES_HOST`: The hostname or IP address of the PostgreSQL server.
492 |     *   `POSTGRES_PORT`: The port number for the PostgreSQL server.
493 |     *   `POSTGRES_DATABASE`: The name of the database to connect to.
494 |     *   `POSTGRES_USER`: The database username.
495 |     *   `POSTGRES_PASSWORD`: The password for the database user.
496 |     *   `POSTGRES_QUERY_PARAMS`: (Optional) Raw query to be added to the db
497 |         connection string.
498 | *   **Permissions:**
499 |     *   Database-level permissions (e.g., `SELECT`, `INSERT`) are required to
500 |         execute queries.
501 | *   **Tools:**
502 |     *   `execute_sql`: Executes a SQL query.
503 |     *   `list_tables`: Lists tables in the database.
504 |     *   `list_autovacuum_configurations`: Lists autovacuum configurations in the
505 |         database.
506 |     *   `list_memory_configurations`: Lists memory-related configurations in the
507 |         database.
508 |     *   `list_top_bloated_tables`: List top bloated tables in the database.
509 |     *   `list_replication_slots`: Lists replication slots in the database.
510 |     *   `list_invalid_indexes`: Lists invalid indexes in the database.
511 |     *   `get_query_plan`: Generate the execution plan of a statement.
512 |     *   `list_views`: Lists views in the database from pg_views with a default
513 |         limit of 50 rows. Returns schemaname, viewname and the ownername.
514 |     *   `list_schemas`: Lists schemas in the database. 
515 | 
516 | ## Google Cloud Serverless for Apache Spark
517 | 
518 | *   `--prebuilt` value: `serverless-spark`
519 | *   **Environment Variables:**
520 |     *   `SERVERLESS_SPARK_PROJECT`: The GCP project ID
521 |     *   `SERVERLESS_SPARK_LOCATION`: The GCP Location.
522 | *   **Permissions:**
523 |     *   **Dataproc Serverless Viewer** (`roles/dataproc.serverlessViewer`) to
524 |         view serverless batches.
525 |     *   **Dataproc Serverless Editor** (`roles/dataproc.serverlessEditor`) to
526 |         view serverless batches.
527 | *   **Tools:**
528 |     *   `list_batches`: Lists Spark batches.
529 | 
530 | ## Spanner (GoogleSQL dialect)
531 | 
532 | *   `--prebuilt` value: `spanner`
533 | *   **Environment Variables:**
534 |     *   `SPANNER_PROJECT`: The GCP project ID.
535 |     *   `SPANNER_INSTANCE`: The Spanner instance ID.
536 |     *   `SPANNER_DATABASE`: The Spanner database ID.
537 | *   **Permissions:**
538 |     *   **Cloud Spanner Database Reader** (`roles/spanner.databaseReader`) to
539 |         execute DQL queries and list tables.
540 |     *   **Cloud Spanner Database User** (`roles/spanner.databaseUser`) to
541 |         execute DML queries.
542 | *   **Tools:**
543 |     *   `execute_sql`: Executes a DML SQL query.
544 |     *   `execute_sql_dql`: Executes a DQL SQL query.
545 |     *   `list_tables`: Lists tables in the database.
546 | 
547 | ## Spanner (PostgreSQL dialect)
548 | 
549 | *   `--prebuilt` value: `spanner-postgres`
550 | *   **Environment Variables:**
551 |     *   `SPANNER_PROJECT`: The GCP project ID.
552 |     *   `SPANNER_INSTANCE`: The Spanner instance ID.
553 |     *   `SPANNER_DATABASE`: The Spanner database ID.
554 | *   **Permissions:**
555 |     *   **Cloud Spanner Database Reader** (`roles/spanner.databaseReader`) to
556 |         execute DQL queries and list tables.
557 |     *   **Cloud Spanner Database User** (`roles/spanner.databaseUser`) to
558 |         execute DML queries.
559 | *   **Tools:**
560 |     *   `execute_sql`: Executes a DML SQL query using the PostgreSQL interface
561 |         for Spanner.
562 |     *   `execute_sql_dql`: Executes a DQL SQL query using the PostgreSQL
563 |         interface for Spanner.
564 |     *   `list_tables`: Lists tables in the database.
565 | 
566 | ## SQLite
567 | 
568 | *   `--prebuilt` value: `sqlite`
569 | *   **Environment Variables:**
570 |     *   `SQLITE_DATABASE`: The path to the SQLite database file (e.g.,
571 |         `./sample.db`).
572 | *   **Permissions:**
573 |     *   File system read/write permissions for the specified database file.
574 | *   **Tools:**
575 |     *   `execute_sql`: Executes a SQL query.
576 |     *   `list_tables`: Lists tables in the database.
577 | 
578 | ## Neo4j
579 | 
580 | *   `--prebuilt` value: `neo4j`
581 | *   **Environment Variables:**
582 |     *   `NEO4J_URI`: The URI of the Neo4j instance (e.g.,
583 |         `bolt://localhost:7687`).
584 |     *   `NEO4J_DATABASE`: The name of the Neo4j database to connect to.
585 |     *   `NEO4J_USERNAME`: The username for the Neo4j instance.
586 |     *   `NEO4J_PASSWORD`: The password for the Neo4j instance.
587 | *   **Permissions:**
588 |     *   **Database-level permissions** are required to execute Cypher queries.
589 | *   **Tools:**
590 |     *   `execute_cypher`: Executes a Cypher query.
591 |     *   `get_schema`: Retrieves the schema of the Neo4j database.
592 | 
```

--------------------------------------------------------------------------------
/tests/spanner/spanner_integration_test.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2024 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package spanner
 16 | 
 17 | import (
 18 | 	"bytes"
 19 | 	"context"
 20 | 	"encoding/json"
 21 | 	"fmt"
 22 | 	"io"
 23 | 	"net/http"
 24 | 	"os"
 25 | 	"regexp"
 26 | 	"strings"
 27 | 	"testing"
 28 | 	"time"
 29 | 
 30 | 	"cloud.google.com/go/spanner"
 31 | 	database "cloud.google.com/go/spanner/admin/database/apiv1"
 32 | 	"cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
 33 | 	"github.com/google/uuid"
 34 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 35 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 36 | 	"github.com/googleapis/genai-toolbox/tests"
 37 | )
 38 | 
 39 | var (
 40 | 	SpannerSourceKind = "spanner"
 41 | 	SpannerToolKind   = "spanner-sql"
 42 | 	SpannerProject    = os.Getenv("SPANNER_PROJECT")
 43 | 	SpannerDatabase   = os.Getenv("SPANNER_DATABASE")
 44 | 	SpannerInstance   = os.Getenv("SPANNER_INSTANCE")
 45 | )
 46 | 
 47 | func getSpannerVars(t *testing.T) map[string]any {
 48 | 	switch "" {
 49 | 	case SpannerProject:
 50 | 		t.Fatal("'SPANNER_PROJECT' not set")
 51 | 	case SpannerDatabase:
 52 | 		t.Fatal("'SPANNER_DATABASE' not set")
 53 | 	case SpannerInstance:
 54 | 		t.Fatal("'SPANNER_INSTANCE' not set")
 55 | 	}
 56 | 
 57 | 	return map[string]any{
 58 | 		"kind":     SpannerSourceKind,
 59 | 		"project":  SpannerProject,
 60 | 		"instance": SpannerInstance,
 61 | 		"database": SpannerDatabase,
 62 | 	}
 63 | }
 64 | 
 65 | func initSpannerClients(ctx context.Context, project, instance, dbname string) (*spanner.Client, *database.DatabaseAdminClient, error) {
 66 | 	// Configure the connection to the database
 67 | 	db := fmt.Sprintf("projects/%s/instances/%s/databases/%s", project, instance, dbname)
 68 | 
 69 | 	// Configure session pool to automatically clean inactive transactions
 70 | 	sessionPoolConfig := spanner.SessionPoolConfig{
 71 | 		TrackSessionHandles: true,
 72 | 		InactiveTransactionRemovalOptions: spanner.InactiveTransactionRemovalOptions{
 73 | 			ActionOnInactiveTransaction: spanner.WarnAndClose,
 74 | 		},
 75 | 	}
 76 | 
 77 | 	// Create Spanner client (for queries)
 78 | 	dataClient, err := spanner.NewClientWithConfig(context.Background(), db, spanner.ClientConfig{SessionPoolConfig: sessionPoolConfig})
 79 | 	if err != nil {
 80 | 		return nil, nil, fmt.Errorf("unable to create new Spanner client: %w", err)
 81 | 	}
 82 | 
 83 | 	// Create Spanner admin client (for creating databases)
 84 | 	adminClient, err := database.NewDatabaseAdminClient(ctx)
 85 | 	if err != nil {
 86 | 		return nil, nil, fmt.Errorf("unable to create new Spanner admin client: %w", err)
 87 | 	}
 88 | 
 89 | 	return dataClient, adminClient, nil
 90 | }
 91 | 
 92 | func TestSpannerToolEndpoints(t *testing.T) {
 93 | 	sourceConfig := getSpannerVars(t)
 94 | 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
 95 | 	defer cancel()
 96 | 
 97 | 	var args []string
 98 | 
 99 | 	// Create Spanner client
100 | 	dataClient, adminClient, err := initSpannerClients(ctx, SpannerProject, SpannerInstance, SpannerDatabase)
101 | 	if err != nil {
102 | 		t.Fatalf("unable to create Spanner client: %s", err)
103 | 	}
104 | 
105 | 	// create table name with UUID
106 | 	tableNameParam := "param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
107 | 	tableNameAuth := "auth_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
108 | 	tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
109 | 
110 | 	// set up data for param tool
111 | 	createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := getSpannerParamToolInfo(tableNameParam)
112 | 	dbString := fmt.Sprintf(
113 | 		"projects/%s/instances/%s/databases/%s",
114 | 		SpannerProject,
115 | 		SpannerInstance,
116 | 		SpannerDatabase,
117 | 	)
118 | 	teardownTable1 := setupSpannerTable(t, ctx, adminClient, dataClient, createParamTableStmt, insertParamTableStmt, tableNameParam, dbString, paramTestParams)
119 | 	defer teardownTable1(t)
120 | 
121 | 	// set up data for auth tool
122 | 	createAuthTableStmt, insertAuthTableStmt, authToolStmt, authTestParams := getSpannerAuthToolInfo(tableNameAuth)
123 | 	teardownTable2 := setupSpannerTable(t, ctx, adminClient, dataClient, createAuthTableStmt, insertAuthTableStmt, tableNameAuth, dbString, authTestParams)
124 | 	defer teardownTable2(t)
125 | 
126 | 	// set up data for template param tool
127 | 	createStatementTmpl := fmt.Sprintf("CREATE TABLE %s (id INT64, name STRING(MAX), age INT64) PRIMARY KEY (id)", tableNameTemplateParam)
128 | 	teardownTableTmpl := setupSpannerTable(t, ctx, adminClient, dataClient, createStatementTmpl, "", tableNameTemplateParam, dbString, nil)
129 | 	defer teardownTableTmpl(t)
130 | 
131 | 	// Write config into a file and pass it to command
132 | 	toolsFile := tests.GetToolsConfig(sourceConfig, SpannerToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
133 | 	toolsFile = addSpannerExecuteSqlConfig(t, toolsFile)
134 | 	toolsFile = addSpannerReadOnlyConfig(t, toolsFile)
135 | 	toolsFile = addTemplateParamConfig(t, toolsFile)
136 | 	toolsFile = addSpannerListTablesConfig(t, toolsFile)
137 | 
138 | 	cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
139 | 	if err != nil {
140 | 		t.Fatalf("command initialization returned an error: %s", err)
141 | 	}
142 | 	defer cleanup()
143 | 
144 | 	waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
145 | 	defer cancel()
146 | 	out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
147 | 	if err != nil {
148 | 		t.Logf("toolbox command logs: \n%s", out)
149 | 		t.Fatalf("toolbox didn't start successfully: %s", err)
150 | 	}
151 | 
152 | 	// Get configs for tests
153 | 	select1Want := "[{\"\":\"1\"}]"
154 | 	invokeParamWant := "[{\"id\":\"1\",\"name\":\"Alice\"},{\"id\":\"3\",\"name\":\"Sid\"}]"
155 | 	accessSchemaWant := "[{\"schema_name\":\"INFORMATION_SCHEMA\"}]"
156 | 	toolInvokeMyToolById4Want := `[{"id":"4","name":null}]`
157 | 	mcpMyFailToolWant := `"jsonrpc":"2.0","id":"invoke-fail-tool","result":{"content":[{"type":"text","text":"unable to execute client: unable to parse row: spanner: code = \"InvalidArgument\", desc = \"Syntax error: Unexpected identifier \\\\\\\"SELEC\\\\\\\" [at 1:1]\\\\nSELEC 1;\\\\n^\"`
158 | 	mcpMyToolId3NameAliceWant := `{"jsonrpc":"2.0","id":"my-tool","result":{"content":[{"type":"text","text":"{\"id\":\"1\",\"name\":\"Alice\"}"},{"type":"text","text":"{\"id\":\"3\",\"name\":\"Sid\"}"}]}}`
159 | 	mcpSelect1Want := `{"jsonrpc":"2.0","id":"invoke my-auth-required-tool","result":{"content":[{"type":"text","text":"{\"\":\"1\"}"}]}}`
160 | 	tmplSelectAllWwant := "[{\"age\":\"21\",\"id\":\"1\",\"name\":\"Alex\"},{\"age\":\"100\",\"id\":\"2\",\"name\":\"Alice\"}]"
161 | 	tmplSelectId1Want := "[{\"age\":\"21\",\"id\":\"1\",\"name\":\"Alex\"}]"
162 | 
163 | 	// Run tests
164 | 	tests.RunToolGetTest(t)
165 | 	tests.RunToolInvokeTest(t, select1Want,
166 | 		tests.WithMyToolId3NameAliceWant(invokeParamWant),
167 | 		tests.WithMyArrayToolWant(invokeParamWant),
168 | 		tests.WithMyToolById4Want(toolInvokeMyToolById4Want),
169 | 	)
170 | 	tests.RunMCPToolCallMethod(t, mcpMyFailToolWant, mcpSelect1Want, tests.WithMcpMyToolId3NameAliceWant(mcpMyToolId3NameAliceWant))
171 | 	tests.RunToolInvokeWithTemplateParameters(
172 | 		t, tableNameTemplateParam,
173 | 		tests.WithSelectAllWant(tmplSelectAllWwant),
174 | 		tests.WithTmplSelectId1Want(tmplSelectId1Want),
175 | 		tests.DisableDdlTest(),
176 | 	)
177 | 	runSpannerSchemaToolInvokeTest(t, accessSchemaWant)
178 | 	runSpannerExecuteSqlToolInvokeTest(t, select1Want, invokeParamWant, tableNameParam, tableNameAuth)
179 | 	runSpannerListTablesTest(t, tableNameParam, tableNameAuth, tableNameTemplateParam)
180 | }
181 | 
182 | // getSpannerToolInfo returns statements and param for my-tool for spanner-sql kind
183 | func getSpannerParamToolInfo(tableName string) (string, string, string, string, string, string, map[string]any) {
184 | 	createStatement := fmt.Sprintf("CREATE TABLE %s (id INT64, name STRING(MAX)) PRIMARY KEY (id)", tableName)
185 | 	insertStatement := fmt.Sprintf("INSERT INTO %s (id, name) VALUES (1, @name1), (2, @name2), (3, @name3), (4, @name4)", tableName)
186 | 	toolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = @id OR name = @name", tableName)
187 | 	idToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = @id", tableName)
188 | 	nameToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE name = @name", tableName)
189 | 	arrayToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id IN UNNEST(@idArray) AND name IN UNNEST(@nameArray)", tableName)
190 | 	params := map[string]any{"name1": "Alice", "name2": "Jane", "name3": "Sid", "name4": nil}
191 | 	return createStatement, insertStatement, toolStatement, idToolStatement, nameToolStatement, arrayToolStatement, params
192 | }
193 | 
194 | // getSpannerAuthToolInfo returns statements and param of my-auth-tool for spanner-sql kind
195 | func getSpannerAuthToolInfo(tableName string) (string, string, string, map[string]any) {
196 | 	createStatement := fmt.Sprintf("CREATE TABLE %s (id INT64, name STRING(MAX), email STRING(MAX)) PRIMARY KEY (id)", tableName)
197 | 	insertStatement := fmt.Sprintf("INSERT INTO %s (id, name, email) VALUES (1, @name1, @email1), (2, @name2, @email2)", tableName)
198 | 	toolStatement := fmt.Sprintf("SELECT name FROM %s WHERE email = @email", tableName)
199 | 	params := map[string]any{
200 | 		"name1":  "Alice",
201 | 		"email1": tests.ServiceAccountEmail,
202 | 		"name2":  "Jane",
203 | 		"email2": "[email protected]",
204 | 	}
205 | 	return createStatement, insertStatement, toolStatement, params
206 | }
207 | 
208 | // setupSpannerTable creates and inserts data into a table of tool
209 | // compatible with spanner-sql tool
210 | func setupSpannerTable(t *testing.T, ctx context.Context, adminClient *database.DatabaseAdminClient, dataClient *spanner.Client, createStatement, insertStatement, tableName, dbString string, params map[string]any) func(*testing.T) {
211 | 
212 | 	// Create table
213 | 	op, err := adminClient.UpdateDatabaseDdl(ctx, &databasepb.UpdateDatabaseDdlRequest{
214 | 		Database:   dbString,
215 | 		Statements: []string{createStatement},
216 | 	})
217 | 	if err != nil {
218 | 		t.Fatalf("unable to start create table operation %s: %s", tableName, err)
219 | 	}
220 | 	err = op.Wait(ctx)
221 | 	if err != nil {
222 | 		t.Fatalf("unable to create test table %s: %s", tableName, err)
223 | 	}
224 | 
225 | 	// Insert test data
226 | 	if insertStatement != "" {
227 | 		_, err = dataClient.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
228 | 			stmt := spanner.Statement{
229 | 				SQL:    insertStatement,
230 | 				Params: params,
231 | 			}
232 | 			_, err := txn.Update(ctx, stmt)
233 | 			return err
234 | 		})
235 | 		if err != nil {
236 | 			t.Fatalf("unable to insert test data: %s", err)
237 | 		}
238 | 	}
239 | 
240 | 	return func(t *testing.T) {
241 | 		// tear down test
242 | 		op, err = adminClient.UpdateDatabaseDdl(ctx, &databasepb.UpdateDatabaseDdlRequest{
243 | 			Database:   dbString,
244 | 			Statements: []string{fmt.Sprintf("DROP TABLE %s", tableName)},
245 | 		})
246 | 		if err != nil {
247 | 			t.Errorf("unable to start drop %s operation: %s", tableName, err)
248 | 			return
249 | 		}
250 | 
251 | 		opErr := op.Wait(ctx)
252 | 		if opErr != nil {
253 | 			t.Errorf("Teardown failed: %s", opErr)
254 | 		}
255 | 	}
256 | }
257 | 
258 | // addSpannerExecuteSqlConfig gets the tools config for `spanner-execute-sql`
259 | func addSpannerExecuteSqlConfig(t *testing.T, config map[string]any) map[string]any {
260 | 	tools, ok := config["tools"].(map[string]any)
261 | 	if !ok {
262 | 		t.Fatalf("unable to get tools from config")
263 | 	}
264 | 	tools["my-exec-sql-tool-read-only"] = map[string]any{
265 | 		"kind":        "spanner-execute-sql",
266 | 		"source":      "my-instance",
267 | 		"description": "Tool to execute sql",
268 | 		"readOnly":    true,
269 | 	}
270 | 	tools["my-exec-sql-tool"] = map[string]any{
271 | 		"kind":        "spanner-execute-sql",
272 | 		"source":      "my-instance",
273 | 		"description": "Tool to execute sql",
274 | 	}
275 | 	tools["my-auth-exec-sql-tool"] = map[string]any{
276 | 		"kind":        "spanner-execute-sql",
277 | 		"source":      "my-instance",
278 | 		"description": "Tool to execute sql",
279 | 		"authRequired": []string{
280 | 			"my-google-auth",
281 | 		},
282 | 	}
283 | 	config["tools"] = tools
284 | 	return config
285 | }
286 | 
287 | func addSpannerReadOnlyConfig(t *testing.T, config map[string]any) map[string]any {
288 | 	tools, ok := config["tools"].(map[string]any)
289 | 	if !ok {
290 | 		t.Fatalf("unable to get tools from config")
291 | 	}
292 | 	tools["access-schema-read-only"] = map[string]any{
293 | 		"kind":        "spanner-sql",
294 | 		"source":      "my-instance",
295 | 		"description": "Tool to access information schema in read-only mode.",
296 | 		"statement":   "SELECT schema_name FROM `INFORMATION_SCHEMA`.SCHEMATA WHERE schema_name='INFORMATION_SCHEMA';",
297 | 		"readOnly":    true,
298 | 	}
299 | 	tools["access-schema"] = map[string]any{
300 | 		"kind":        "spanner-sql",
301 | 		"source":      "my-instance",
302 | 		"description": "Tool to access information schema.",
303 | 		"statement":   "SELECT schema_name FROM `INFORMATION_SCHEMA`.SCHEMATA WHERE schema_name='INFORMATION_SCHEMA';",
304 | 	}
305 | 	config["tools"] = tools
306 | 	return config
307 | }
308 | 
309 | // addSpannerListTablesConfig adds the spanner-list-tables tool configuration
310 | func addSpannerListTablesConfig(t *testing.T, config map[string]any) map[string]any {
311 | 	tools, ok := config["tools"].(map[string]any)
312 | 	if !ok {
313 | 		t.Fatalf("unable to get tools from config")
314 | 	}
315 | 
316 | 	// Add spanner-list-tables tool
317 | 	tools["list-tables-tool"] = map[string]any{
318 | 		"kind":        "spanner-list-tables",
319 | 		"source":      "my-instance",
320 | 		"description": "Lists tables with their schema information",
321 | 	}
322 | 
323 | 	config["tools"] = tools
324 | 	return config
325 | }
326 | 
327 | func addTemplateParamConfig(t *testing.T, config map[string]any) map[string]any {
328 | 	toolsMap, ok := config["tools"].(map[string]any)
329 | 	if !ok {
330 | 		t.Fatalf("unable to get tools from config")
331 | 	}
332 | 	toolsMap["insert-table-templateParams-tool"] = map[string]any{
333 | 		"kind":        "spanner-sql",
334 | 		"source":      "my-instance",
335 | 		"description": "Insert tool with template parameters",
336 | 		"statement":   "INSERT INTO {{.tableName}} ({{array .columns}}) VALUES ({{.values}})",
337 | 		"templateParameters": []tools.Parameter{
338 | 			tools.NewStringParameter("tableName", "some description"),
339 | 			tools.NewArrayParameter("columns", "The columns to insert into", tools.NewStringParameter("column", "A column name that will be returned from the query.")),
340 | 			tools.NewStringParameter("values", "The values to insert as a comma separated string"),
341 | 		},
342 | 	}
343 | 	toolsMap["select-templateParams-tool"] = map[string]any{
344 | 		"kind":        "spanner-sql",
345 | 		"source":      "my-instance",
346 | 		"description": "Create table tool with template parameters",
347 | 		"statement":   "SELECT * FROM {{.tableName}}",
348 | 		"templateParameters": []tools.Parameter{
349 | 			tools.NewStringParameter("tableName", "some description"),
350 | 		},
351 | 	}
352 | 	toolsMap["select-templateParams-combined-tool"] = map[string]any{
353 | 		"kind":        "spanner-sql",
354 | 		"source":      "my-instance",
355 | 		"description": "Create table tool with template parameters",
356 | 		"statement":   "SELECT * FROM {{.tableName}} WHERE id = @id",
357 | 		"parameters":  []tools.Parameter{tools.NewIntParameter("id", "the id of the user")},
358 | 		"templateParameters": []tools.Parameter{
359 | 			tools.NewStringParameter("tableName", "some description"),
360 | 		},
361 | 	}
362 | 	toolsMap["select-fields-templateParams-tool"] = map[string]any{
363 | 		"kind":        "spanner-sql",
364 | 		"source":      "my-instance",
365 | 		"description": "Create table tool with template parameters",
366 | 		"statement":   "SELECT {{array .fields}} FROM {{.tableName}}",
367 | 		"templateParameters": []tools.Parameter{
368 | 			tools.NewStringParameter("tableName", "some description"),
369 | 			tools.NewArrayParameter("fields", "The fields to select from", tools.NewStringParameter("field", "A field that will be returned from the query.")),
370 | 		},
371 | 	}
372 | 	toolsMap["select-filter-templateParams-combined-tool"] = map[string]any{
373 | 		"kind":        "spanner-sql",
374 | 		"source":      "my-instance",
375 | 		"description": "Create table tool with template parameters",
376 | 		"statement":   "SELECT * FROM {{.tableName}} WHERE {{.columnFilter}} = @name",
377 | 		"parameters":  []tools.Parameter{tools.NewStringParameter("name", "the name of the user")},
378 | 		"templateParameters": []tools.Parameter{
379 | 			tools.NewStringParameter("tableName", "some description"),
380 | 			tools.NewStringParameter("columnFilter", "some description"),
381 | 		},
382 | 	}
383 | 	config["tools"] = toolsMap
384 | 	return config
385 | }
386 | 
387 | func runSpannerExecuteSqlToolInvokeTest(t *testing.T, select1Want, invokeParamWant, tableNameParam, tableNameAuth string) {
388 | 	// Get ID token
389 | 	idToken, err := tests.GetGoogleIdToken(tests.ClientId)
390 | 	if err != nil {
391 | 		t.Fatalf("error getting Google ID token: %s", err)
392 | 	}
393 | 
394 | 	// Test tool invoke endpoint
395 | 	invokeTcs := []struct {
396 | 		name          string
397 | 		api           string
398 | 		requestHeader map[string]string
399 | 		requestBody   io.Reader
400 | 		want          string
401 | 		isErr         bool
402 | 	}{
403 | 		{
404 | 			name:          "invoke my-exec-sql-tool-read-only",
405 | 			api:           "http://127.0.0.1:5000/api/tool/my-exec-sql-tool-read-only/invoke",
406 | 			requestHeader: map[string]string{},
407 | 			requestBody:   bytes.NewBuffer([]byte(`{"sql":"SELECT 1"}`)),
408 | 			want:          select1Want,
409 | 			isErr:         false,
410 | 		},
411 | 		{
412 | 			name:          "invoke my-exec-sql-tool-read-only with data present in table",
413 | 			api:           "http://127.0.0.1:5000/api/tool/my-exec-sql-tool-read-only/invoke",
414 | 			requestHeader: map[string]string{},
415 | 			requestBody:   bytes.NewBuffer([]byte(fmt.Sprintf("{\"sql\":\"SELECT * FROM %s WHERE id = 3 OR name = 'Alice'\"}", tableNameParam))),
416 | 			want:          invokeParamWant,
417 | 			isErr:         false,
418 | 		},
419 | 		{
420 | 			name:          "invoke my-exec-sql-tool-read-only create table",
421 | 			api:           "http://127.0.0.1:5000/api/tool/my-exec-sql-tool-read-only/invoke",
422 | 			requestHeader: map[string]string{},
423 | 			requestBody:   bytes.NewBuffer([]byte(`{"sql":"CREATE TABLE t (id SERIAL PRIMARY KEY, name TEXT)"}`)),
424 | 			isErr:         true,
425 | 		},
426 | 		{
427 | 			name:          "invoke my-exec-sql-tool-read-only drop table",
428 | 			api:           "http://127.0.0.1:5000/api/tool/my-exec-sql-tool-read-only/invoke",
429 | 			requestHeader: map[string]string{},
430 | 			requestBody:   bytes.NewBuffer([]byte(`{"sql":"DROP TABLE t"}`)),
431 | 			isErr:         true,
432 | 		},
433 | 		{
434 | 			name:          "invoke my-exec-sql-tool-read-only insert entry",
435 | 			api:           "http://127.0.0.1:5000/api/tool/my-exec-sql-tool-read-only/invoke",
436 | 			requestHeader: map[string]string{},
437 | 			requestBody:   bytes.NewBuffer([]byte(fmt.Sprintf("{\"sql\":\"INSERT INTO %s (id, name) VALUES (4, 'test_name')\"}", tableNameParam))),
438 | 			isErr:         true,
439 | 		},
440 | 		{
441 | 			name:          "invoke my-exec-sql-tool without body",
442 | 			api:           "http://127.0.0.1:5000/api/tool/my-exec-sql-tool/invoke",
443 | 			requestHeader: map[string]string{},
444 | 			requestBody:   bytes.NewBuffer([]byte(`{}`)),
445 | 			isErr:         true,
446 | 		},
447 | 		{
448 | 			name:          "invoke my-exec-sql-tool",
449 | 			api:           "http://127.0.0.1:5000/api/tool/my-exec-sql-tool/invoke",
450 | 			requestHeader: map[string]string{},
451 | 			requestBody:   bytes.NewBuffer([]byte(`{"sql":"SELECT 1"}`)),
452 | 			want:          select1Want,
453 | 			isErr:         false,
454 | 		},
455 | 		{
456 | 			name:          "invoke my-exec-sql-tool create table",
457 | 			api:           "http://127.0.0.1:5000/api/tool/my-exec-sql-tool/invoke",
458 | 			requestHeader: map[string]string{},
459 | 			requestBody:   bytes.NewBuffer([]byte(`{"sql":"CREATE TABLE t (id SERIAL PRIMARY KEY, name TEXT)"}`)),
460 | 			isErr:         true,
461 | 		},
462 | 		{
463 | 			name:          "invoke my-exec-sql-tool drop table",
464 | 			api:           "http://127.0.0.1:5000/api/tool/my-exec-sql-tool/invoke",
465 | 			requestHeader: map[string]string{},
466 | 			requestBody:   bytes.NewBuffer([]byte(`{"sql":"DROP TABLE t"}`)),
467 | 			isErr:         true,
468 | 		},
469 | 		{
470 | 			name:          "invoke my-exec-sql-tool insert entry",
471 | 			api:           "http://127.0.0.1:5000/api/tool/my-exec-sql-tool/invoke",
472 | 			requestHeader: map[string]string{},
473 | 			requestBody:   bytes.NewBuffer([]byte(fmt.Sprintf("{\"sql\":\"INSERT INTO %s (id, name) VALUES (5, 'test_name')\"}", tableNameParam))),
474 | 			want:          "null",
475 | 			isErr:         false,
476 | 		},
477 | 		{
478 | 			name:          "invoke my-exec-sql-tool without body",
479 | 			api:           "http://127.0.0.1:5000/api/tool/my-exec-sql-tool/invoke",
480 | 			requestHeader: map[string]string{},
481 | 			requestBody:   bytes.NewBuffer([]byte(`{}`)),
482 | 			isErr:         true,
483 | 		},
484 | 		{
485 | 			name:          "Invoke my-auth-exec-sql-tool with auth token",
486 | 			api:           "http://127.0.0.1:5000/api/tool/my-auth-exec-sql-tool/invoke",
487 | 			requestHeader: map[string]string{"my-google-auth_token": idToken},
488 | 			requestBody:   bytes.NewBuffer([]byte(`{"sql":"SELECT 1"}`)),
489 | 			isErr:         false,
490 | 			want:          select1Want,
491 | 		},
492 | 		{
493 | 			name:          "Invoke my-auth-exec-sql-tool with invalid auth token",
494 | 			api:           "http://127.0.0.1:5000/api/tool/my-auth-exec-sql-tool/invoke",
495 | 			requestHeader: map[string]string{"my-google-auth_token": "INVALID_TOKEN"},
496 | 			requestBody:   bytes.NewBuffer([]byte(`{"sql":"SELECT 1"}`)),
497 | 			isErr:         true,
498 | 		},
499 | 		{
500 | 			name:          "Invoke my-auth-exec-sql-tool without auth token",
501 | 			api:           "http://127.0.0.1:5000/api/tool/my-auth-exec-sql-tool/invoke",
502 | 			requestHeader: map[string]string{},
503 | 			requestBody:   bytes.NewBuffer([]byte(`{"sql":"SELECT 1"}`)),
504 | 			isErr:         true,
505 | 		},
506 | 	}
507 | 	for _, tc := range invokeTcs {
508 | 		t.Run(tc.name, func(t *testing.T) {
509 | 			// Send Tool invocation request
510 | 			req, err := http.NewRequest(http.MethodPost, tc.api, tc.requestBody)
511 | 			if err != nil {
512 | 				t.Fatalf("unable to create request: %s", err)
513 | 			}
514 | 			req.Header.Add("Content-type", "application/json")
515 | 			for k, v := range tc.requestHeader {
516 | 				req.Header.Add(k, v)
517 | 			}
518 | 			resp, err := http.DefaultClient.Do(req)
519 | 			if err != nil {
520 | 				t.Fatalf("unable to send request: %s", err)
521 | 			}
522 | 			defer resp.Body.Close()
523 | 
524 | 			if resp.StatusCode != http.StatusOK {
525 | 				if tc.isErr {
526 | 					return
527 | 				}
528 | 				bodyBytes, _ := io.ReadAll(resp.Body)
529 | 				t.Fatalf("response status code is not 200, got %d: %s", resp.StatusCode, string(bodyBytes))
530 | 			}
531 | 
532 | 			// Check response body
533 | 			var body map[string]interface{}
534 | 			err = json.NewDecoder(resp.Body).Decode(&body)
535 | 			if err != nil {
536 | 				t.Fatalf("error parsing response body")
537 | 			}
538 | 
539 | 			got, ok := body["result"].(string)
540 | 			if !ok {
541 | 				t.Fatalf("unable to find result in response body")
542 | 			}
543 | 
544 | 			if got != tc.want {
545 | 				t.Fatalf("unexpected value: got %q, want %q", got, tc.want)
546 | 			}
547 | 		})
548 | 	}
549 | }
550 | 
551 | // Helper function to verify table list results
552 | func verifyTableListResult(t *testing.T, body map[string]interface{}, expectedTables []string, expectedSimpleFormat bool) {
553 | 	// Parse the result
554 | 	result, ok := body["result"].(string)
555 | 	if !ok {
556 | 		t.Fatalf("unable to find result in response body")
557 | 	}
558 | 
559 | 	var tables []interface{}
560 | 	err := json.Unmarshal([]byte(result), &tables)
561 | 	if err != nil {
562 | 		t.Fatalf("unable to parse result as JSON array: %s", err)
563 | 	}
564 | 
565 | 	// If we expect specific tables, verify they exist
566 | 	if len(expectedTables) > 0 {
567 | 		tableNames := make(map[string]bool)
568 | 		requiredKeys := []string{"schema_name", "object_name", "object_type", "columns", "constraints", "indexes"}
569 | 		if expectedSimpleFormat {
570 | 			requiredKeys = []string{"name"}
571 | 		}
572 | 
573 | 		for _, table := range tables {
574 | 			tableMap, ok := table.(map[string]interface{})
575 | 			if !ok {
576 | 				continue
577 | 			}
578 | 
579 | 			// Parse object_details JSON string into map[string]interface{}
580 | 			if objectDetailsStr, ok := tableMap["object_details"].(string); ok {
581 | 				var objectDetails map[string]interface{}
582 | 				if err := json.Unmarshal([]byte(objectDetailsStr), &objectDetails); err != nil {
583 | 					t.Errorf("failed to parse object_details JSON: %v for %v", err, objectDetailsStr)
584 | 					continue
585 | 				}
586 | 
587 | 				for _, reqKey := range requiredKeys {
588 | 					if _, hasKey := objectDetails[reqKey]; !hasKey {
589 | 						t.Errorf("missing required key '%s', for object_details: %v", reqKey, objectDetails)
590 | 					}
591 | 				}
592 | 			}
593 | 
594 | 			if name, ok := tableMap["object_name"].(string); ok {
595 | 				tableNames[name] = true
596 | 			}
597 | 		}
598 | 
599 | 		for _, expected := range expectedTables {
600 | 			if !tableNames[expected] {
601 | 				t.Errorf("expected table %s not found in results", expected)
602 | 			}
603 | 		}
604 | 	}
605 | }
606 | 
607 | // runSpannerListTablesTest tests the spanner-list-tables tool
608 | func runSpannerListTablesTest(t *testing.T, tableNameParam, tableNameAuth, tableNameTemplateParam string) {
609 | 	invokeTcs := []struct {
610 | 		name            string
611 | 		requestBody     io.Reader
612 | 		expectedTables  []string // empty means don't check specific tables
613 | 		useSimpleFormat bool
614 | 	}{
615 | 		{
616 | 			name:           "list all tables with detailed format",
617 | 			requestBody:    bytes.NewBuffer([]byte(`{}`)),
618 | 			expectedTables: []string{tableNameParam, tableNameAuth, tableNameTemplateParam},
619 | 		},
620 | 		{
621 | 			name:            "list tables with simple format",
622 | 			requestBody:     bytes.NewBuffer([]byte(`{"output_format": "simple"}`)),
623 | 			expectedTables:  []string{tableNameParam, tableNameAuth, tableNameTemplateParam},
624 | 			useSimpleFormat: true,
625 | 		},
626 | 		{
627 | 			name:           "list specific tables",
628 | 			requestBody:    bytes.NewBuffer([]byte(fmt.Sprintf(`{"table_names": "%s,%s"}`, tableNameParam, tableNameAuth))),
629 | 			expectedTables: []string{tableNameParam, tableNameAuth},
630 | 		},
631 | 		{
632 | 			name:           "list non-existent table",
633 | 			requestBody:    bytes.NewBuffer([]byte(`{"table_names": "non_existent_table_xyz"}`)),
634 | 			expectedTables: []string{},
635 | 		},
636 | 	}
637 | 
638 | 	for _, tc := range invokeTcs {
639 | 		t.Run(tc.name, func(t *testing.T) {
640 | 			// Use RunRequest helper function from tests package
641 | 			url := "http://127.0.0.1:5000/api/tool/list-tables-tool/invoke"
642 | 			headers := map[string]string{}
643 | 
644 | 			resp, respBody := tests.RunRequest(t, http.MethodPost, url, tc.requestBody, headers)
645 | 
646 | 			if resp.StatusCode != http.StatusOK {
647 | 				t.Fatalf("response status code is not 200, got %d: %s", resp.StatusCode, string(respBody))
648 | 			}
649 | 
650 | 			// Check response body
651 | 			var body map[string]interface{}
652 | 			err := json.Unmarshal(respBody, &body)
653 | 			if err != nil {
654 | 				t.Fatalf("error parsing response body: %s", err)
655 | 			}
656 | 
657 | 			verifyTableListResult(t, body, tc.expectedTables, tc.useSimpleFormat)
658 | 		})
659 | 	}
660 | }
661 | 
662 | func runSpannerSchemaToolInvokeTest(t *testing.T, accessSchemaWant string) {
663 | 	invokeTcs := []struct {
664 | 		name          string
665 | 		api           string
666 | 		requestHeader map[string]string
667 | 		requestBody   io.Reader
668 | 		want          string
669 | 		isErr         bool
670 | 	}{
671 | 		{
672 | 			name:          "invoke list-tables-read-only",
673 | 			api:           "http://127.0.0.1:5000/api/tool/access-schema-read-only/invoke",
674 | 			requestHeader: map[string]string{},
675 | 			requestBody:   bytes.NewBuffer([]byte(`{}`)),
676 | 			want:          accessSchemaWant,
677 | 			isErr:         false,
678 | 		},
679 | 		{
680 | 			name:          "invoke list-tables",
681 | 			api:           "http://127.0.0.1:5000/api/tool/access-schema/invoke",
682 | 			requestHeader: map[string]string{},
683 | 			requestBody:   bytes.NewBuffer([]byte(`{}`)),
684 | 			isErr:         true,
685 | 		},
686 | 	}
687 | 	for _, tc := range invokeTcs {
688 | 		t.Run(tc.name, func(t *testing.T) {
689 | 			// Send Tool invocation request
690 | 			req, err := http.NewRequest(http.MethodPost, tc.api, tc.requestBody)
691 | 			if err != nil {
692 | 				t.Fatalf("unable to create request: %s", err)
693 | 			}
694 | 			req.Header.Add("Content-type", "application/json")
695 | 			for k, v := range tc.requestHeader {
696 | 				req.Header.Add(k, v)
697 | 			}
698 | 			resp, err := http.DefaultClient.Do(req)
699 | 			if err != nil {
700 | 				t.Fatalf("unable to send request: %s", err)
701 | 			}
702 | 			defer resp.Body.Close()
703 | 
704 | 			if resp.StatusCode != http.StatusOK {
705 | 				if tc.isErr {
706 | 					return
707 | 				}
708 | 				bodyBytes, _ := io.ReadAll(resp.Body)
709 | 				t.Fatalf("response status code is not 200, got %d: %s", resp.StatusCode, string(bodyBytes))
710 | 			}
711 | 
712 | 			// Check response body
713 | 			var body map[string]interface{}
714 | 			err = json.NewDecoder(resp.Body).Decode(&body)
715 | 			if err != nil {
716 | 				t.Fatalf("error parsing response body")
717 | 			}
718 | 
719 | 			got, ok := body["result"].(string)
720 | 			if !ok {
721 | 				t.Fatalf("unable to find result in response body")
722 | 			}
723 | 
724 | 			if got != tc.want {
725 | 				t.Fatalf("unexpected value: got %q, want %q", got, tc.want)
726 | 			}
727 | 		})
728 | 	}
729 | }
730 | 
```

--------------------------------------------------------------------------------
/.ci/integration.cloudbuild.yaml:
--------------------------------------------------------------------------------

```yaml
  1 | # Copyright 2024 Google LLC
  2 | #
  3 | # Licensed under the Apache License, Version 2.0 (the "License");
  4 | # you may not use this file except in compliance with the License.
  5 | # You may obtain a copy of the License at
  6 | #
  7 | #      http://www.apache.org/licenses/LICENSE-2.0
  8 | #
  9 | # Unless required by applicable law or agreed to in writing, software
 10 | # distributed under the License is distributed on an "AS IS" BASIS,
 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | # See the License for the specific language governing permissions and
 13 | # limitations under the License.
 14 | 
 15 | steps:
 16 |   - id: "install-dependencies"
 17 |     name: golang:1
 18 |     waitFor: ["-"]
 19 |     env:
 20 |       - "GOPATH=/gopath"
 21 |     volumes:
 22 |       - name: "go"
 23 |         path: "/gopath"
 24 |     script: |
 25 |       go get -d ./...
 26 | 
 27 |   - id: "compile-test-binary"
 28 |     name: golang:1
 29 |     waitFor: ["install-dependencies"]
 30 |     env:
 31 |       - "GOPATH=/gopath"
 32 |     volumes:
 33 |       - name: "go"
 34 |         path: "/gopath"
 35 |     script: |
 36 |       go test -c -race -cover \
 37 |       -coverpkg=./internal/sources/...,./internal/tools/... ./tests/...
 38 |       chmod +x .ci/test_with_coverage.sh
 39 | 
 40 |   - id: "cloud-sql-pg"
 41 |     name: golang:1
 42 |     waitFor: ["compile-test-binary"]
 43 |     entrypoint: /bin/bash
 44 |     env:
 45 |       - "GOPATH=/gopath"
 46 |       - "CLOUD_SQL_POSTGRES_PROJECT=$PROJECT_ID"
 47 |       - "CLOUD_SQL_POSTGRES_INSTANCE=$_CLOUD_SQL_POSTGRES_INSTANCE"
 48 |       - "CLOUD_SQL_POSTGRES_DATABASE=$_DATABASE_NAME"
 49 |       - "CLOUD_SQL_POSTGRES_REGION=$_REGION"
 50 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
 51 |     secretEnv:
 52 |       ["CLOUD_SQL_POSTGRES_USER", "CLOUD_SQL_POSTGRES_PASS", "CLIENT_ID"]
 53 |     volumes:
 54 |       - name: "go"
 55 |         path: "/gopath"
 56 |     args:
 57 |       - -c
 58 |       - |
 59 |         .ci/test_with_coverage.sh \
 60 |           "Cloud SQL Postgres" \
 61 |           cloudsqlpg \
 62 |           postgressql \
 63 |           postgresexecutesql
 64 | 
 65 |   - id: "alloydb"
 66 |     name: golang:1
 67 |     waitFor: ["compile-test-binary"]
 68 |     entrypoint: /bin/bash
 69 |     env:
 70 |       - "GOPATH=/gopath"
 71 |       - "ALLOYDB_PROJECT=$PROJECT_ID"
 72 |       - "ALLOYDB_CLUSTER=$_ALLOYDB_POSTGRES_CLUSTER"
 73 |       - "ALLOYDB_INSTANCE=$_ALLOYDB_POSTGRES_INSTANCE"
 74 |       - "ALLOYDB_REGION=$_REGION"
 75 |     secretEnv: ["ALLOYDB_POSTGRES_USER"]
 76 |     volumes:
 77 |       - name: "go"
 78 |         path: "/gopath"
 79 |     args:
 80 |       - -c
 81 |       - |
 82 |         .ci/test_with_coverage.sh \
 83 |           "AlloyDB" \
 84 |           alloydb \
 85 |           alloydb
 86 | 
 87 |   - id: "alloydb-pg"
 88 |     name: golang:1
 89 |     waitFor: ["compile-test-binary"]
 90 |     entrypoint: /bin/bash
 91 |     env:
 92 |       - "GOPATH=/gopath"
 93 |       - "ALLOYDB_POSTGRES_PROJECT=$PROJECT_ID"
 94 |       - "ALLOYDB_POSTGRES_CLUSTER=$_ALLOYDB_POSTGRES_CLUSTER"
 95 |       - "ALLOYDB_POSTGRES_INSTANCE=$_ALLOYDB_POSTGRES_INSTANCE"
 96 |       - "ALLOYDB_POSTGRES_DATABASE=$_DATABASE_NAME"
 97 |       - "ALLOYDB_POSTGRES_REGION=$_REGION"
 98 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
 99 |     secretEnv: ["ALLOYDB_POSTGRES_USER", "ALLOYDB_POSTGRES_PASS", "CLIENT_ID"]
100 |     volumes:
101 |       - name: "go"
102 |         path: "/gopath"
103 |     args:
104 |       - -c
105 |       - |
106 |         .ci/test_with_coverage.sh \
107 |           "AlloyDB Postgres" \
108 |           alloydbpg \
109 |           postgressql \
110 |           postgresexecutesql
111 | 
112 |   - id: "alloydb-ai-nl"
113 |     name: golang:1
114 |     waitFor: ["compile-test-binary"]
115 |     entrypoint: /bin/bash
116 |     env:
117 |       - "GOPATH=/gopath"
118 |       - "ALLOYDB_AI_NL_PROJECT=$PROJECT_ID"
119 |       - "ALLOYDB_AI_NL_CLUSTER=$_ALLOYDB_AI_NL_CLUSTER"
120 |       - "ALLOYDB_AI_NL_INSTANCE=$_ALLOYDB_AI_NL_INSTANCE"
121 |       - "ALLOYDB_AI_NL_DATABASE=$_AI_NL_DATABASE_NAME"
122 |       - "ALLOYDB_AI_NL_REGION=$_REGION"
123 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
124 |     secretEnv: ["ALLOYDB_AI_NL_USER", "ALLOYDB_AI_NL_PASS", "CLIENT_ID"]
125 |     volumes:
126 |       - name: "go"
127 |         path: "/gopath"
128 |     args:
129 |       - -c
130 |       - |
131 |         .ci/test_with_coverage.sh \
132 |           "AlloyDB AI NL" \
133 |           alloydbainl \
134 |           alloydbainl
135 | 
136 |   - id: "bigtable"
137 |     name: golang:1
138 |     waitFor: ["compile-test-binary"]
139 |     entrypoint: /bin/bash
140 |     env:
141 |       - "GOPATH=/gopath"
142 |       - "BIGTABLE_PROJECT=$PROJECT_ID"
143 |       - "BIGTABLE_INSTANCE=$_BIGTABLE_INSTANCE"
144 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
145 |     secretEnv: ["CLIENT_ID"]
146 |     volumes:
147 |       - name: "go"
148 |         path: "/gopath"
149 |     args:
150 |       - -c
151 |       - |
152 |         .ci/test_with_coverage.sh \
153 |           "Bigtable" \
154 |           bigtable \
155 |           bigtable
156 | 
157 |   - id: "bigquery"
158 |     name: golang:1
159 |     waitFor: ["compile-test-binary"]
160 |     entrypoint: /bin/bash
161 |     env:
162 |       - "GOPATH=/gopath"
163 |       - "BIGQUERY_PROJECT=$PROJECT_ID"
164 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
165 |     secretEnv: ["CLIENT_ID"]
166 |     volumes:
167 |       - name: "go"
168 |         path: "/gopath"
169 |     args:
170 |       - -c
171 |       - |
172 |         .ci/test_with_coverage.sh \
173 |           "BigQuery" \
174 |           bigquery \
175 |           bigquery
176 | 
177 |   - id: "dataplex"
178 |     name: golang:1
179 |     waitFor: ["compile-test-binary"]
180 |     entrypoint: /bin/bash
181 |     env:
182 |       - "GOPATH=/gopath"
183 |       - "DATAPLEX_PROJECT=$PROJECT_ID"
184 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
185 |     secretEnv: ["CLIENT_ID"]
186 |     volumes:
187 |       - name: "go"
188 |         path: "/gopath"
189 |     args:
190 |       - -c
191 |       - |
192 |         .ci/test_with_coverage.sh \
193 |           "Dataplex" \
194 |           dataplex \
195 |           dataplex
196 | 
197 |   - id: "dataform"
198 |     name: golang:1
199 |     waitFor: ["compile-test-binary"]
200 |     entrypoint: /bin/bash
201 |     env:
202 |       - "GOPATH=/gopath"
203 |     secretEnv: ["CLIENT_ID"]
204 |     volumes:
205 |       - name: "go"
206 |         path: "/gopath"
207 |     args:
208 |       - -c
209 |       - |
210 |         apt-get update && apt-get install -y npm && \
211 |         npm install -g @dataform/cli && \
212 |         .ci/test_with_coverage.sh \
213 |           "Dataform" \
214 |           dataform \
215 |           dataform
216 | 
217 |   - id: "postgres"
218 |     name: golang:1
219 |     waitFor: ["compile-test-binary"]
220 |     entrypoint: /bin/bash
221 |     env:
222 |       - "GOPATH=/gopath"
223 |       - "POSTGRES_DATABASE=$_DATABASE_NAME"
224 |       - "POSTGRES_HOST=$_POSTGRES_HOST"
225 |       - "POSTGRES_PORT=$_POSTGRES_PORT"
226 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
227 |     secretEnv: ["POSTGRES_USER", "POSTGRES_PASS", "CLIENT_ID"]
228 |     volumes:
229 |       - name: "go"
230 |         path: "/gopath"
231 |     args:
232 |       - -c
233 |       - |
234 |         .ci/test_with_coverage.sh \
235 |           "Postgres" \
236 |           postgres \
237 |           postgressql \
238 |           postgresexecutesql
239 | 
240 |   - id: "spanner"
241 |     name: golang:1
242 |     waitFor: ["compile-test-binary"]
243 |     entrypoint: /bin/bash
244 |     env:
245 |       - "GOPATH=/gopath"
246 |       - "SPANNER_PROJECT=$PROJECT_ID"
247 |       - "SPANNER_DATABASE=$_DATABASE_NAME"
248 |       - "SPANNER_INSTANCE=$_SPANNER_INSTANCE"
249 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
250 |     secretEnv: ["CLIENT_ID"]
251 |     volumes:
252 |       - name: "go"
253 |         path: "/gopath"
254 |     args:
255 |       - -c
256 |       - |
257 |         .ci/test_with_coverage.sh \
258 |           "Spanner" \
259 |           spanner \
260 |           spanner
261 | 
262 |   - id: "neo4j"
263 |     name: golang:1
264 |     waitFor: ["compile-test-binary"]
265 |     entrypoint: /bin/bash
266 |     env:
267 |       - "GOPATH=/gopath"
268 |       - "NEO4J_DATABASE=$_NEO4J_DATABASE"
269 |       - "NEO4J_URI=$_NEO4J_URI"
270 |     secretEnv: ["NEO4J_USER", "NEO4J_PASS"]
271 |     volumes:
272 |       - name: "go"
273 |         path: "/gopath"
274 |     args:
275 |       - -c
276 |       - |
277 |         .ci/test_with_coverage.sh \
278 |           "Neo4j" \
279 |           neo4j \
280 |           neo4j
281 | 
282 |   - id: "cloud-sql-mssql"
283 |     name: golang:1
284 |     waitFor: ["compile-test-binary"]
285 |     entrypoint: /bin/bash
286 |     env:
287 |       - "GOPATH=/gopath"
288 |       - "CLOUD_SQL_MSSQL_PROJECT=$PROJECT_ID"
289 |       - "CLOUD_SQL_MSSQL_INSTANCE=$_CLOUD_SQL_MSSQL_INSTANCE"
290 |       - "CLOUD_SQL_MSSQL_IP=$_CLOUD_SQL_MSSQL_IP"
291 |       - "CLOUD_SQL_MSSQL_DATABASE=$_DATABASE_NAME"
292 |       - "CLOUD_SQL_MSSQL_REGION=$_REGION"
293 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
294 |     secretEnv: ["CLOUD_SQL_MSSQL_USER", "CLOUD_SQL_MSSQL_PASS", "CLIENT_ID"]
295 |     volumes:
296 |       - name: "go"
297 |         path: "/gopath"
298 |     args:
299 |       - -c
300 |       - |
301 |         .ci/test_with_coverage.sh \
302 |           "Cloud SQL MSSQL" \
303 |           cloudsqlmssql \
304 |           mssql
305 | 
306 |   - id: "cloud-sql-mysql"
307 |     name: golang:1
308 |     waitFor: ["compile-test-binary"]
309 |     entrypoint: /bin/bash
310 |     env:
311 |       - "GOPATH=/gopath"
312 |       - "CLOUD_SQL_MYSQL_PROJECT=$PROJECT_ID"
313 |       - "CLOUD_SQL_MYSQL_INSTANCE=$_CLOUD_SQL_MYSQL_INSTANCE"
314 |       - "CLOUD_SQL_MYSQL_DATABASE=$_DATABASE_NAME"
315 |       - "CLOUD_SQL_MYSQL_REGION=$_REGION"
316 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
317 |     secretEnv: ["CLOUD_SQL_MYSQL_USER", "CLOUD_SQL_MYSQL_PASS", "CLIENT_ID"]
318 |     volumes:
319 |       - name: "go"
320 |         path: "/gopath"
321 |     args:
322 |       - -c
323 |       - |
324 |         .ci/test_with_coverage.sh \
325 |           "Cloud SQL MySQL" \
326 |           cloudsqlmysql \
327 |           mysql || echo "Integration tests failed." # ignore test failures
328 | 
329 |   - id: "mysql"
330 |     name: golang:1
331 |     waitFor: ["compile-test-binary"]
332 |     entrypoint: /bin/bash
333 |     env:
334 |       - "GOPATH=/gopath"
335 |       - "MYSQL_DATABASE=$_DATABASE_NAME"
336 |       - "MYSQL_HOST=$_MYSQL_HOST"
337 |       - "MYSQL_PORT=$_MYSQL_PORT"
338 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
339 |     secretEnv: ["MYSQL_USER", "MYSQL_PASS", "CLIENT_ID"]
340 |     volumes:
341 |       - name: "go"
342 |         path: "/gopath"
343 |     args:
344 |       - -c
345 |       - |
346 |         .ci/test_with_coverage.sh \
347 |           "MySQL" \
348 |           mysql \
349 |           mysql || echo "Integration tests failed." # ignore test failures
350 | 
351 |   - id: "mssql"
352 |     name: golang:1
353 |     waitFor: ["compile-test-binary"]
354 |     entrypoint: /bin/bash
355 |     env:
356 |       - "GOPATH=/gopath"
357 |       - "MSSQL_DATABASE=$_DATABASE_NAME"
358 |       - "MSSQL_HOST=$_MSSQL_HOST"
359 |       - "MSSQL_PORT=$_MSSQL_PORT"
360 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
361 |     secretEnv: ["MSSQL_USER", "MSSQL_PASS", "CLIENT_ID"]
362 |     volumes:
363 |       - name: "go"
364 |         path: "/gopath"
365 |     args:
366 |       - -c
367 |       - |
368 |         .ci/test_with_coverage.sh \
369 |           "MSSQL" \
370 |           mssql \
371 |           mssql
372 | 
373 |   # - id: "dgraph"
374 |   #   name: golang:1
375 |   #   waitFor: ["compile-test-binary"]
376 |   #   entrypoint: /bin/bash
377 |   #   env:
378 |   #     - "GOPATH=/gopath"
379 |   #     - "DGRAPH_URL=$_DGRAPHURL"
380 |   #   volumes:
381 |   #     - name: "go"
382 |   #       path: "/gopath"
383 |   #   args:
384 |   #     - -c
385 |   #     - |
386 |   #       .ci/test_with_coverage.sh \
387 |   #         "Dgraph" \
388 |   #         dgraph \
389 |   #         dgraph
390 | 
391 |   - id: "http"
392 |     name: golang:1
393 |     waitFor: ["compile-test-binary"]
394 |     entrypoint: /bin/bash
395 |     env:
396 |       - "GOPATH=/gopath"
397 |     secretEnv: ["CLIENT_ID"]
398 |     volumes:
399 |       - name: "go"
400 |         path: "/gopath"
401 |     args:
402 |       - -c
403 |       - |
404 |         .ci/test_with_coverage.sh \
405 |           "HTTP" \
406 |           http \
407 |           http
408 | 
409 |   - id: "sqlite"
410 |     name: golang:1
411 |     waitFor: ["compile-test-binary"]
412 |     entrypoint: /bin/bash
413 |     env:
414 |       - "GOPATH=/gopath"
415 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
416 |     volumes:
417 |       - name: "go"
418 |         path: "/gopath"
419 |     secretEnv: ["CLIENT_ID"]
420 |     args:
421 |       - -c
422 |       - |
423 |         .ci/test_with_coverage.sh \
424 |           "SQLite" \
425 |           sqlite \
426 |           sqlite
427 | 
428 |   - id: "couchbase"
429 |     name: golang:1
430 |     waitFor: ["compile-test-binary"]
431 |     entrypoint: /bin/bash
432 |     env:
433 |       - "GOPATH=/gopath"
434 |       - "COUCHBASE_SCOPE=$_COUCHBASE_SCOPE"
435 |       - "COUCHBASE_BUCKET=$_COUCHBASE_BUCKET"
436 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
437 |     secretEnv:
438 |       ["COUCHBASE_CONNECTION", "COUCHBASE_USER", "COUCHBASE_PASS", "CLIENT_ID"]
439 |     volumes:
440 |       - name: "go"
441 |         path: "/gopath"
442 |     args:
443 |       - -c
444 |       - |
445 |         .ci/test_with_coverage.sh \
446 |           "Couchbase" \
447 |           couchbase \
448 |           couchbase
449 | 
450 |   - id: "redis"
451 |     name: golang:1
452 |     waitFor: ["compile-test-binary"]
453 |     entrypoint: /bin/bash
454 |     env:
455 |       - "GOPATH=/gopath"
456 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
457 |     secretEnv: ["REDIS_ADDRESS", "REDIS_PASS", "CLIENT_ID"]
458 |     volumes:
459 |       - name: "go"
460 |         path: "/gopath"
461 |     args:
462 |       - -c
463 |       - |
464 |         .ci/test_with_coverage.sh \
465 |           "Redis" \
466 |           redis \
467 |           redis
468 | 
469 |   - id: "valkey"
470 |     name: golang:1
471 |     waitFor: ["compile-test-binary"]
472 |     entrypoint: /bin/bash
473 |     env:
474 |       - "GOPATH=/gopath"
475 |       - "VALKEY_DATABASE=$_VALKEY_DATABASE"
476 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
477 |     secretEnv: ["VALKEY_ADDRESS", "CLIENT_ID"]
478 |     volumes:
479 |       - name: "go"
480 |         path: "/gopath"
481 |     args:
482 |       - -c
483 |       - |
484 |         .ci/test_with_coverage.sh \
485 |           "Valkey" \
486 |           valkey \
487 |           valkey
488 | 
489 |   - id: "oceanbase"
490 |     name: golang:1
491 |     waitFor: ["compile-test-binary"]
492 |     entrypoint: /bin/bash
493 |     env:
494 |       - "GOPATH=/gopath"
495 |       - "OCEANBASE_PORT=$_OCEANBASE_PORT"
496 |       - "OCEANBASE_DATABASE=$_OCEANBASE_DATABASE"
497 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
498 |     secretEnv:
499 |       ["CLIENT_ID", "OCEANBASE_HOST", "OCEANBASE_USER", "OCEANBASE_PASSWORD"]
500 |     volumes:
501 |       - name: "go"
502 |         path: "/gopath"
503 |     args:
504 |       - -c
505 |       - |
506 |         .ci/test_with_coverage.sh \
507 |           "OceanBase" \
508 |           oceanbase \
509 |           oceanbase
510 | 
511 |   - id: "firestore"
512 |     name: golang:1
513 |     waitFor: ["compile-test-binary"]
514 |     entrypoint: /bin/bash
515 |     env:
516 |       - "GOPATH=/gopath"
517 |       - "FIRESTORE_PROJECT=$PROJECT_ID"
518 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
519 |     secretEnv: ["CLIENT_ID"]
520 |     volumes:
521 |       - name: "go"
522 |         path: "/gopath"
523 |     args:
524 |       - -c
525 |       - |
526 |         .ci/test_with_coverage.sh \
527 |           "Firestore" \
528 |           firestore \
529 |           firestore
530 | 
531 |   - id: "looker"
532 |     name: golang:1
533 |     waitFor: ["compile-test-binary"]
534 |     entrypoint: /bin/bash
535 |     env:
536 |       - "GOPATH=/gopath"
537 |       - "FIRESTORE_PROJECT=$PROJECT_ID"
538 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
539 |       - "LOOKER_VERIFY_SSL=$_LOOKER_VERIFY_SSL"
540 |       - "LOOKER_PROJECT=$_LOOKER_PROJECT"
541 |       - "LOOKER_LOCATION=$_LOOKER_LOCATION"
542 |     secretEnv:
543 |       [
544 |         "CLIENT_ID",
545 |         "LOOKER_BASE_URL",
546 |         "LOOKER_CLIENT_ID",
547 |         "LOOKER_CLIENT_SECRET",
548 |       ]
549 |     volumes:
550 |       - name: "go"
551 |         path: "/gopath"
552 |     args:
553 |       - -c
554 |       - |
555 |         .ci/test_with_coverage.sh \
556 |           "Looker" \
557 |           looker \
558 |           looker
559 |     
560 |   - id: "mindsdb"
561 |     name: golang:1
562 |     waitFor: ["compile-test-binary"]
563 |     entrypoint: /bin/bash
564 |     env:
565 |       - "GOPATH=/gopath"
566 |       - "MINDSDB_PORT=$_MINDSDB_PORT"
567 |       - "MINDSDB_DATABASE=$_MINDSDB_DATABASE"
568 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
569 |     secretEnv: ["MINDSDB_HOST", "MINDSDB_USER", "MINDSDB_PASS", "CLIENT_ID"]
570 |     volumes:
571 |       - name: "go"
572 |         path: "/gopath"
573 |     args:
574 |       - -c
575 |       - |
576 |         .ci/test_with_coverage.sh \
577 |           "MindsDB" \
578 |           mindsdb \
579 |           mindsdb
580 | 
581 |   - id: "cloud-sql"
582 |     name: golang:1
583 |     waitFor: ["compile-test-binary"]
584 |     entrypoint: /bin/bash
585 |     env:
586 |       - "GOPATH=/gopath"
587 |     secretEnv: ["CLIENT_ID"]
588 |     volumes:
589 |       - name: "go"
590 |         path: "/gopath"
591 |     args:
592 |       - -c
593 |       - |
594 |         .ci/test_with_coverage.sh \
595 |           "Cloud SQL Wait for Operation" \
596 |           cloudsql \
597 |           cloudsql
598 | 
599 |   - id: "tidb"
600 |     name: golang:1
601 |     waitFor: ["compile-test-binary"]
602 |     entrypoint: /bin/bash
603 |     env:
604 |       - "GOPATH=/gopath"
605 |       - "TIDB_DATABASE=$_DATABASE_NAME"
606 |       - "TIDB_HOST=$_TIDB_HOST"
607 |       - "TIDB_PORT=$_TIDB_PORT"
608 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
609 |     secretEnv: ["CLIENT_ID", "TIDB_USER", "TIDB_PASS"]
610 |     volumes:
611 |       - name: "go"
612 |         path: "/gopath"
613 |     args:
614 |       - -c
615 |       - |
616 |         .ci/test_with_coverage.sh \
617 |           "TiDB" \
618 |           tidb \
619 |           tidbsql tidbexecutesql
620 | 
621 |   - id: "firebird"
622 |     name: golang:1
623 |     waitFor: ["compile-test-binary"]
624 |     entrypoint: /bin/bash
625 |     env:
626 |       - "GOPATH=/gopath"
627 |       - "FIREBIRD_DATABASE=$_FIREBIRD_DATABASE_NAME"
628 |       - "FIREBIRD_HOST=$_FIREBIRD_HOST"
629 |       - "FIREBIRD_PORT=$_FIREBIRD_PORT"
630 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
631 |     secretEnv: ["CLIENT_ID", "FIREBIRD_USER", "FIREBIRD_PASS"]
632 |     volumes:
633 |       - name: "go"
634 |         path: "/gopath"
635 |     args:
636 |       - -c
637 |       - |
638 |         .ci/test_with_coverage.sh \
639 |           "Firebird" \
640 |           firebird \
641 |           firebirdsql firebirdexecutesql
642 | 
643 |   - id: "clickhouse"
644 |     name: golang:1
645 |     waitFor: ["compile-test-binary"]
646 |     entrypoint: /bin/bash
647 |     env:
648 |       - "GOPATH=/gopath"
649 |       - "CLICKHOUSE_DATABASE=$_CLICKHOUSE_DATABASE"
650 |       - "CLICKHOUSE_PORT=$_CLICKHOUSE_PORT"
651 |       - "CLICKHOUSE_PROTOCOL=$_CLICKHOUSE_PROTOCOL"
652 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
653 |     secretEnv: ["CLICKHOUSE_HOST", "CLICKHOUSE_USER", "CLIENT_ID"]
654 |     volumes:
655 |       - name: "go"
656 |         path: "/gopath"
657 |     args:
658 |       - -c
659 |       - |
660 |         .ci/test_with_coverage.sh \
661 |           "ClickHouse" \
662 |           clickhouse \
663 |           clickhouse
664 | 
665 |   - id: "trino"
666 |     name: golang:1
667 |     waitFor: ["compile-test-binary"]
668 |     entrypoint: /bin/bash
669 |     env:
670 |       - "GOPATH=/gopath"
671 |       - "TRINO_HOST=$_TRINO_HOST"
672 |       - "TRINO_PORT=$_TRINO_PORT"
673 |       - "TRINO_CATALOG=$_TRINO_CATALOG"
674 |       - "TRINO_SCHEMA=$_TRINO_SCHEMA"
675 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
676 |     secretEnv: ["CLIENT_ID", "TRINO_USER"]
677 |     volumes:
678 |       - name: "go"
679 |         path: "/gopath"
680 |     args:
681 |       - -c
682 |       - |
683 |         .ci/test_with_coverage.sh \
684 |           "Trino" \
685 |           trino \
686 |           trinosql trinoexecutesql
687 | 
688 |   - id: "yugabytedb"
689 |     name: golang:1
690 |     waitFor: ["compile-test-binary"]
691 |     entrypoint: /bin/bash
692 |     env:
693 |       - "GOPATH=/gopath"
694 |       - "YUGABYTEDB_DATABASE=$_YUGABYTEDB_DATABASE"
695 |       - "YUGABYTEDB_PORT=$_YUGABYTEDB_PORT"
696 |       - "YUGABYTEDB_LOADBALANCE=$_YUGABYTEDB_LOADBALANCE"
697 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
698 |     secretEnv:
699 |       ["YUGABYTEDB_USER", "YUGABYTEDB_PASS", "YUGABYTEDB_HOST", "CLIENT_ID"]
700 |     volumes:
701 |       - name: "go"
702 |         path: "/gopath"
703 |     args:
704 |       - -c
705 |       - |
706 |         ./yugabytedb.test -test.v
707 | 
708 |   - id: "elasticsearch"
709 |     name: golang:1
710 |     waitFor: ["compile-test-binary"]
711 |     entrypoint: /bin/bash
712 |     env:
713 |       - "GOPATH=/gopath"
714 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
715 |     secretEnv: ["CLIENT_ID", "ELASTICSEARCH_USER", "ELASTICSEARCH_PASS", "ELASTICSEARCH_HOST"]
716 |     volumes:
717 |       - name: "go"
718 |         path: "/gopath"
719 |     args:
720 |       - -c
721 |       - |
722 |         .ci/test_with_coverage.sh \
723 |           "Elasticsearch" \
724 |           elasticsearch \
725 |           elasticsearch
726 | 
727 | 
728 |   - id: "cassandra"
729 |     name: golang:1
730 |     waitFor: ["compile-test-binary"]
731 |     entrypoint: /bin/bash
732 |     env:
733 |       - "GOPATH=/gopath"
734 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
735 |     secretEnv: ["CLIENT_ID", "CASSANDRA_USER", "CASSANDRA_PASS", "CASSANDRA_HOST"]
736 |     volumes:
737 |       - name: "go"
738 |         path: "/gopath"
739 |     args:
740 |       - -c
741 |       - |
742 |         .ci/test_with_coverage.sh \
743 |           "Cassandra" \
744 |           cassandra \
745 |           cassandra
746 | 
747 |   - id: "oracle"
748 |     name: golang:1
749 |     waitFor: ["compile-test-binary"]
750 |     entrypoint: /bin/bash
751 |     env:
752 |       - "GOPATH=/gopath"
753 |       - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
754 |       - "ORACLE_SERVER_NAME=$_ORACLE_SERVER_NAME"
755 |     secretEnv: ["CLIENT_ID", "ORACLE_USER", "ORACLE_PASS", "ORACLE_HOST"]
756 |     volumes:
757 |       - name: "go"
758 |         path: "/gopath"
759 |     args:
760 |       - -c
761 |       - |
762 |         .ci/test_with_coverage.sh \
763 |           "Oracle" \
764 |           oracle \
765 |           oracle
766 | 
767 |   - id: "serverless-spark"
768 |     name: golang:1
769 |     waitFor: ["compile-test-binary"]
770 |     entrypoint: /bin/bash
771 |     env:
772 |       - "GOPATH=/gopath"
773 |       - "SERVERLESS_SPARK_LOCATION=$_REGION"
774 |       - "SERVERLESS_SPARK_PROJECT=$PROJECT_ID"
775 |       - "SERVERLESS_SPARK_SERVICE_ACCOUNT=$SERVICE_ACCOUNT_EMAIL"
776 |     secretEnv: ["CLIENT_ID"]
777 |     volumes:
778 |       - name: "go"
779 |         path: "/gopath"
780 |     args:
781 |       - -c
782 |       - |
783 |         .ci/test_with_coverage.sh \
784 |           "Serverless Spark" \
785 |           serverlessspark
786 | 
787 | availableSecrets:
788 |   secretManager:
789 |     - versionName: projects/$PROJECT_ID/secrets/cloud_sql_pg_user/versions/latest
790 |       env: CLOUD_SQL_POSTGRES_USER
791 |     - versionName: projects/$PROJECT_ID/secrets/cloud_sql_pg_pass/versions/latest
792 |       env: CLOUD_SQL_POSTGRES_PASS
793 |     - versionName: projects/$PROJECT_ID/secrets/alloydb_pg_user/versions/latest
794 |       env: ALLOYDB_POSTGRES_USER
795 |     - versionName: projects/$PROJECT_ID/secrets/alloydb_pg_pass/versions/latest
796 |       env: ALLOYDB_POSTGRES_PASS
797 |     - versionName: projects/$PROJECT_ID/secrets/alloydb_ai_nl_user/versions/latest
798 |       env: ALLOYDB_AI_NL_USER
799 |     - versionName: projects/$PROJECT_ID/secrets/alloydb_ai_nl_pass/versions/latest
800 |       env: ALLOYDB_AI_NL_PASS
801 |     - versionName: projects/$PROJECT_ID/secrets/postgres_user/versions/latest
802 |       env: POSTGRES_USER
803 |     - versionName: projects/$PROJECT_ID/secrets/postgres_pass/versions/latest
804 |       env: POSTGRES_PASS
805 |     - versionName: projects/$PROJECT_ID/secrets/client_id/versions/latest
806 |       env: CLIENT_ID
807 |     - versionName: projects/$PROJECT_ID/secrets/neo4j_user/versions/latest
808 |       env: NEO4J_USER
809 |     - versionName: projects/$PROJECT_ID/secrets/neo4j_pass/versions/latest
810 |       env: NEO4J_PASS
811 |     - versionName: projects/$PROJECT_ID/secrets/cloud_sql_mssql_user/versions/latest
812 |       env: CLOUD_SQL_MSSQL_USER
813 |     - versionName: projects/$PROJECT_ID/secrets/cloud_sql_mssql_pass/versions/latest
814 |       env: CLOUD_SQL_MSSQL_PASS
815 |     - versionName: projects/$PROJECT_ID/secrets/cloud_sql_mysql_user/versions/latest
816 |       env: CLOUD_SQL_MYSQL_USER
817 |     - versionName: projects/$PROJECT_ID/secrets/cloud_sql_mysql_pass/versions/latest
818 |       env: CLOUD_SQL_MYSQL_PASS
819 |     - versionName: projects/$PROJECT_ID/secrets/mysql_user/versions/latest
820 |       env: MYSQL_USER
821 |     - versionName: projects/$PROJECT_ID/secrets/mysql_pass/versions/latest
822 |       env: MYSQL_PASS
823 |     - versionName: projects/$PROJECT_ID/secrets/mssql_user/versions/latest
824 |       env: MSSQL_USER
825 |     - versionName: projects/$PROJECT_ID/secrets/mssql_pass/versions/latest
826 |       env: MSSQL_PASS
827 |     - versionName: projects/$PROJECT_ID/secrets/couchbase_connection/versions/latest
828 |       env: COUCHBASE_CONNECTION
829 |     - versionName: projects/$PROJECT_ID/secrets/couchbase_user/versions/latest
830 |       env: COUCHBASE_USER
831 |     - versionName: projects/$PROJECT_ID/secrets/couchbase_pass/versions/latest
832 |       env: COUCHBASE_PASS
833 |     - versionName: projects/$PROJECT_ID/secrets/memorystore_redis_address/versions/latest
834 |       env: REDIS_ADDRESS
835 |     - versionName: projects/$PROJECT_ID/secrets/memorystore_redis_pass/versions/latest
836 |       env: REDIS_PASS
837 |     - versionName: projects/$PROJECT_ID/secrets/memorystore_valkey_address/versions/latest
838 |       env: VALKEY_ADDRESS
839 |     - versionName: projects/$PROJECT_ID/secrets/looker_base_url/versions/latest
840 |       env: LOOKER_BASE_URL
841 |     - versionName: projects/$PROJECT_ID/secrets/looker_client_id/versions/latest
842 |       env: LOOKER_CLIENT_ID
843 |     - versionName: projects/$PROJECT_ID/secrets/looker_client_secret/versions/latest
844 |       env: LOOKER_CLIENT_SECRET
845 |     - versionName: projects/$PROJECT_ID/secrets/tidb_user/versions/latest
846 |       env: TIDB_USER
847 |     - versionName: projects/$PROJECT_ID/secrets/tidb_pass/versions/latest
848 |       env: TIDB_PASS
849 |     - versionName: projects/$PROJECT_ID/secrets/clickhouse_host/versions/latest
850 |       env: CLICKHOUSE_HOST
851 |     - versionName: projects/$PROJECT_ID/secrets/clickhouse_user/versions/latest
852 |       env: CLICKHOUSE_USER
853 |     - versionName: projects/$PROJECT_ID/secrets/firebird_user/versions/latest
854 |       env: FIREBIRD_USER
855 |     - versionName: projects/$PROJECT_ID/secrets/firebird_pass/versions/latest
856 |       env: FIREBIRD_PASS
857 |     - versionName: projects/$PROJECT_ID/secrets/trino_user/versions/latest
858 |       env: TRINO_USER
859 |     - versionName: projects/$PROJECT_ID/secrets/oceanbase_host/versions/latest
860 |       env: OCEANBASE_HOST
861 |     - versionName: projects/$PROJECT_ID/secrets/oceanbase_user/versions/latest
862 |       env: OCEANBASE_USER
863 |     - versionName: projects/$PROJECT_ID/secrets/oceanbase_pass/versions/latest
864 |       env: OCEANBASE_PASSWORD
865 |     - versionName: projects/$PROJECT_ID/secrets/mindsdb_host/versions/latest
866 |       env: MINDSDB_HOST
867 |     - versionName: projects/$PROJECT_ID/secrets/mindsdb_user/versions/latest
868 |       env: MINDSDB_USER
869 |     - versionName: projects/$PROJECT_ID/secrets/mindsdb_pass/versions/latest
870 |       env: MINDSDB_PASS
871 |     - versionName: projects/$PROJECT_ID/secrets/yugabytedb_host/versions/latest
872 |       env: YUGABYTEDB_HOST
873 |     - versionName: projects/$PROJECT_ID/secrets/yugabytedb_user/versions/latest
874 |       env: YUGABYTEDB_USER
875 |     - versionName: projects/$PROJECT_ID/secrets/yugabytedb_pass/versions/latest
876 |       env: YUGABYTEDB_PASS
877 |     - versionName: projects/$PROJECT_ID/secrets/elastic_search_host/versions/latest
878 |       env: ELASTICSEARCH_HOST
879 |     - versionName: projects/$PROJECT_ID/secrets/elastic_search_user/versions/latest
880 |       env: ELASTICSEARCH_USER
881 |     - versionName: projects/$PROJECT_ID/secrets/elastic_search_pass/versions/latest
882 |       env: ELASTICSEARCH_PASS
883 |     - versionName: projects/$PROJECT_ID/secrets/cassandra_user/versions/latest
884 |       env: CASSANDRA_USER
885 |     - versionName: projects/$PROJECT_ID/secrets/cassandra_pass/versions/latest
886 |       env: CASSANDRA_PASS
887 |     - versionName: projects/$PROJECT_ID/secrets/cassandra_host/versions/latest
888 |       env: CASSANDRA_HOST
889 |     - versionName: projects/$PROJECT_ID/secrets/oracle_user/versions/latest
890 |       env: ORACLE_USER
891 |     - versionName: projects/$PROJECT_ID/secrets/oracle_pass/versions/latest
892 |       env: ORACLE_PASS
893 |     - versionName: projects/$PROJECT_ID/secrets/oracle_host/versions/latest
894 |       env: ORACLE_HOST
895 | 
896 | options:
897 |   logging: CLOUD_LOGGING_ONLY
898 |   automapSubstitutions: true
899 |   substitutionOption: "ALLOW_LOOSE"
900 |   dynamicSubstitutions: true
901 |   pool:
902 |     name: projects/$PROJECT_ID/locations/us-central1/workerPools/integration-testing # Necessary for VPC network connection
903 | 
904 | substitutions:
905 |   _DATABASE_NAME: test_database
906 |   _AI_NL_DATABASE_NAME: ainl_update_testing
907 |   _FIREBIRD_DATABASE_NAME: /firebird/test_database.fdb
908 |   _REGION: "us-central1"
909 |   _CLOUD_SQL_POSTGRES_INSTANCE: "cloud-sql-pg-testing"
910 |   _ALLOYDB_POSTGRES_CLUSTER: "alloydb-pg-testing"
911 |   _ALLOYDB_POSTGRES_INSTANCE: "alloydb-pg-testing-instance"
912 |   _ALLOYDB_AI_NL_CLUSTER: "alloydb-ai-nl-testing"
913 |   _ALLOYDB_AI_NL_INSTANCE: "alloydb-ai-nl-testing-instance"
914 |   _BIGTABLE_INSTANCE: "bigtable-testing-instance"
915 |   _POSTGRES_HOST: 127.0.0.1
916 |   _POSTGRES_PORT: "5432"
917 |   _SPANNER_INSTANCE: "spanner-testing"
918 |   _NEO4J_DATABASE: "neo4j"
919 |   _CLOUD_SQL_MSSQL_INSTANCE: "cloud-sql-mssql-testing"
920 |   _CLOUD_SQL_MYSQL_INSTANCE: "cloud-sql-mysql-testing"
921 |   _MYSQL_HOST: 127.0.0.1
922 |   _MYSQL_PORT: "3306"
923 |   _MSSQL_HOST: 127.0.0.1
924 |   _MSSQL_PORT: "1433"
925 |   _DGRAPHURL: "https://play.dgraph.io"
926 |   _COUCHBASE_BUCKET: "couchbase-bucket"
927 |   _COUCHBASE_SCOPE: "couchbase-scope"
928 |   _LOOKER_LOCATION: "us"
929 |   _LOOKER_PROJECT: "149671255749"
930 |   _LOOKER_VERIFY_SSL: "true"
931 |   _TIDB_HOST: 127.0.0.1
932 |   _TIDB_PORT: "4000"
933 |   _CLICKHOUSE_DATABASE: "default"
934 |   _CLICKHOUSE_PORT: "8123"
935 |   _CLICKHOUSE_PROTOCOL: "http"
936 |   _FIREBIRD_HOST: 127.0.0.1
937 |   _FIREBIRD_PORT: "3050"
938 |   _TRINO_HOST: 127.0.0.1
939 |   _TRINO_PORT: "8080"
940 |   _TRINO_CATALOG: "memory"
941 |   _TRINO_SCHEMA: "default"
942 |   _OCEANBASE_PORT: "2883"
943 |   _OCEANBASE_DATABASE: "oceanbase"
944 |   _MINDSDB_PORT: "47335"
945 |   _MINDSDB_DATABASE: "mindsdb_test"
946 |   _YUGABYTEDB_DATABASE: "yugabyte"
947 |   _YUGABYTEDB_PORT: "5433"
948 |   _YUGABYTEDB_LOADBALANCE: "false"
949 |   _ORACLE_SERVER_NAME: "FREEPDB1"
950 | 
```
Page 41/53FirstPrevNextLast