Skip to content

Conversation

@anicoll
Copy link
Contributor

@anicoll anicoll commented Oct 30, 2025

This feature simply adds the ability to parse DDL statements with subquery's as the test validates.
I think it resolved this issue as well

The highlights are the following:

  • Added ScalarSubquery type
  • Added SQL generation methods
  • Updated parseLit() to detect and parse subqueries

Related: #8519

@anicoll anicoll requested review from a team as code owners October 30, 2025 02:09
@product-auto-label product-auto-label bot added the api: spanner Issues related to the Spanner API. label Oct 30, 2025
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @anicoll, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the spansql parser's capabilities by introducing support for scalar subqueries within DDL statements, particularly for CREATE VIEW definitions. This change allows the parser to correctly interpret and represent more advanced SQL syntax, ensuring better compatibility and robustness when working with Spanner SQL DDLs that leverage subqueries.

Highlights

  • ScalarSubquery Type Introduction: A new ScalarSubquery struct has been added to represent subqueries that return a single value, enhancing the Abstract Syntax Tree (AST) to accurately model more complex SQL constructs.
  • SQL Generation for Scalar Subqueries: New methods, SQL() and addSQL(), have been implemented for the ScalarSubquery type. These methods ensure that scalar subqueries are correctly formatted and enclosed in parentheses when generating SQL strings.
  • Enhanced Parser Logic: The parseLit() function in the parser has been updated to intelligently detect and parse scalar subqueries. It now 'sniffs' for a 'SELECT' keyword after an opening parenthesis to differentiate between a regular parenthesized expression and a subquery, allowing for proper parsing of DDL statements containing subqueries.
  • Comprehensive Test Coverage: A new test case has been added to parser_test.go specifically for a CREATE OR REPLACE VIEW statement that includes a scalar subquery within a JOIN ON clause. This ensures the parser correctly handles and represents such complex DDL statements.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a valuable feature to support subqueries within VIEW JOIN clauses. The changes to introduce the ScalarSubquery type and its SQL generation are well-implemented. However, there is a critical issue in the parser logic within parser.go that needs to be addressed. The current implementation for handling parenthesized expressions and subqueries does not consume the opening parenthesis, which can lead to infinite recursion or parsing errors. I've provided a detailed comment and a code suggestion to resolve this.

Comment on lines 4659 to 4680
if tok.value == "(" {
// Look ahead to see if this is a subquery
if p.sniff("SELECT") {
q, err := p.parseQuery()
if err != nil {
return nil, err
}
if err := p.expect(")"); err != nil {
return nil, err
}
return ScalarSubquery{Query: q}, nil
}

// Regular parenthesized expression
e, err := p.parseExpr()
if err != nil {
return nil, err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This logic for handling parenthesized expressions and scalar subqueries appears to have a critical bug. The opening parenthesis ( is not consumed before p.parseQuery() or p.parseExpr() is called.

  1. For a scalar subquery like (SELECT ...), p.sniff("SELECT") will be true, but since the parser's position is still at (, the subsequent call to p.parseQuery() will likely fail as it expects to see a SELECT keyword.
  2. For a regular parenthesized expression like (1+2), p.sniff("SELECT") will be false, and execution will fall through to p.parseExpr(). Since the parser is still positioned at (, calling p.parseExpr() will eventually lead to a recursive call to p.parseLit(), which will see ( again, causing infinite recursion.

The fix is to consume the opening parenthesis ( before deciding which kind of expression to parse. The logic for the entire if tok.value == "(" block should be refactored to handle this correctly.

if tok.value == "(" {
		p.next() // Consume "(".

		// Check if this is a scalar subquery.
		tok := p.peek()
		if tok.kind == keyword && strings.EqualFold(tok.value, "SELECT") {
			q, err := p.parseQuery()
			if err != nil {
				return nil, err
			}
			if err := p.expect(")"); err != nil {
				return nil, err
			}
			return ScalarSubquery{Query: q}, nil
		}

		// It's a regular parenthesized expression.
		e, err := p.parseExpr()
		if err != nil {
			return nil, err
		}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No this is correct, I have just pushed a commit which validates it with tests and added an extra comment.

@anicoll anicoll changed the title spansql: [feature] support subquery in View Join feat(spansql): support subquery in View Join Oct 30, 2025
@anicoll
Copy link
Contributor Author

anicoll commented Oct 30, 2025

@rahul2393 if you could please be so kind as to review this PR too.

@anicoll
Copy link
Contributor Author

anicoll commented Oct 30, 2025

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for scalar subqueries by adding a ScalarSubquery type and updating the parser to recognize (SELECT ...) constructs within expressions. The implementation is clean and includes new methods for SQL generation and extensive test cases covering various scenarios, including SELECT lists, WHERE clauses, and CREATE VIEW statements.

My review identified one potential issue: the parser logic for detecting a subquery only checks for the SELECT keyword. This overlooks subqueries that start with a WITH clause (Common Table Expressions), which would cause parsing to fail for such valid queries. I've provided a suggestion to address this.

Overall, this is a great addition that significantly enhances the parser's capabilities. With the suggested fix, it will be even more robust.

if tok.value == "(" {
// Look ahead to see if this is a subquery like: (SELECT ...)
// p.sniff("SELECT") peeks at the next token without consuming it.
if p.sniff("SELECT") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current lookahead only checks for SELECT to identify a subquery. However, a subquery can also start with a WITH clause. This will cause parsing to fail for valid scalar subqueries that use common table expressions (CTEs).

To fix this, you should also check for the WITH keyword.

For example, a query like SELECT (WITH t AS (SELECT 1) SELECT * FROM t) would fail to parse correctly.

I'd also recommend adding a test case to TestParseExpr to cover subqueries that start with a WITH clause to prevent future regressions.

Suggested change
if p.sniff("SELECT") {
if p.sniff("SELECT") || p.sniff("WITH") {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WITH is currently not supported and this PR does not aim to resolve it either as it would grow to large.

@anicoll
Copy link
Contributor Author

anicoll commented Oct 30, 2025

@rahul2393 Is there any way I can get this reviewed soon?

rahul2393
rahul2393 previously approved these changes Oct 31, 2025
@rahul2393 rahul2393 added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Oct 31, 2025
@kokoro-team kokoro-team removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Oct 31, 2025
rahul2393 pushed a commit that referenced this pull request Oct 31, 2025
## Summary
This is a very simple PR to improve the readability of SQL written out
by this package.
## Highlights
* improve the SQL output for all types from a single long line to a more
human readable format
* Add test coverage for all fields updated

Please note that this is related to another of my PR's as I really need
both to progress please.
Related: #13266
@anicoll anicoll force-pushed the feat/subquery_support branch from 7d87b93 to a91c626 Compare November 1, 2025 08:47
@anicoll
Copy link
Contributor Author

anicoll commented Nov 1, 2025

I ran go vet locally (script from workflows) no issues 🤷

@anicoll anicoll requested a review from rahul2393 November 1, 2025 08:48
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when running vet.sh from the workflows folder these changes were applied

@rahul2393 rahul2393 added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Nov 1, 2025
@kokoro-team kokoro-team removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Nov 1, 2025
@anicoll
Copy link
Contributor Author

anicoll commented Nov 1, 2025

Thanks @rahul2393 I also looked at the failing test last run.
Seems it was an unrelated test. maybe flaky.

--- FAIL: TestMultiplexSessionWorker (0.07s)

@anicoll
Copy link
Contributor Author

anicoll commented Nov 3, 2025

@sakthivelmanii if you could please have a look at this 🙏

@rahul2393 rahul2393 merged commit d19f797 into googleapis:main Nov 3, 2025
9 of 10 checks passed
rahul2393 added a commit that referenced this pull request Dec 15, 2025
PR created by the Librarian CLI to initialize a release. Merging this PR
will auto trigger a release.

Librarian Version: v1.0.0
Language Image:
us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/librarian-go@sha256:718167d5c23ed389b41f617b3a00ac839bdd938a6bd2d48ae0c2f1fa51ab1c3d
<details><summary>spanner: 1.87.0</summary>

##
[1.87.0](spanner/v1.86.1...spanner/v1.87.0)
(2025-12-10)

### Features

* Add Send and Ack mutations for Queues (PiperOrigin-RevId: 832425466)
([185951b](185951b3))

* Exposing AutoscalingConfig in InstancePartition (PiperOrigin-RevId:
825184314)
([185951b](185951b3))

* Add Spanner location API (PiperOrigin-RevId: 833474957)
([185951b](185951b3))

* Add QueryAdvisorResult for query plan (PiperOrigin-RevId: 832425466)
([185951b](185951b3))

* improve the SQL formatting when printing out SQL (#13267)
([af0806f](af0806f4))

* Add grpc.xds.resource_type label to xDS client metrics (#13358)
([b9196cf](b9196cf6))

* support subquery in View Join (#13266)
([d19f797](d19f797b))

### Bug Fixes

* add env var to allow disabling directpath bound token (#13265)
([029bc79](029bc795))

* fix createMultiplexedSession goroutine leak (#13396)
([1805e89](1805e895))

* decoding spanner rows using SelectAll should map values in correct
annotations (#13301)
([315f65b](315f65b5))

* error instead of panic for iterator after tx end (#13366)
([a27c19a](a27c19ae))

* transaction_tag should be set on BeginTransactionRequest (#13463)
([a429aea](a429aea4))

* Configure keepAlive time for gRPC TCP connections (#13216)
([ca8f64e](ca8f64e0))

* avoid double decrement in session counting (#13395)
([e036421](e0364214))

### Documentation

* minor update for Spanner Location API (PiperOrigin-RevId: 834841888)
([185951b](185951b3))

* Update description for the BatchCreateSessionsRequest and Session
(PiperOrigin-RevId: 832425466)
([185951b](185951b3))

* Update description for the IsolationLevel (PiperOrigin-RevId:
832425466)
([185951b](185951b3))

</details>
cpriti-os pushed a commit to cpriti-os/google-cloud-go that referenced this pull request Dec 16, 2025
…3464)

PR created by the Librarian CLI to initialize a release. Merging this PR
will auto trigger a release.

Librarian Version: v1.0.0
Language Image:
us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/librarian-go@sha256:718167d5c23ed389b41f617b3a00ac839bdd938a6bd2d48ae0c2f1fa51ab1c3d
<details><summary>spanner: 1.87.0</summary>

##
[1.87.0](googleapis/google-cloud-go@spanner/v1.86.1...spanner/v1.87.0)
(2025-12-10)

### Features

* Add Send and Ack mutations for Queues (PiperOrigin-RevId: 832425466)
([185951b](googleapis@185951b3))

* Exposing AutoscalingConfig in InstancePartition (PiperOrigin-RevId:
825184314)
([185951b](googleapis@185951b3))

* Add Spanner location API (PiperOrigin-RevId: 833474957)
([185951b](googleapis@185951b3))

* Add QueryAdvisorResult for query plan (PiperOrigin-RevId: 832425466)
([185951b](googleapis@185951b3))

* improve the SQL formatting when printing out SQL (googleapis#13267)
([af0806f](googleapis@af0806f4))

* Add grpc.xds.resource_type label to xDS client metrics (googleapis#13358)
([b9196cf](googleapis@b9196cf6))

* support subquery in View Join (googleapis#13266)
([d19f797](googleapis@d19f797b))

### Bug Fixes

* add env var to allow disabling directpath bound token (googleapis#13265)
([029bc79](googleapis@029bc795))

* fix createMultiplexedSession goroutine leak (googleapis#13396)
([1805e89](googleapis@1805e895))

* decoding spanner rows using SelectAll should map values in correct
annotations (googleapis#13301)
([315f65b](googleapis@315f65b5))

* error instead of panic for iterator after tx end (googleapis#13366)
([a27c19a](googleapis@a27c19ae))

* transaction_tag should be set on BeginTransactionRequest (googleapis#13463)
([a429aea](googleapis@a429aea4))

* Configure keepAlive time for gRPC TCP connections (googleapis#13216)
([ca8f64e](googleapis@ca8f64e0))

* avoid double decrement in session counting (googleapis#13395)
([e036421](googleapis@e0364214))

### Documentation

* minor update for Spanner Location API (PiperOrigin-RevId: 834841888)
([185951b](googleapis@185951b3))

* Update description for the BatchCreateSessionsRequest and Session
(PiperOrigin-RevId: 832425466)
([185951b](googleapis@185951b3))

* Update description for the IsolationLevel (PiperOrigin-RevId:
832425466)
([185951b](googleapis@185951b3))

</details>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: spanner Issues related to the Spanner API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants