-
Notifications
You must be signed in to change notification settings - Fork 1.5k
feat(spansql): support subquery in View Join #13266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Summary of ChangesHello @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 Highlights
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this 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.
| 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
- For a scalar subquery like
(SELECT ...),p.sniff("SELECT")will be true, but since the parser's position is still at(, the subsequent call top.parseQuery()will likely fail as it expects to see aSELECTkeyword. - For a regular parenthesized expression like
(1+2),p.sniff("SELECT")will be false, and execution will fall through top.parseExpr(). Since the parser is still positioned at(, callingp.parseExpr()will eventually lead to a recursive call top.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
}There was a problem hiding this comment.
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.
|
@rahul2393 if you could please be so kind as to review this PR too. |
|
/gemini review |
There was a problem hiding this 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") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| if p.sniff("SELECT") { | |
| if p.sniff("SELECT") || p.sniff("WITH") { |
There was a problem hiding this comment.
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.
|
@rahul2393 Is there any way I can get this reviewed soon? |
## 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
4655ee8 to
1104313
Compare
7d87b93 to
a91c626
Compare
|
I ran go vet locally (script from workflows) no issues 🤷 |
| 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= |
There was a problem hiding this comment.
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
|
Thanks @rahul2393 I also looked at the failing test last run.
|
|
@sakthivelmanii if you could please have a look at this 🙏 |
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>
…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>
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:
Related: #8519