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
feat(spanner/spansql): support table_hint_expr at from_clause on query_statement #4457
Changes from 1 commit
6e961d4
7191acf
83d63c5
02f2545
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1885,7 +1885,7 @@ func (p *parser) parseQuery() (Query, *parseError) { | |
[ LIMIT count [ OFFSET skip_rows ] ] | ||
*/ | ||
|
||
// TODO: hints, sub-selects, etc. | ||
// TODO: sub-selects, etc. | ||
|
||
if err := p.expect("SELECT"); err != nil { | ||
return Query{}, err | ||
|
@@ -2111,6 +2111,13 @@ func (p *parser) parseSelectFrom() (SelectFrom, *parseError) { | |
return nil, err | ||
} | ||
sf := SelectFromTable{Table: tname} | ||
if p.eat("@") { | ||
hint, err := p.parseHints(map[string]string{}, false) | ||
if err != nil { | ||
return nil, err | ||
} | ||
sf.Hint = hint | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: same as above, rename to |
||
} | ||
|
||
// TODO: The "AS" keyword is optional. | ||
if p.eat("AS") { | ||
|
@@ -2159,46 +2166,20 @@ func (p *parser) parseSelectFrom() (SelectFrom, *parseError) { | |
Type: jt, | ||
LHS: sf, | ||
} | ||
setHint := func(k, v string) { | ||
if sfj.Hints == nil { | ||
sfj.Hints = make(map[string]string) | ||
} | ||
sfj.Hints[k] = v | ||
} | ||
var hints map[string]string | ||
if hashJoin { | ||
setHint("JOIN_METHOD", "HASH_JOIN") | ||
hints = map[string]string{} | ||
hints["JOIN_METHOD"] = "HASH_JOIN" | ||
} | ||
|
||
if p.eat("@") { | ||
if err := p.expect("{"); err != nil { | ||
return nil, err | ||
} | ||
for { | ||
if p.sniff("}") { | ||
break | ||
} | ||
tok := p.next() | ||
if tok.err != nil { | ||
return nil, tok.err | ||
} | ||
k := tok.value | ||
if err := p.expect("="); err != nil { | ||
return nil, err | ||
} | ||
tok = p.next() | ||
if tok.err != nil { | ||
return nil, tok.err | ||
} | ||
v := tok.value | ||
setHint(k, v) | ||
if !p.eat(",") { | ||
break | ||
} | ||
} | ||
if err := p.expect("}"); err != nil { | ||
h, err := p.parseHints(hints, true) | ||
if err != nil { | ||
return nil, err | ||
} | ||
hints = h | ||
} | ||
sfj.Hints = hints | ||
|
||
sfj.RHS, err = p.parseSelectFrom() | ||
if err != nil { | ||
|
@@ -2889,6 +2870,41 @@ func (p *parser) parseAlias() (ID, *parseError) { | |
return p.parseTableOrIndexOrColumnName() | ||
} | ||
|
||
func (p *parser) parseHints(hints map[string]string, multiple bool) (map[string]string, *parseError) { | ||
if hints == nil { | ||
hints = map[string]string{} | ||
} | ||
if err := p.expect("{"); err != nil { | ||
return nil, err | ||
} | ||
for { | ||
if p.sniff("}") { | ||
break | ||
} | ||
tok := p.next() | ||
if tok.err != nil { | ||
return nil, tok.err | ||
} | ||
k := tok.value | ||
if err := p.expect("="); err != nil { | ||
return nil, err | ||
} | ||
tok = p.next() | ||
if tok.err != nil { | ||
return nil, tok.err | ||
} | ||
v := tok.value | ||
hints[k] = v | ||
if !multiple || !p.eat(",") { | ||
break | ||
} | ||
} | ||
if err := p.expect("}"); err != nil { | ||
return nil, err | ||
} | ||
return hints, nil | ||
} | ||
|
||
func (p *parser) parseTableOrIndexOrColumnName() (ID, *parseError) { | ||
/* | ||
table_name and column_name and index_name: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -365,6 +365,14 @@ func (sel Select) addSQL(sb *strings.Builder) { | |
|
||
func (sft SelectFromTable) SQL() string { | ||
str := sft.Table.SQL() | ||
if len(sft.Hint) > 0 { | ||
for k, v := range sft.Hint { | ||
str += fmt.Sprintf("@{%s=%s}", k, v) | ||
// table hint exists only one key | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above, it seems that the documentation is not completely clear on this, but Cloud Spanner does accept more than one table hint. |
||
break | ||
} | ||
} | ||
|
||
if sft.Alias != "" { | ||
str += " AS " + sft.Alias.SQL() | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -394,6 +394,7 @@ type SelectFrom interface { | |
type SelectFromTable struct { | ||
Table ID | ||
Alias ID // empty if not aliased | ||
Hint map[string]string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: rename to |
||
} | ||
|
||
func (SelectFromTable) isSelectFrom() {} | ||
|
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.
Hmm... It seems that the documentation is slightly off here. It is actually possible to have more than one table hint, so the variable here should be renamed to
hints
and theparseHints(..)
function should be called withtrue
rather thanfalse
.For example the following query is accepted by Cloud Spanner: