DEV Community

Cover image for A spanner-cli Contributor's Perspective on the Official Spanner CLI
apstndb
apstndb

Posted on • Originally published at zenn.dev

A spanner-cli Contributor's Perspective on the Official Spanner CLI

Note: This article is an AI-assisted English translation of my original Japanese article. As the author, I wanted to share these insights about the official Spanner CLI release with the international developer community. The translation was performed using Claude to ensure technical accuracy while making the content accessible to a broader audience.

The official Spanner CLI has been released in preview.
https://cloud.google.com/spanner/docs/release-notes

You can directly connect and interact with your Spanner database using the Spanner CLI, an interactive shell for Spanner that is built into the Google Cloud CLI. You can use the Spanner CLI to start an interactive session and automate SQL executions from the shell or an input file. This feature is available in Preview. For more information, see Spanner CLI quickstart.

I'll share my observations about what this is, from the perspective of apstndb, an unemployed technology enthusiast who is a contributor to the OSS spanner-cli and the author of its fork spanner-mycli. For more about spanner-mycli, please read Why I Forked spanner-cli: Introducing spanner-mycli (Japanese article).

Note: This article describes what can be observed about gcloud alpha spanner cli, which is still in Preview, as of June 25, 2025. Please note that specifications may change at any time.

Key Points of This Article

  • Official Spanner CLI is derived from OSS spanner-cli v0.10.6
  • Technical evidence discovered through binary analysis
  • Unique evolution including introduction of meta-commands
  • Expectations for a healthy ecosystem through coexistence of official and OSS versions

The Importance of Interactive Tools

Interactive clients have been and continue to be essential for SQL databases.

Interactive clients for SQL databases have been recognized as essential tools since the era of SEQUEL: A structured English query language, SQL's predecessor, which made multiple references to interactive systems.
All major RDBMSs without exception provide first-party interactive clients:

How about Spanner? The officially provided interfaces that users could directly use were:

The gcloud spanner subcommand is not an interactive tool. It's a command that can execute SQL one-off from Bash or PowerShell, or be incorporated into scripts:

gcloud spanner databases execute-sql example-db \
    --sql='SELECT SingerId, AlbumId, AlbumTitle FROM Albums'
Enter fullscreen mode Exit fullscreen mode

Also, Spanner Studio is operated from a browser and doesn't run standalone locally.
The execution plan display is not suitable for sharing and cannot be used for automation.

Thus, Spanner lacked what other RDBMSs naturally provided.
Therefore, after spanner-cli developed by current Googler Yuki Furuyama was donated to the Cloud Spanner Ecosystem organization, it became the de facto standard tool for Spanner.

However, since spanner-cli was not part of Google's product, there was a disconnect between Spanner Studio used in official documentation and spanner-cli used primarily by the user community, without official documentation references.

Official Spanner CLI

After years of this situation, the official Spanner CLI was suddenly released. This happened on the morning of June 25, 2025 (JST) as I write this article.

Installation

As of June 25, 2025, the official documentation states that running gcloud alpha spanner cli will automatically install it:

The Spanner CLI is available in the gcloud CLI. When you run the gcloud alpha spanner cli command for the first time, gcloud CLI automatically installs the Spanner CLI component.

However, you currently cannot use the official Spanner CLI as described.

$ gcloud alpha spanner cli --project ${SPANNER_PROJECT_ID} --instance ${SPANNER_INSTANCE_ID} ${SPANNER_DATABASE_ID}  
Pausing command execution:

This command requires the `spannercli` component to be installed. Would you like to install the `spannercli` component to continue command execution? (Y/n)?  y

ERROR: (gcloud.alpha.spanner.cli) The following components are unknown [spannercli].
Enter fullscreen mode Exit fullscreen mode

This is because the component it's trying to install called spannercli doesn't exist. What actually exists is spanner-cli.

$ gcloud components list | grep spanner

Your current Google Cloud CLI version is: 528.0.0
The latest available version is: 528.0.0

To install or remove components at your current SDK version [528.0.0], run:
  $ gcloud components install COMPONENT_ID
  $ gcloud components remove COMPONENT_ID

To update your SDK installation to the latest version [528.0.0], run:
  $ gcloud components update

│ Not Installed │ Spanner Cli                                          │ spanner-cli                  │  12.1 MiB │
Enter fullscreen mode Exit fullscreen mode

So you need to install the spanner-cli component as follows:

$ gcloud components install --quiet spanner-cli

Your current Google Cloud CLI version is: 528.0.0
Installing components from version: 528.0.0

┌──────────────────────────────────────────────────────┐
│         These components will be installed.          │
├─────────────────────────────────┬─────────┬──────────┤
│               Name              │ Version │   Size   │
├─────────────────────────────────┼─────────┼──────────┤
│ Spanner Cli (Platform Specific) │   1.0.0 │ 12.1 MiB │
└─────────────────────────────────┴─────────┴──────────┘

For the latest full release notes, please visit:
  https://cloud.google.com/sdk/release_notes

Performing in place update...

╔════════════════════════════════════════════════════════════╗
╠═ Downloading: Spanner Cli                                 ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Downloading: Spanner Cli (Platform Specific)             ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: Spanner Cli                                  ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: Spanner Cli (Platform Specific)              ═╣
╚════════════════════════════════════════════════════════════╝

Performing post processing steps...done.                                                                                                                                                                                                                                                                            

Google Cloud CLI works best with Python 3.12 and certain modules.

Setting up virtual environment
Updating modules...
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.5/5.5 MB 31.2 MB/s eta 0:00:00
Modules updated.
Virtual env enabled.

Update done!
Enter fullscreen mode Exit fullscreen mode

Once installed, you can use gcloud alpha spanner cli:

$ gcloud alpha spanner cli --project ${SPANNER_PROJECT_ID} --instance ${SPANNER_INSTANCE_ID} ${SPANNER_DATABASE_ID}
Welcome to Spanner-Cli Client.

Type 'help;' or '\h' for help.
Type 'exit;' or 'quit;' or '\q' to exit.

spanner-cli> DESCRIBE SELECT 1;
+-------------+-------------+
| Column_Name | Column_Type |
+-------------+-------------+
|             | INT64       |
+-------------+-------------+
1 rows in set (0.17 sec)
Enter fullscreen mode Exit fullscreen mode

The official Spanner CLI can also be used directly without going through the gcloud command, as the google-cloud-sdk/bin/spannercli binary is available:

$ ~/google-cloud-sdk/bin/spannercli sql --help
Starts a SQL shell for a Spanner instance.

Usage:
  spanner sql --database=<database> [flags]

Examples:

# Start an interactive SQL shell for the 'my-database' database:
$ spanner sql --database my-database

# Execute a batch of SQL statements from the 'my-commands.sql' file:
$ spanner sql --database my-database --file my-commands.sql

# Execute a batch of SQL statements directly on the command line:
$ spanner sql --database my-database --execute "SELECT * FROM my-table; INSERT INTO my-table VALUES (1, 'apple');"

# SQL statements can be piped into the command:
$ spanner sql --database my-database < my-commands.sql
$ cat my-commands.sql | spanner sql --database my-database



Flags:
      --database string                The name of the database to start the sqlshell for. This flag is required.
      --delimiter string               The delimiter to use in the SQL shell.
      --deployment_endpoint string     The endpoint to use for the Spanner instance.
      --execute string                 The SQL commands to execute. This flag is mutually exclusive with the --source flag.
  -h, --help                           help for sql
      --history string                 The file to use for the SQL shell history.
      --html                           Show output in HTML format.
      --idle_transaction_timeout int   Idle transaction timeout. (default 60)
      --init-command string            The SQL command to execute before the SQL commands in the source file.
      --init-command-add string        The SQL command to execute after the SQL commands in the source file.
      --instance string                The instance ID of the Spanner instance.
      --project string                 The project ID of the Spanner instance.
      --prompt string                  The prompt to use in the SQL shell.
      --role string                    Role to use to connect to the Spanner instance.
      --skip-column-names              Show column names in the SQL shell.
      --skip-system-command            Allow system command in the SQL shell.
      --source string                  The file containing the SQL commands to execute. This flag is mutually exclusive with the --execute flag.
      --table                          Show output in Table format. (default true)
      --tee string                     The file to tee the SQL commands to.
      --xml                            Show output in XML format.
Enter fullscreen mode Exit fullscreen mode

Evidence that Official Spanner CLI is a Derivative of OSS spanner-cli

The relationship between OSS spanner-cli and official Spanner CLI is not mentioned at all in the official Spanner CLI documentation.
However, I can say with confidence: The official Spanner CLI is a derivative of OSS spanner-cli.

Let me show you the evidence. Let's extract strings contained in the official Spanner CLI binary using the strings command and filter for a specific string (apstndb):

$ strings google-cloud-sdk/bin/spannercli | grep apstndb
Bgoogle3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*InputStatement).StripComments
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.SeparateInputString
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.SeparateInput
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.newSeparator
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.SeparateInputPreserveComments
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).consumeRawString
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).consumeBytesString
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).consumeRawBytesString
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).consumeString
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).consumeStringContent
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.hasStringPrefix
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.hasPrefix
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).consumeStringDelimiter
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).skipComments
google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.(*separator).separate
type:.eq.google3/third_party/golang/github_com/apstndb/gsqlsep/v/v0/gsqlsep.InputStatement
third_party/golang/github_com/apstndb/gsqlsep/v/v0/separator.go
Enter fullscreen mode Exit fullscreen mode

We found github.com/apstndb/gsqlsep, a third-party library vendored within Google's monorepo google3.
To tell the truth, this apstndb/gsqlsep is something I created by improving and turning into a library the statement splitting implementation that spanner-cli had, in order to fix comment handling bugs in spanner-cli with fix comment handling #150.
No software other than spanner-cli uses this library. This is highly reliable evidence that it's a derivative of spanner-cli.

https://pkg.go.dev/github.com/apstndb/gsqlsep?tab=importedby

Note

Currently, it's explicitly stated that the official Spanner CLI doesn't support Spanner PostgreSQL interface.

PostgreSQL interface note: For PostgreSQL-dialect databases, you can use the psql command-line tool. The examples in this document are intended for GoogleSQL-dialect databases.

Since apstndb/gsqlsep is an implementation based on GoogleSQL lexical structure, it cannot support Spanner PostgreSQL interface.
As Spanner-specific features that cannot be supported through PGAdapter are expected to continue increasing, I predict that at some point they will abandon the dependency on apstndb/gsqlsep and natively support both GoogleSQL/PostgreSQL dialects.

Note end

As for when exactly it was forked, since DESCRIBE that I implemented by October 2024 is available, we can narrow it down to being derived from v0.10.6 or later.

spanner-cli> DESCRIBE SELECT * FROM Singers;
+-------------+-------------+
| Column_Name | Column_Type |
+-------------+-------------+
| SingerId    | INT64       |
| FirstName   | STRING      |
| LastName    | STRING      |
| SingerInfo  | BYTES       |
| BirthDate   | DATE        |
+-------------+-------------+
5 rows in set (0.17 sec)
Enter fullscreen mode Exit fullscreen mode

Also, since BEGIN RW ISOLATION LEVEL doesn't work, it's highly likely to be before v0.11.0.

spanner-cli> BEGIN RW ISOLATION LEVEL SERIALIZABLE;
ERROR: invalid statement
Enter fullscreen mode Exit fullscreen mode

Note: While many spanner-cli features including DESCRIBE can still be used in the official Spanner CLI, they may be removed in future releases as they are not documented in the official documentation.

Organizing the Fork Relationships

Based on the investigation so far, the relationships between OSS spanner-cli, official Spanner CLI, and spanner-mycli can be organized as follows (as of June 25, 2025):

Both of official Spanner CLI and spanner-mycli are forked from spanner-cli v0.10.6

Thus, both official Spanner CLI and spanner-mycli were forked from spanner-cli v0.10.6, each evolving independently. As of June 25, 2025, OSS spanner-cli has progressed to v0.11.0, and spanner-mycli to v0.19.0.

New Features Added in Official Spanner CLI

The official Spanner CLI has significant changes from OSS spanner-cli even in this initial release.

Meta-commands

The biggest change is the introduction of meta-commands.

Command Syntax Description
? \? Displays help information. Same as \h.
Delimiter \d Sets the statement delimiter. The default delimiter is a semi-colon.
Exit \q Exits the Spanner CLI. Same as quit.
Go \g Sends and runs SQL statement in Spanner.
Help \h Displays help information. Same as \?.
Notee \t Turns off writing to the output file set by the \T.
Prompt \R Changes your prompt to a user prompt string.
Quit \q Quits Spanner CLI. Same as exit.
Source \. Executes SQL from an input file. Takes [filename] as an argument.
System \! Executes a system shell command.
Tee \T Appends command output to a specified [filename] along with the standard output.
Use \u Connects to another database. Takes the new database name as an argument.

This seems to be strongly influenced by PostgreSQL psql command's meta-commands.
Client-side features of Spanner CLI will likely be extended as meta-commands in the future.

Note: Features for interacting with Spanner that are not limited to the client side are still largely undocumented. My intuition is that since it's undesirable for Google's official tools to have inconsistent command systems, it will converge to the Spanner JDBC command system.

Changes in Command-line Options

Command-line options have also changed significantly. Comparing the initial release v1.0.0 of official Spanner CLI with the latest release v0.11.1 of OSS spanner-cli, the following differences appear:

  • gcloud spanner integration
    • Connection specification is now common with gcloud spanner, supporting gcloud config set etc.
    • Authentication: --credential is removed and integrated into gcloud credential
    • However, I confirmed it doesn't work after gcloud auth application-default revoke, so it seems gcloud auth login alone isn't sufficient yet > Error: failed to create sql shell: credentials: could not find default credentials. See https://cloud.google.com/docs/authentication/external/set-up-adc for more information
    • GCLOUD_WIDE_FLAG support - mentions support for other gcloud level flags like --impersonate-service-account
  • Single-letter short forms are removed except for -h corresponding to --help
  • --verbose(-v), which was almost default in spanner-cli, is removed
  • Changed from --file to --source
  • Connection API endpoint changes: from --endpoint to --deployment_endpoint, --host, --post
  • Output format: Added --html, --xml in addition to --table
  • Other options removed from OSS spanner-cli: --priority, --directed-read, --proto-descriptor-file, --skip-tls-verify
  • Other options added in official Spanner CLI: --init-command string, --init-command-add string --tee string, --idle-transaction-timeout int, --skip-column-names string, --skip-system-command string, --system-command string, --delimiter string

While functionality as a CLI tool is enhanced, some Spanner API-related parts seem to have been removed.

Note

Regarding command-line options, there seem to be differences in official Spanner CLI between launching as gcloud alpha spanner cli and as spannercli sql.
This is probably because gcloud alpha spanner cli as a wrapper adjusts for consistency with the gcloud spanner command. There's a possibility that spannercli sql may become unavailable in the future.

gcloud alpha spanner cli spannercli sql
Database ID Positional parameter --database
Role specification --database-role --role

Thoughts as a Spanner Interactive Tool Developer

The birth of official Spanner CLI is very welcome.
This is because, as official documentation is written using it going forward, both officials and the user community will use the same tool and be able to discuss operations and results in a shareable text format.
While EXPLAIN and EXPLAIN ANALYZE are not currently mentioned, I hope we'll be able to discuss execution plans using this tool as well.

My concern is that unlike OSS-developed spanner-cli, the official Spanner CLI's source is not public.
Being provided only as a compiled binary in the compiled language Go means it's impossible to investigate problems independently and apply patches, which may lead to continued inconvenience for users.

I think it would be preferable to become like gsutil, which is distributed by Google as one of the gcloud components but developed on GitHub.

With this, the official tool's future evolution is promised, but I intend to continue developing my spanner-mycli as the only actively developed OSS Spanner interactive client.

Acknowledgments

When I confirmed the relationship between this official Spanner CLI and OSS spanner-cli, I learned that they are considering putting spanner-cli into maintenance mode and archiving it after the official Spanner CLI goes GA.

https://github.com/cloudspannerecosystem/spanner-cli/issues/214

Today Google officially released the official Spanner CLI and it's basically equivalent (or superset) of OSS spanner-cli in terms of functionality.
To prevent confusion from having two separate CLIs, I'm considering to make OSS spanner-cli maintenance mode and archive this repository once the official Spanner CLI goes to GA.

I deeply thank Yuki Furuyama, the author and owner of spanner-cli, who has supported the Spanner community as an interactive client not provided by first-party Google itself for over six and a half years since v0.1.0 in October 2018.

Top comments (0)