README
¶
spanner-mycli
My personal fork of spanner-cli, interactive command line tool for Cloud Spanner.
Description
spanner-mycli is an interactive command line tool for Google Cloud Spanner.
You can control your Spanner databases with idiomatic SQL commands.
Differences from original spanner-cli
spanner-mycli was forked from spanner-cli v0.10.6 and restarted its version numbering from v0.1.0. There are differences between spanner-mycli and spanner-cli that include not only functionality but also philosophical differences.
- Advanced query plan features for constrained display environments and comprehensive analysis. See docs/query_plan.md for details.
- Configurable
EXPLAIN ANALYZEwith customizable execution stats columns usingCLI_ANALYZE_COLUMNSand inline stats usingCLI_INLINE_STATS - Configurable query plan appendix presets and sections with
EXPLAIN PRINT=<preset-or-sections>andCLI_EXPLAIN_PRINT_SECTIONS - Query plan investigation with
EXPLAIN [ANALYZE] LAST QUERYfor re-rendering without re-execution andSHOW PLAN NODEfor inspecting specific plan nodes - Compact format (
FORMAT=COMPACT) and wrapped plans (WIDTH=<width>) with hanging indent for limited display spaces like narrow terminals, code blocks, and technical documentation - Query plan linter (EARLY EXPERIMENTAL) using
CLI_LINT_PLANsystem variable for heuristic query plan analysis - Query profiles (EARLY EXPERIMENTAL) for rendering sampled query plans using
SHOW QUERY PROFILESandSHOW QUERY PROFILE
- Configurable
- Respects my minor use cases
- Protocol Buffers support as
SHOW LOCAL PROTO,SHOW REMOTE PROTO,SYNC PROTO BUNDLEstatement - Can use embedded runtime backends (
--embedded-emulator,--embedded-omni) - Support query parameters
- Test root-partitionable with
TRY PARTITIONED QUERY <sql>command - Experimental Partitioned Query and Data Boost support.
- GenAI support(
GEMINIstatement). - BigQuery support (
BIGQUERYstatement). - Interactive DDL batching
- Async DDL execution support (
--asyncflag andCLI_ASYNC_DDLsystem variable) - Experimental Cassandra interface support as
CQL <cql>statement. - Support split points.
- Run as MCP (Model Context Protocol) server (EXPERIMENTAL,
--mcp). See Model Context Protocol for more information.- Statement calls are serialized. Calls cancelled while waiting do not execute; cancellation after execution starts does not guarantee rollback.
- Protocol Buffers support as
- Respects training and verification use-cases.
- gRPC logging(
--log-grpc) - Support mutations
- gRPC logging(
- Respects batch use cases as well as interactive use cases
- Breaking change from spanner-cli: Default output format for batch mode is
TABLE(same as interactive mode), notTAB. Use--format=TABfor tab-separated output.
- Breaking change from spanner-cli: Default output format for batch mode is
- More
gcloud spanner databases execute-sqlcompatibilities- Support compatible flags (
--sql,--query-mode,--strong,--read-timestamp,--timeout)
- Support compatible flags (
- More
gcloud spanner databases ddl updatecompatibilities- Support
--proto-descriptor-fileflag
- Support
- More Google Cloud Spanner CLI (
gcloud alpha spanner cli) compatibilities- Support
--skip-column-namesflag to suppress column headers in output (useful for scripting) - Support
--hostand--portflags as first-class options - Support
--deployment-endpointas an alias for--endpoint - Support
--html,--xml, and--csvoutput format options with proper escaping (security-enhanced compared to reference implementation) - Support
--format=jsonlfor type-aware JSON Lines output: INT64/ENUM as numbers, BOOL as booleans, ARRAY as JSON arrays, STRUCT as JSON objects, NULL as null
- Support
- Generalized concepts to extend without a lot of original syntax
- Generalized system variables concept inspired by Spanner JDBC properties
SET <name> = <value>statementSET LOCAL <name> = <value>statement (transaction-scoped; the value reverts when the transaction ends)SHOW VARIABLESstatementSHOW VARIABLE <name>statement--set <name>=<value>flag
- Generalized system variables concept inspired by Spanner JDBC properties
- Improved interactive experience
- Use
hymkor/go-multiline-nyinstead ofchzyer/readline"- Native multi-line editing
- Improved prompt
- Use
%for prompt expansion, instead of\to avoid escaping - Allow newlines in prompt using
%n - System variables expansion
- Prompt2 with margin and waiting status
- Use
- Autowrap and auto adjust column width to fit within terminal width (overridable with
CLI_FIXED_WIDTH) whenCLI_AUTOWRAP = TRUE. Pluggable width strategies viaCLI_WIDTH_STRATEGY(GREEDY_FREQUENCY,PROPORTIONAL,MARGINAL_COST). - Pager support when
CLI_USE_PAGER = TRUE - Progress bar of DDL execution.
- Syntax highlight when
CLI_ENABLE_HIGHLIGHT = TRUE - Fuzzy finder (default
Ctrl+T, configurable viaCLI_FUZZY_FINDER_KEY) powered by fzf for databases, tables, system variables, database roles, operations, and statement names
- Use
- Utilize other libraries
- Dogfooding
cloudspannerecosystem/memefish- Spin out memefish logic as
apstndb/gsqlutils.
- Spin out memefish logic as
- Utilize
apstndb/spantypeandapstndb/spanvalue
- Dogfooding
Disclaimer
Do not use this tool for production databases as the tool is experimental/alpha quality forever.
Version policy
This software will not have a stable release. In other words, v1.0.0 will never be released. It will be operated as a kind of ZeroVer.
v0.X.Y will be operated as follows:
- The initial release version is v0.1.0, forked from spanner-cli v0.10.6.
- The patch version Y will be incremented for changes that include only bug fixes.
- The minor version X will always be incremented when there are new features or changes related to compatibility.
- As a general rule, unreleased updates to the main branch will be released within one week.
Install
Pre-built binaries
Download pre-built binaries from GitHub Releases.
Build from source
Install Go and run the following command.
# Requires Go 1.26+
go install github.com/apstndb/spanner-mycli@latest
Container image
Use the container image from GitHub Container Registry.
https://github.com/apstndb/spanner-mycli/pkgs/container/spanner-mycli
Usage
Usage: spanner-mycli [flags]
Flags:
-p, --project=STRING (required) GCP Project ID ($SPANNER_PROJECT_ID).
-i, --instance=STRING (required) Cloud Spanner Instance ID ($SPANNER_INSTANCE_ID)
-d, --database=STRING Cloud Spanner Database ID. Optional when --detached is used
($SPANNER_DATABASE_ID).
--detached Start in detached mode, ignoring database env var/flag
-e, --execute=STRING Execute SQL statement and quit. --sql is an alias.
-f, --file=STRING Execute SQL statement from file and quit. --source is an alias.
-t, --table Display output in table format for batch mode.
--html Display output in HTML format.
--xml Display output in XML format.
--csv Display output in CSV format.
--format=STRING Output format (table, tab, tsv, vertical, html, xml, csv, jsonl)
-v, --verbose Display verbose output.
--credential=STRING Use the specific credential file
--prompt=PROMPT Set the prompt to the specified format (default: "spanner%t> ")
--prompt2=PROMPT2 Set the prompt2 to the specified format (default: "%P%R> ")
--history=HISTORY Set the history file to the specified path (default:
~/.spanner_mycli_history)
--priority=STRING Set default request priority (HIGH|MEDIUM|LOW)
--role=STRING Use the specific database role. --database-role is an alias.
--endpoint=STRING Set the Spanner API endpoint (host:port)
--host=STRING Host on which Spanner server is located
--port=INT Port number for Spanner connection
--directed-read=STRING Directed read option (replica_location:replica_type). The replica_type is
optional and either READ_ONLY or READ_WRITE
--set=KEY=VALUE Set system variables e.g. --set=name1=value1 --set=name2=value2
--param=KEY=VALUE Set query parameters, it can be literal or type(EXPLAIN/DESCRIBE only)
e.g. --param="p1='string_value'" --param=p2=FLOAT64
--proto-descriptor-file=STRING Path of a file that contains a protobuf-serialized
google.protobuf.FileDescriptorSet message.
--insecure Skip TLS verification and permit plaintext gRPC. --skip-tls-verify is an
alias.
--embedded-emulator Use embedded Cloud Spanner Emulator. --project, --instance, --database,
--endpoint, --insecure will be automatically configured.
--embedded-omni Use embedded experimental Spanner Omni. --project, --instance,
--database, --endpoint, --insecure will be automatically configured.
--emulator-image=STRING container image for embedded runtime (--embedded-emulator or
--embedded-omni)
--emulator-platform=STRING Container platform (e.g. linux/amd64, linux/arm64) for embedded runtime
--sample-database=STRING Initialize embedded runtime with built-in sample (e.g. fingraph,
singers, banking) or path to a metadata file (.json, .yaml, .yml).
Requires --embedded-emulator or --embedded-omni. Cannot be combined with
--detached.
--list-samples List available sample databases and exit
--output-template=STRING Filepath of output template. (EXPERIMENTAL)
--log-level=STRING Set CLI log level (DEBUG, INFO, WARN, ERROR). INFO and DEBUG include
embedded runtime container lifecycle logs. SQL SET CLI_LOG_LEVEL does not
change those container logs.
--log-grpc Show gRPC logs
--query-mode=QUERY-MODE Mode in which the query must be processed. Allowed values: NORMAL, PLAN,
PROFILE, WITH_STATS, WITH_PLAN_AND_STATS.
--strong Perform a strong query.
--read-timestamp=STRING Perform a query at the given timestamp.
--database-dialect=DATABASE-DIALECT The SQL dialect of the Cloud Spanner Database. Allowed values:
POSTGRESQL, GOOGLE_STANDARD_SQL, DATABASE_DIALECT_UNSPECIFIED. Omit this
flag to leave it unset.
--impersonate-service-account=STRING Impersonate service account email
-h, --help Show this help message and exit.
--version Show version string.
--enable-partitioned-dml Partitioned DML as default (AUTOCOMMIT_DML_MODE=PARTITIONED_NON_ATOMIC)
--timeout=STRING Statement timeout (e.g., '10s', '5m', '1h'). Omit for 10m on ordinary
statements and 24h on partitioned DML.
--async Return immediately, without waiting for the operation in progress to
complete
--try-partition-query Test whether the query can be executed as partition query without
execution
--mcp Run as MCP server
--skip-system-command Do not allow system commands
--system-command=ON|OFF Enable or disable system commands (ON/OFF). Default: ON.
--tee=STRING Append a copy of output to the specified file (both screen and file)
-o, --output=STRING Redirect query/data output to file (overwrites existing file)
--skip-column-names Suppress column headers in output
--table-streaming="AUTO" Table streaming output mode: AUTO/FALSE buffer table output, TRUE streams
table output. Non-table formats always stream.
--color="AUTO" ANSI styling in output: AUTO (styled if TTY), TRUE (always styled),
FALSE (never styled)
-q, --quiet Suppress result lines like 'rows in set' for clean output
--vertexai-project=STRING Gemini Enterprise project override
--vertexai-model=VERTEXAI-MODEL Gemini model (default: gemini-3.7-flash)
--vertexai-location=VERTEXAI-LOCATION Gemini Enterprise location (default: global)
Authentication
Unless you specify a credential file with --credential, this tool uses Application Default Credentials as credential source to connect to Spanner databases.
Please make sure to prepare your credential by gcloud auth application-default login.
If you're running spanner-mycli in docker container on your local machine, you have to pass local credentials to the container with the following command.
docker run -it \
-e GOOGLE_APPLICATION_CREDENTIALS=/tmp/credentials.json \
-v $HOME/.config/gcloud/application_default_credentials.json:/tmp/credentials.json:ro \
spanner-mycli --help
$ docker run -it \
-v $HOME/.config/gcloud/application_default_credentials.json:/home/nonroot/.config/gcloud/application_default_credentials.json:ro \
ghcr.io/apstndb/spanner-mycli --help
Example
Interactive mode
$ spanner-mycli -p myproject -i myinstance -d mydb
Connected.
spanner> CREATE TABLE users (
-> id INT64 NOT NULL,
-> name STRING(16) NOT NULL,
-> active BOOL NOT NULL
-> ) PRIMARY KEY (id);
Query OK, 0 rows affected (30.60 sec)
spanner> SHOW TABLES;
+----------------+
| Tables_in_mydb |
+----------------+
| users |
+----------------+
1 rows in set (18.66 msecs)
spanner> INSERT INTO users (id, name, active) VALUES (1, "foo", true), (2, "bar", false);
Query OK, 2 rows affected (5.08 sec)
spanner> SELECT * FROM users ORDER BY id ASC;
+----+------+--------+
| id | name | active |
+----+------+--------+
| 1 | foo | true |
| 2 | bar | false |
+----+------+--------+
2 rows in set (3.09 msecs)
spanner> BEGIN;
Query OK, 0 rows affected (0.02 sec)
spanner(rw txn)> DELETE FROM users WHERE active = false;
Query OK, 1 rows affected (0.61 sec)
spanner(rw txn)> COMMIT;
Query OK, 0 rows affected (0.20 sec)
spanner> SELECT * FROM users ORDER BY id ASC;
+----+------+--------+
| id | name | active |
+----+------+--------+
| 1 | foo | true |
+----+------+--------+
1 rows in set (2.58 msecs)
spanner> DROP TABLE users;
Query OK, 0 rows affected (25.20 sec)
spanner> SHOW TABLES;
Empty set (2.02 msecs)
spanner> EXIT;
Bye
Batch mode
By passing SQL from standard input, spanner-mycli runs in batch mode.
$ echo 'SELECT * FROM users;' | spanner-mycli -p myproject -i myinstance -d mydb
+----+------+--------+
| id | name | active |
+----+------+--------+
| 1 | foo | true |
| 2 | bar | false |
+----+------+--------+
You can also pass SQL with command line option -e.
$ spanner-mycli -p myproject -i myinstance -d mydb -e 'SELECT * FROM users;'
+----+------+--------+
| id | name | active |
+----+------+--------+
| 1 | foo | true |
| 2 | bar | false |
+----+------+--------+
For tab-separated output (useful for scripting), use --format=TAB:
$ spanner-mycli -p myproject -i myinstance -d mydb -e 'SELECT * FROM users;' --format=TAB
id name active
1 foo true
2 bar false
TAB writes values as-is, so values containing tabs or newlines break the row/column structure.
Use --format=TSV for the same layout with lossless escaping: tab, newline, carriage return, and
backslash inside values are escaped as \t, \n, \r, and \\, guaranteeing one row per line
and one field per tab-separated column.
With --skip-column-names option, column headers are suppressed in output (useful for scripting).
$ spanner-mycli -p myproject -i myinstance -d mydb -e 'SELECT * FROM users;' --skip-column-names
1 foo true
2 bar false
# With table format
$ spanner-mycli -p myproject -i myinstance -d mydb -e 'SELECT * FROM users;' -t --skip-column-names
+---+-----+-------+
| 1 | foo | true |
| 2 | bar | false |
+---+-----+-------+
Timeout support
The --timeout flag allows you to set a timeout for SQL statement execution, compatible with gcloud spanner databases execute-sql behavior.
# Set 30 second timeout for queries
$ spanner-mycli --timeout 30s -p myproject -i myinstance -d mydb -e 'SELECT * FROM large_table;'
# Set 5 minute timeout for partitioned DML
$ spanner-mycli --timeout 5m --enable-partitioned-dml -p myproject -i myinstance -d mydb -e 'UPDATE large_table SET status = "active";'
# Use default timeout (10 minutes for queries, 24 hours for partitioned DML)
$ spanner-mycli -p myproject -i myinstance -d mydb -e 'SELECT * FROM users;'
You can also configure timeout interactively using the STATEMENT_TIMEOUT system variable:
spanner> SET STATEMENT_TIMEOUT = '2m';
Query OK, 0 rows affected (0.00 sec)
spanner> SHOW VARIABLE STATEMENT_TIMEOUT;
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| STATEMENT_TIMEOUT | 2m0s |
+-------------------+-------+
1 rows in set (0.00 sec)
Output logging and redirection
spanner-mycli provides two ways to capture output to files:
- Tee functionality: Append output to a file while still displaying it on the console (like the Unix
teecommand) - Output redirect: Send query and result output to a file while prompts, progress, and errors stay on their normal screen streams
Both features are available through command-line options and interactive meta-commands.
Tee output (both screen and file)
# Log all query results to a file while displaying on screen
$ spanner-mycli --tee output.log -p myproject -i myinstance -d mydb
# In batch mode with --tee
$ spanner-mycli --tee queries.log -p myproject -i myinstance -d mydb -e 'SELECT * FROM users;'
spanner> \T session.log -- Start tee to session.log (both screen and file)
spanner> SELECT * FROM users; -- This query and result will be logged and displayed
spanner> \t -- Stop tee
spanner> SELECT * FROM keys; -- This won't be logged (screen only)
spanner> \T another.log -- Start tee to a different file
Output redirect (query/data output to file)
# Redirect query/data output to file (overwrites existing file)
$ spanner-mycli --output backup.sql -p myproject -i myinstance -d mydb -e 'DUMP DATABASE;'
# Useful for clean SQL exports while leaving progress and errors on screen
$ spanner-mycli --output export.sql -p myproject -i myinstance -d mydb
spanner> \o backup.sql -- Redirect query/data output to file (overwrite if it exists)
spanner> DUMP DATABASE; -- SQL goes to file, progress shows on screen
spanner> \o -- Disable redirect (return to screen output)
spanner> SELECT * FROM users; -- This shows on screen only
-- Alternative using \O (symmetric with \T/\t pattern)
spanner> \o export.sql -- Redirect query/data output to file (overwrite if it exists)
spanner> SELECT * FROM keys; -- Output goes to file only
spanner> \O -- Disable redirect using \O
DUMP SCHEMA and DUMP DATABASE move inline foreign keys that reference
tables created later into ALTER TABLE ... ADD statements after the other DDL,
before any data. This allows an empty cyclic foreign-key schema to be restored
even when Spanner returns its constraints inside CREATE TABLE. If an affected
table definition cannot be parsed or safely rewritten, the dump fails before
emitting SQL. DUMP TABLES does not export or modify DDL. Populated cycles
involving enforced foreign keys or interleave relationships are rejected by
default. The opt-in CLI_DUMP_CYCLIC_MODE = 'MUTATE' mode emits one mutation
transaction per populated cyclic table group; it does not make the whole restore
atomic or predict service commit limits. See cyclic DUMP restoration
for usage, local buffering limits, and target prerequisites.
What gets logged
The tee file will contain:
- Query results and output
- SQL statements when
CLI_ECHO_INPUTis enabled - Error messages and warnings
- Result metadata (row counts, execution times)
The tee file will NOT contain:
- Interactive prompts (e.g.,
spanner>) - Progress indicators (e.g., DDL progress bars)
- Confirmation dialogs (e.g., DROP DATABASE confirmations)
- Readline input display
Features
- Dynamic control with meta-commands: Start and stop logging during the session
- File switching: Starting a new tee (with
\T) automatically closes the previous file - Quoted filenames: Supports filenames with spaces:
\T "my output.log" - Combine with --tee: Start with
--tee, use\tto pause, and\Tto resume
File handling
- Tee files use append mode;
--outputand\ooverwrite existing content - Files are created if they don't exist
- Only regular files are supported (not directories, FIFOs, or device files)
- Tee file write failures warn once and leave console output running
- File-only output preserves write errors instead of treating the failed file as optional. A failed export can leave a partial file; do not replay it as a complete dump.
- Result-display failures, including summaries and query-plan appendices, report an error. This does not undo a statement that already completed successfully.
# Example: Logging a session with CLI_ECHO_INPUT
$ spanner-mycli --tee session.log -p myproject -i myinstance -d mydb
Connected.
spanner> SET CLI_ECHO_INPUT = TRUE;
Query OK, 0 rows affected (0.00 sec)
spanner> SELECT 1 AS test;
# In session.log:
# SELECT 1 AS test;
# +------+
# | test |
# +------+
# | 1 |
# +------+
# 1 rows in set (2.41 msecs)
EXPLAIN
[!WARNING] The Cloud Spanner Emulator does not return query plans in PLAN mode (the field is absent in the API response). While the API itself succeeds and returns other metadata like row types,
EXPLAINwill error in spanner-mycli as it requires query plan data to produce meaningful output. See emulator limitations for details.
You can see query plan without query execution using the EXPLAIN client side statement.
For advanced query plan features and configuration options, see docs/query_plan.md.
spanner> EXPLAIN
SELECT SingerId, FirstName FROM Singers WHERE FirstName LIKE "A%";
+----+-------------------------------------------------------------------------------------------+
| ID | Query_Execution_Plan |
+----+-------------------------------------------------------------------------------------------+
| *0 | Distributed Union <Row> (distribution_table: indexOnSingers, split_ranges_aligned: false) |
| 1 | +- Local Distributed Union <Row> |
| 2 | +- Serialize Result <Row> |
| 3 | +- Filter Scan <Row> (seekable_key_size: 1) |
| *4 | +- Index Scan <Row> (Index: indexOnSingers, scan_method: Row) |
+----+-------------------------------------------------------------------------------------------+
Predicates(identified by ID):
0: Split Range: STARTS_WITH($FirstName, 'A')
4: Seek Condition: STARTS_WITH($FirstName, 'A')
5 rows in set (0.86 sec)
Note: <Row> or <Batch> after the operator name mean execution method of the operator node.
EXPLAIN ANALYZE
[!WARNING] The Cloud Spanner Emulator does not return query plans in PROFILE mode (the field is absent in the API response). While the API itself succeeds and returns other metadata like row types,
EXPLAIN ANALYZEwill error in spanner-mycli as it requires query plan data to produce meaningful output. See emulator limitations for details.
You can see query plan and execution profile using the EXPLAIN ANALYZE client side statement.
You should know that it requires executing the query.
For advanced query plan features and configuration options, see docs/query_plan.md.
spanner> EXPLAIN ANALYZE
SELECT SingerId, FirstName FROM Singers WHERE FirstName LIKE "A%";
+----+-------------------------------------------------------------------------------------------+---------------+------------+---------------+
| ID | Query_Execution_Plan | Rows_Returned | Executions | Total_Latency |
+----+-------------------------------------------------------------------------------------------+---------------+------------+---------------+
| *0 | Distributed Union <Row> (distribution_table: indexOnSingers, split_ranges_aligned: false) | 235 | 1 | 1.17 msecs |
| 1 | +- Local Distributed Union <Row> | 235 | 1 | 1.12 msecs |
| 2 | +- Serialize Result <Row> | 235 | 1 | 1.1 msecs |
| 3 | +- Filter Scan <Row> (seekable_key_size: 1) | 235 | 1 | 1.05 msecs |
| *4 | +- Index Scan <Row> (Index: indexOnSingers, scan_method: Row) | 235 | 1 | 1.02 msecs |
+----+-------------------------------------------------------------------------------------------+---------------+------------+---------------+
Predicates(identified by ID):
0: Split Range: STARTS_WITH($FirstName, 'A')
4: Seek Condition: STARTS_WITH($FirstName, 'A')
5 rows in set (4.49 msecs)
timestamp: 2025-04-16T01:07:59.137819+09:00
cpu time: 3.73 msecs
rows scanned: 235 rows
deleted rows scanned: 0 rows
optimizer version: 7
optimizer statistics: auto_20250413_15_34_23UTC
Directed reads mode
spanner-mycli now supports directed reads, a feature that allows you to read data from a specific replica of a Spanner database.
To use directed reads with spanner-mycli, you need to specify the --directed-read flag.
The --directed-read flag takes a single argument, which is the name of the replica that you want to read from.
The replica name can be specified in one of the following formats:
<replica_location><replica_location>:<replica_type>
The <replica_location> specifies the region where the replica is located such as us-central1, asia-northeast2.
The <replica_type> specifies the type of the replica either READ_WRITE or READ_ONLY.
$ spanner-mycli -p myproject -i myinstance -d mydb --directed-read us-central1
$ spanner-mycli -p myproject -i myinstance -d mydb --directed-read us-central1:READ_ONLY
$ spanner-mycli -p myproject -i myinstance -d mydb --directed-read asia-northeast2:READ_WRITE
Directed reads are only effective for single queries or queries within a read-only transaction. Please note that directed read options do not apply to queries within a read-write transaction.
[!NOTE] If you specify an incorrect region or type for directed reads, directed reads will not be enabled and your requsts won't be routed as expected. For example, in a multi-region configuration
nam3, if you mistypeus-east1asus-east-1, the connection will succeed, but directed reads will not be enabled.To perform directed reads to
asia-northeast2in a multi-region configurationasia1, you need to specifyasia-northeast2orasia-northeast2:READ_WRITE. Since the replicas placed inasia-northeast2are READ_WRITE replicas, directed reads will not be enabled if you specifyasia-northeast2:READ_ONLY.Please refer to the Spanner documentation to verify the valid configurations.
Client-Side Statement Syntax
spanner-mycli supports all Spanner GoogleSQL and Spanner Graph statements, as well as several client-side statements.
Note: If any valid Spanner statement can't be executed, it is a bug.
In the following syntax, we use <> for a placeholder, [] for an optional keyword,
and {A|B|...} for a mutually exclusive keyword.
- The syntax is case-insensitive.
| Usage | Syntax | Note | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Switch database | USE <database> [ROLE <role>]; |
The role you set is used for accessing with fine-grained access control. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Detach from database | DETACH; |
Switch to detached mode, disconnecting from the current database. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Drop database | DROP DATABASE <database>; |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List databases | SHOW DATABASES; |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Show DDL of the schema object | SHOW CREATE <type> <fqn>; |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List tables | SHOW TABLES [<schema>]; |
If schema is not provided, the default schema is used | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Show columns | SHOW COLUMNS FROM <table_fqn>; |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Show indexes | SHOW INDEX FROM <table_fqn>; |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SHOW DDLs | SHOW DDLS; |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Export database DDL and data as SQL statements | DUMP DATABASE; |
Exports DDL plus BASE TABLE data from the default schema and named schemas. Views and synonyms are omitted from data. Catalog, column, and row reads share one read-only transaction. Requires spanner.databases.getDdl; that admin RPC is a fresh GetDatabaseDdl call and is not timestamp-bound to the dump transaction. When the admin response includes proto descriptors, prepends SET PROTO_DESCRIPTORS before rewritten DDL. CLI_DUMP_CYCLIC_MODE defaults to REJECT for populated cyclic FK/interleave groups, including all-NULL or row-acyclic data. Opt-in MUTATE pre-encodes all cyclic groups before output, then emits one unsplit transaction per populated group. No service-quota prediction or globally atomic restore; earlier work may remain committed. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Export database DDL only as SQL statements | DUMP SCHEMA; |
Exports a fresh GetDatabaseDdl response as SQL. Requires spanner.databases.getDdl. When the admin response includes proto descriptors, prepends SET PROTO_DESCRIPTORS so CREATE PROTO BUNDLE replay is self-contained. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Export specific tables as SQL statements | DUMP TABLES <table1> [, <table2>, ...]; |
Table names are [.]. Data only; no constraint changes or implicit inclusion of other tables. Invalid names are rejected before GetDatabaseDdl. Requires spanner.databases.getDdl only when a selected interleaved child has another selected BASE TABLE whose name matches the catalog parent basename. CLI_DUMP_CYCLIC_MODE defaults to REJECT; opt-in MUTATE pre-encodes selected cyclic groups and emits one unsplit transaction per populated group. Omitted parents and prerequisite target rows remain caller responsibilities. No service-quota prediction; earlier restore work may remain committed.
Meta CommandsMeta commands are special commands that start with a backslash ( Note: Meta commands are only supported in interactive mode. They cannot be used in batch mode (with Supported Meta Commands
For detailed documentation on each meta command, see docs/meta_commands.md. Customize promptYou can customize the prompt by Escape sequences:
Example:
The default prompt is Prompt2
The default prompt2 is If you set only
Config fileThis tool supports a TOML configuration file called Example:
Configuration Precedence
Request PriorityYou can set request priority for command level or transaction level.
By default To set a priority for command line level, you can use To set a priority for transaction level, you can use Here are some examples for transaction-level priority.
Note that transaction-level priority takes precedence over command-level priority. Transaction Tags and Request TagsYou can set transaction tag using
Using with the Cloud Spanner EmulatorThis tool supports the Cloud Spanner Emulator via the
Using Regional Endpointsspanner-mycli supports connecting to regional endpoints for improved performance and reliability. You can specify a regional endpoint using either the
The
Note: Detached Modespanner-mycli supports a detached mode (admin operation only mode) that allows you to connect to a Cloud Spanner instance without initially connecting to a specific database. This is useful for performing instance-level administrative operations like creating or dropping databases. Starting in Detached ModeYou can start spanner-mycli in detached mode using the
Switching Between Database and Detached ModeYou can switch between databases and detached mode during an interactive session:
Database Parameter PriorityThe database connection follows this priority order:
Limitations in Detached ModeWhen in detached mode, you can only execute:
Database-specific operations will fail with an error message indicating that no database is connected. Notable features of spanner-mycliThis section describes some notable features of spanner-mycli, they are not appeared in original spanner-cli. System VariablesSpanner JDBC inspired variablesThey have almost same semantics with Spanner JDBC properties For how these and other connection properties map to the official Spanner drivers (Spanner JDBC and go-sql-spanner), including tracked and intentionally skipped deltas, see docs/spanner-driver-compatibility.md.
spanner-mycli original variables
Batch statementsDDL batchingYou can issue a batch DDL statements.
Note:
DMLYou can use batch DML.
Embedded Cloud Spanner Emulatorspanner-mycli can launch Cloud Spanner Emulator with empty database, powered by testcontainers.
Sample DatabasesYou can initialize the embedded runtime with Google's official sample databases using the
The sample databases include both embedded samples (fingraph, singers) and samples downloaded from Google Cloud Storage. You can also create custom samples using metadata files in JSON or YAML format.
Embedded Spanner Omnispanner-mycli can also launch experimental Spanner Omni via
Protocol Buffers supportYou can use
You can also use
(EXPERIMENTAL) It also supports non-compiled Comma-separated inputs are merged into one descriptor graph before validation,
both for
This feature is powered by bufbuild/protocompile. (EXPERIMENTAL)
|
| CLI_PARSE_MODE | Description |
|---|---|
| FALLBACK | Use memefish but fallback if error |
| NO_MEMEFISH | Don't use memefish |
| MEMEFISH_ONLY | Use memefish and don't fallback |
spanner> SET CLI_PARSE_MODE = "MEMEFISH_ONLY";
Empty set (0.00 sec)
spanner> SELECT * FRM 1;
ERROR: invalid statement: syntax error: :1:10: expected token: <eof>, but: <ident>
1: SELECT * FRM 1
^~~
spanner> SET CLI_PARSE_MODE = "FALLBACK";
Empty set (0.00 sec)
spanner> SELECT * FRM 1;
2024/11/02 00:22:57 ignore memefish parse error, err: syntax error: :1:10: expected token: <eof>, but: <ident>
1: SELECT * FRM 1
^~~
ERROR: spanner: code = "InvalidArgument", desc = "Syntax error: Expected end of input but got identifier \\\"FRM\\\" [at 1:10]\\nSELECT * FRM 1\\n ^"
spanner> SET CLI_PARSE_MODE = "NO_MEMEFISH";
Empty set (0.00 sec)
spanner> SELECT * FRM 1;
ERROR: spanner: code = "InvalidArgument", desc = "Syntax error: Expected end of input but got identifier \\\"FRM\\\" [at 1:10]\\nSELECT * FRM 1\\n ^"
Mutations support
spanner-mycli supports mutations.
Mutations are buffered in read-write transaction, or immediately commit outside explicit transaction.
Write mutations
MUTATE <table_fqn> {INSERT|UPDATE|REPLACE|INSERT_OR_UPDATE} {<struct_literal> | <array_of_struct_literal>};
Delete mutations
MUTATE <table_fqn> DELETE ALL;
MUTATE <table_fqn> DELETE {<tuple_struct_literal> | <array_of_tuple_struct_literal>};
MUTATE <table_fqn> DELETE KEY_RANGE({start_closed | start_open} => <tuple_struct_literal>,
{end_closed | end_open} => <tuple_struct_literal>);
Note: In this context, parenthesized expression and some simple literals are treated as a single field struct literal.
Examples of mutations
Example schema
CREATE TABLE MutationTest (PK INT64, Col INT64) PRIMARY KEY(PK);
CREATE TABLE MutationTest2 (PK1 INT64, PK2 STRING(MAX), Col INT64) PRIMARY KEY(PK1, PK2);
Insert a single row with key(1).
MUTATE MutationTest INSERT STRUCT(1 AS PK);
Insert or update four rows with keys(1, "foo", 1, "n", 1, "m", 50, "foobar").
MUTATE MutationTest2 INSERT_OR_UPDATE [STRUCT(1 AS PK1, "foo" AS PK2, 0 AS Col), (1, "n", 1), (1, "m", 3), (50, "foobar", 4)];
You can set commit timestamps using PENDING_COMMIT_TIMESTAMP().
spanner> MUTATE Performances INSERT_OR_UPDATE STRUCT(
1 AS SingerId, 1 AS VenueId,
DATE "2024-12-25" AS EventDate,
PENDING_COMMIT_TIMESTAMP() AS LastUpdateTime);
Query OK, 0 rows affected (1.18 sec)
timestamp: 2024-12-13T02:40:46.253698+09:00
mutation_count: 4
spanner> SELECT * FROM Performances;
+----------+---------+------------+---------+-----------------------------+
| SingerId | VenueId | EventDate | Revenue | LastUpdateTime |
| INT64 | INT64 | DATE | INT64 | TIMESTAMP |
+----------+---------+------------+---------+-----------------------------+
| 1 | 1 | 2024-12-25 | NULL | 2024-12-12T17:40:46.253698Z |
+----------+---------+------------+---------+-----------------------------+
1 rows in set (14.89 msecs)
timestamp: 2024-12-13T02:40:57.15133+09:00
cpu time: 13.35 msecs
rows scanned: 1 rows
deleted rows scanned: 0 rows
optimizer version: 7
optimizer statistics: auto_20241212_12_01_00UTC
Delete all rows in MutationTest table.
MUTATE MutationTest DELETE ALL;
Delete rows with PK (1) in MutationTest table.
MUTATE MutationTest DELETE (1);
Delete a single row with PK (1, "foo") in MutationTest2 table.
MUTATE MutationTest2 DELETE (1, "foo");
Delete two rows with PK (1, "foo", 2, "bar") in MutationTest2 table.
MUTATE MutationTest2 DELETE [(1, "foo"), (2, "bar")];
Delete rows between (1, "a") <= PK < (1, "n") in MutationTest2 table
MUTATE MutationTest2 DELETE KEY_RANGE(start_closed => (1, "a"), end_open => (1, "n"));
Query parameter support
Many Cloud Spanner clients don't support query parameters.
If you do not modify the query, you will not be able to execute queries that contain query parameters,
and you will not be able to view the query plan for queries with parameter types STRUCT or ARRAY.
spanner-mycli solves this problem by supporting query parameters.
You can define query parameters using command line option --param or SET commands.
It supports type notation or literal value notation in GoogleSQL.
Note: They are supported on the best effort basis, and type conversions are not supported.
Query parameters definition using --param option
$ spanner-mycli \
--param='array_type=ARRAY<STRUCT<FirstName STRING, LastName STRING>>' \
--param='array_value=[STRUCT("Marc" AS FirstName, "Richards" AS LastName), ("Catalina", "Smith")]'
You can see defined query parameters using SHOW PARAMS; command.
> SHOW PARAMS;
+-------------+------------+-------------------------------------------------------------------+
| Param_Name | Param_Kind | Param_Value |
+-------------+------------+-------------------------------------------------------------------+
| array_value | VALUE | [STRUCT("Marc" AS FirstName, "Richards" AS LastName), ("Catalina", "Smith")] |
| array_type | TYPE | ARRAY<STRUCT<FirstName STRING, LastName STRING>> |
+-------------+------------+-------------------------------------------------------------------+
Empty set (0.00 sec)
You can use value query parameters in any statement.
> SELECT * FROM Singers WHERE STRUCT(FirstName, LastName) IN UNNEST(@array_value);
+----------+-----------+----------+------------+------------+
| SingerId | FirstName | LastName | SingerInfo | BirthDate |
+----------+-----------+----------+------------+------------+
| 2 | Catalina | Smith | NULL | 1990-08-17 |
| 1 | Marc | Richards | NULL | 1970-09-03 |
+----------+-----------+----------+------------+------------+
2 rows in set (7.8 msecs)
You can use type query parameters only in EXPLAIN or DESCRIBE without value.
> EXPLAIN SELECT * FROM Singers WHERE STRUCT(FirstName, LastName) IN UNNEST(@array_type);
+-----+----------------------------------------------------------------------------------+
| ID | Query_Execution_Plan |
+-----+----------------------------------------------------------------------------------+
| 0 | Distributed Cross Apply |
| 1 | +- [Input] Create Batch |
| 2 | | +- Compute Struct |
| 3 | | +- Hash Aggregate |
| 4 | | +- Compute |
| 5 | | +- Array Unnest |
| 10 | | +- [Scalar] Array Subquery |
| 11 | | +- Array Unnest |
| 35 | +- [Map] Serialize Result |
| 36 | +- Cross Apply |
| 37 | +- [Input] Batch Scan (Batch: $v14, scan_method: Scalar) |
| 40 | +- [Map] Local Distributed Union |
| *41 | +- Filter Scan (seekable_key_size: 0) |
| 42 | +- Table Scan (Full scan: true, Table: Singers, scan_method: Scalar) |
+-----+----------------------------------------------------------------------------------+
Predicates(identified by ID):
41: Residual Condition: (($FirstName = $batched_v8) AND ($LastName = $batched_v9))
14 rows in set (0.18 sec)
> DESCRIBE SELECT * FROM Singers WHERE STRUCT(FirstName, LastName) IN UNNEST(@array_type);
+-------------+-------------+
| Column_Name | Column_Type |
+-------------+-------------+
| SingerId | INT64 |
| FirstName | STRING |
| LastName | STRING |
| SingerInfo | BYTES |
| BirthDate | DATE |
+-------------+-------------+
5 rows in set (0.17 sec)
Interactive definition of query parameters using SET commands
You can define type query parameters using SET PARAM param_name type; command.
> SET PARAM string_type STRING;
Empty set (0.00 sec)
> SHOW PARAMS;
+-------------+------------+-------------+
| Param_Name | Param_Kind | Param_Value |
+-------------+------------+-------------+
| string_type | TYPE | STRING |
+-------------+------------+-------------+
Empty set (0.00 sec)
You can define type query parameters using SET PARAM param_name = value; command.
> SET PARAM bytes_value = b"foo";
Empty set (0.00 sec)
> SHOW PARAMS;
+-------------+------------+-------------+
| Param_Name | Param_Kind | Param_Value |
+-------------+------------+-------------+
| bytes_value | VALUE | B"foo" |
+-------------+------------+-------------+
Empty set (0.00 sec)
Partition Queries
spanner-mycli have some partition queries functionality.
Test root-partitionable
You can test whether the query is root-partitionable using TRY PARTITIONED QUERY command.
spanner> TRY PARTITIONED QUERY SELECT * FROM Singers;
+--------------------+
| Root_Partitionable |
+--------------------+
| TRUE |
+--------------------+
1 rows in set (0.78 sec)
spanner> TRY PARTITIONED QUERY SELECT * FROM Singers ORDER BY SingerId;
ERROR: query can't be a partition query: rpc error: code = InvalidArgument desc = Query is not root partitionable since it does not have a DistributedUnion at the root. Please check the conditions for a query to be root-partitionable.
error details: name = Help desc = Conditions for a query to be root-partitionable. url = https://cloud.google.com/spanner/docs/reads#read_data_in_parallel
Run partitioned query (EXPERIMENTAL)
You can execute partitioned query using RUN PARTITIONED QUERY command.
spanner> RUN PARTITIONED QUERY SELECT * FROM Singers;
+----------+-----------+----------+------------+------------+
| SingerId | FirstName | LastName | SingerInfo | BirthDate |
+----------+-----------+----------+------------+------------+
| 1 | Marc | Richards | NULL | 1970-09-03 |
| 2 | Catalina | Smith | NULL | 1990-08-17 |
| 3 | Alice | Trentor | NULL | 1991-10-02 |
| 4 | Lea | Martin | NULL | 1991-11-09 |
| 5 | David | Lomond | NULL | 1977-01-29 |
+----------+-----------+----------+------------+------------+
5 rows in set from 3 partitions (1.40 sec)
Or you can use SET AUTO_PARTITION_MODE.
spanner> SET AUTO_PARTITION_MODE = TRUE;
Empty set (0.00 sec)
spanner> SELECT * FROM Singers;
+----------+-----------+----------+------------+------------+
| SingerId | FirstName | LastName | SingerInfo | BirthDate |
+----------+-----------+----------+------------+------------+
| 1 | Marc | Richards | NULL | 1970-09-03 |
| 2 | Catalina | Smith | NULL | 1990-08-17 |
| 3 | Alice | Trentor | NULL | 1991-10-02 |
| 4 | Lea | Martin | NULL | 1991-11-09 |
| 5 | David | Lomond | NULL | 1977-01-29 |
+----------+-----------+----------+------------+------------+
5 rows in set from 3 partitions (1.40 sec)
Note: Any stats are not available in partitioned query.
You can set session-level default isolation level and transaction-level isolation level.
spanner> SET DEFAULT_ISOLATION_LEVEL = "REPEATABLE_READ";
spanner> BEGIN ISOLATION LEVEL REPEATABLE READ;
You can enable Data Boost using DATA_BOOST_ENABLED.
spanner> SET DATA_BOOST_ENABLED = TRUE;
In default, the number of worker goroutines for partitioned query is the value of GOMAXPROCS.
You can change it using MAX_PARTITIONED_PARALLELISM.
spanner> SET MAX_PARTITIONED_PARALLELISM = 1;
Note: Partitioned queries do not support streaming output in the current implementation.
Show partition tokens.
You can show partition tokens using PARTITION command.
Note: spanner-mycli does not clean up batch read-only transactions, which may prevent resources from being freed until they time out.
spanner> PARTITION SELECT * FROM Singers;
+-------------------------------------------------------------------------------------------------+
| Partition_Token |
+-------------------------------------------------------------------------------------------------+
| QUw0MGxyRjJDZENocXc0TkZnR3NxVHN1QnFCMy1yWkxWZmlIdFhhc2U4T2lWOGJRVFhJRkgydU1URmZUb2dBLXVvZE1O... |
| QUw0MGxyRzhPdmZUR04xclFQVTJKQXlVMEJjZFBUWTV3NUFsT2x0VGxTNHZWSExsejQweXNUWUFtUXd5ZjBzWEhmQ0Fa... |
| QUw0MGxyR3paSk96WUNqdjFsQW9tc2UwOFFoNlA4SzhHUzNWQVltNzVlRHZxdjZpUmFVSFN2UmtBanozc0hEaE9Iem9x... |
+-------------------------------------------------------------------------------------------------+
3 rows in set (0.65 sec)
GenAI support
The GEMINI statement supports both Gemini Enterprise Agent Platform (the
Vertex AI backend) and the Gemini API. Gemini Enterprise is the default backend,
uses Application Default Credentials, and uses the connected Spanner project
(CLI_PROJECT) unless CLI_VERTEXAI_PROJECT or vertexai_project overrides it.
vertexai_project = "example-project"
To use the Gemini API instead, set GEMINI_API_KEY or GOOGLE_API_KEY in the
environment and select the backend in spanner-mycli. When both variables are
set, the Google Gen AI SDK uses GOOGLE_API_KEY:
SET CLI_GENAI_BACKEND = "GEMINI_API";
The canonical backend values are GEMINI_ENTERPRISE and GEMINI_API.
VERTEX_AI remains accepted as an alias for GEMINI_ENTERPRISE.
CLI_VERTEXAI_MODEL selects the model for either backend and defaults to
gemini-3.7-flash. CLI_GENAI_THINKING_LEVEL accepts UNSPECIFIED, MINIMAL,
LOW, MEDIUM, or HIGH. Its default is UNSPECIFIED, which omits the
thinking configuration and lets the selected model use its own default.
The GEMINI statement sends your prompt, the connected database DDL, and its
proto descriptors to the selected Google Gen AI service. Review your
organization's data-handling policy before using it with sensitive schemas.
Built-in Spanner reference docs are always available. Dynamic documentation
lookup via the Developer Knowledge API uses DEVELOPERKNOWLEDGE_API_KEY, or
falls back to GOOGLE_API_KEY. GEMINI_API_KEY is not used for Developer
Knowledge lookup.
The generated query is automatically filled in the prompt.
spanner> GEMINI "Generate query to show all table with table_schema concat by dot if table_schema is not empty string.";
+---------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Column | Value |
+---------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| text | SELECT IF(table_schema != '', CONCAT(table_schema, '.', table_name), table_name) AS table_name_with_schema FROM INFORMATION_SCHEMA.TABLES; |
| semanticDescription | This query retrieves all tables and their schemas, concatenating the schema name with the table name if a schema exists, effectively listing all tables with their schema na |
| | mes when applicable. |
| syntaxDescription | The query selects from the INFORMATION_SCHEMA.TABLES view. It uses IF() to check if table_schema is not empty, and if so, concatenates table_schema and table_name with a do |
| | t ('.'). Otherwise, it just returns the table_name. The result is aliased as table_name_with_schema. |
+---------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Empty set (5.72 sec)
spanner> SELECT IF(table_schema != '', CONCAT(table_schema, '.', table_name), table_name) AS table_name_with_schema FROM INFORMATION_SCHEMA.TABLES;
BigQuery support
spanner-mycli can execute BigQuery SQL with the BIGQUERY client-side statement.
Configure the target project with CLI_BIGQUERY_PROJECT (defaults to CLI_PROJECT when empty).
Optional job settings are available via CLI_BIGQUERY_LOCATION and CLI_BIGQUERY_MAX_BYTES_BILLED.
READONLY sessions allow BIGQUERY scripts only when every statement is a query (SELECT, WITH, or FROM-pipe / parenthesized query); mixed or unrecognized payloads are rejected locally before a BigQuery job is created.
spanner> SET CLI_BIGQUERY_PROJECT = 'my-gcp-project';
Empty set (0.00 sec)
spanner> BIGQUERY SELECT 1 AS n;
+-----+
| n |
| INT |
+-----+
| 1 |
+-----+
1 rows in set (0.42 sec)
Markdown output
spanner-mycli can emit input and output in Markdown.
TODO: More description
$ spanner-mycli -v --set 'CLI_FORMAT=TABLE_DETAIL_COMMENT' --set 'CLI_MARKDOWN_CODEBLOCK=TRUE' --set 'CLI_ECHO_INPUT=TRUE'
spanner> GRAPH FinGraph
-> MATCH (n:Account)
-> RETURN LABELS(n) AS labels, PROPERTY_NAMES(n) AS props, n.id;
```sql
GRAPH FinGraph
MATCH (n:Account)
RETURN LABELS(n) AS labels, PROPERTY_NAMES(n) AS props, n.id;
/*---------------+------------------------------------------+-------+
| labels | props | id |
| ARRAY<STRING> | ARRAY<STRING> | INT64 |
+---------------+------------------------------------------+-------+
| [Account] | [create_time, id, is_blocked, nick_name] | 7 |
| [Account] | [create_time, id, is_blocked, nick_name] | 16 |
| [Account] | [create_time, id, is_blocked, nick_name] | 20 |
+---------------+------------------------------------------+-------+
3 rows in set (6.26 msecs)
timestamp: 2025-02-09T23:28:26.088524+09:00
cpu time: 4.45 msecs
rows scanned: 3 rows
deleted rows scanned: 0 rows
optimizer version: 7
optimizer statistics: auto_20250207_09_19_31UTC
*/
```
Cassandra interface support
spanner-mycli can execute CQL statements of Cassandra interface with CQL client side statement.
# Spanner Cassandra interface doesn't support CQL DDL, so you need to write GoogleSQL DDL
spanner> CREATE TABLE users (
id INT64 OPTIONS (cassandra_type = 'int'),
active BOOL OPTIONS (cassandra_type = 'boolean'),
username STRING(MAX) OPTIONS (cassandra_type = 'text'),
) PRIMARY KEY (id);
Query OK, 0 rows affected (7.88 sec)
spanner> CQL INSERT INTO users (id, active, username) VALUES (1, TRUE, 'John Doe');
Empty set (0.54 sec)
spanner> CQL SELECT * FROM users WHERE id = 1;
+-----+---------+----------+
| id | active | username |
| int | boolean | varchar |
+-----+---------+----------+
| 1 | true | John Doe |
+-----+---------+----------+
1 rows in set (0.55 sec)
How to develop
Run unit tests.
$ make test
Note: It requires Docker because integration tests using testcontainers.
Or run test except integration tests.
$ make test-quick
Incompatibilities from spanner-cli
In principle, spanner-mycli accepts the same input as spanner-cli, but some compatibility is intentionally not maintained.
BEGIN RW TAG <tag>andBEGIN RO TAG <tag>are no longer supported.- Use
SET TRANSACTION_TAG = "<tag>"andSET STATEMENT_TAG = "<tag>". - Rationale: spanner-cli are broken. https://github.com/cloudspannerecosystem/spanner-cli/issues/132
- Use
<rfc3339_timestamp>inBEGIN RO <rfc3339_timestamp>must be quoted likeBEGIN RO "2025-01-01T00:00:00Z".- Rationale: Raw RFC3339 timestamp is not compatible with GoogleSQL lexical structure and memefish.
\Gis no longer supported.- Use
SET CLI_FORMAT = "VERTICAL". - Rationale:
\Gis not compatible with GoogleSQL lexical structure and memefish.
- Use
\is no longer used for prompt expansions.- Use
%instead. - Rationale:
%works consistently in prompts and TOML configuration files.
- Use
- The default format of
EXPLAINandEXPLAIN ANALYZEhas been changed.
Tab character handling
spanner-mycli expands tab characters to whitespaces.
Tab width can be configured using CLI_TAB_WIDTH system variable. (default: 4)
spanner> SET CLI_TAB_WIDTH = 2;
Empty set (0.00 sec)
spanner> SELECT "a\tb\tc\td\te\tf\t\n"||
-> "あ\tい\tう\tえ\tお\tか\t\n"||
-> "abc\tdef\tghi\tjkl\tmno\tpqr\t\n"||
-> "あい\tうえ\tおか\tきく\tけこ\tさし\t" AS s;
+--------------------------------------+
| s |
+--------------------------------------+
| a b c d e f |
| あ い う え お か |
| abc def ghi jkl mno pqr |
| あい うえ おか きく けこ さし |
+--------------------------------------+
1 rows in set (2.81 msecs)
spanner> SET CLI_TAB_WIDTH = 4;
Empty set (0.00 sec)
spanner> SELECT "a\tb\tc\td\te\tf\t\n"||
-> "あ\tい\tう\tえ\tお\tか\t\n"||
-> "abc\tdef\tghi\tjkl\tmno\tpqr\t\n"||
-> "あい\tうえ\tおか\tきく\tけこ\tさし\t" AS s;
+--------------------------------------------------+
| s |
+--------------------------------------------------+
| a b c d e f |
| あ い う え お か |
| abc def ghi jkl mno pqr |
| あい うえ おか きく けこ さし |
+--------------------------------------------------+
1 rows in set (2.31 msecs)
spanner> SET CLI_TAB_WIDTH = 8;
Empty set (0.00 sec)
spanner> SELECT "a\tb\tc\td\te\tf\t\n"||
-> "あ\tい\tう\tえ\tお\tか\t\n"||
-> "abc\tdef\tghi\tjkl\tmno\tpqr\t\n"||
-> "あい\tうえ\tおか\tきく\tけこ\tさし\t" AS s;
+--------------------------------------------------+
| s |
+--------------------------------------------------+
| a b c d e f |
| あ い う え お か |
| abc def ghi jkl mno pqr |
| あい うえ おか きく けこ さし |
+--------------------------------------------------+
1 rows in set (2.76 msecs)
Documentation
¶
There is no documentation for this package.
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
mycli/feature/all
Package all assembles the full set of optional features (issue #778) for the full spanner-mycli binary.
|
Package all assembles the full set of optional features (issue #778) for the full spanner-mycli binary. |
|
mycli/feature/bigquery
Package bigquery contributes the BIGQUERY statement family to spanner-mycli through the Feature registration seam (issue #778).
|
Package bigquery contributes the BIGQUERY statement family to spanner-mycli through the Feature registration seam (issue #778). |
|
mycli/feature/cql
Package cql contributes the CQL (Cassandra interface) statement family to spanner-mycli through the Feature registration seam (issue #778).
|
Package cql contributes the CQL (Cassandra interface) statement family to spanner-mycli through the Feature registration seam (issue #778). |
|
mycli/feature/llm
Package llm contributes the GEMINI statement family (LLM-assisted query composition) to spanner-mycli through the Feature registration seam (issue #778).
|
Package llm contributes the GEMINI statement family (LLM-assisted query composition) to spanner-mycli through the Feature registration seam (issue #778). |
|
mycli/streamio
StreamManager manages all I/O streams for the CLI.
|
StreamManager manages all I/O streams for the CLI. |
|
protostruct
Package protostruct supports operations on the protocol buffer Struct message.
|
Package protostruct supports operations on the protocol buffer Struct message. |