Skip to content

Commit

Permalink
Improve Spanner JDBC driver compatibility (#78)
Browse files Browse the repository at this point in the history
  • Loading branch information
apstndb authored Dec 9, 2024
1 parent 41beaba commit e536a8d
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 57 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,10 @@ and `{}` for a mutually exclusive keyword.
| Show Query Result Shape | `DESCRIBE SELECT ...;` | |
| Show DML Result Shape | `DESCRIBE {INSERT\|UPDATE\|DELETE} ... THEN RETURN ...;` | |
| Start a new query optimizer statistics package construction | `ANALYZE;` | |
| Start Read-Write Transaction | `BEGIN [RW] [PRIORITY {HIGH\|MEDIUM\|LOW}];` | See [Request Priority](#request-priority) for details on the priority. The tag you set is used as both transaction tag and request tag.|
| Commit Read-Write Transaction | `COMMIT;` | |
| Rollback Read-Write Transaction | `ROLLBACK;` | |
| Start Read-Only Transaction | `BEGIN RO [{<seconds>\|<RFC3339-formatted time>}] [PRIORITY {HIGH\|MEDIUM\|LOW}];` | `<seconds>` and `<RFC3339-formatted time>` is used for stale read. See [Request Priority](#request-priority) for details on the priority.|
| End Read-Only Transaction | `CLOSE;` | |
| Start Read-Write Transaction | `BEGIN [RW] [TRANSACTION] [PRIORITY {HIGH\|MEDIUM\|LOW}];` | See [Request Priority](#request-priority) for details on the priority. The tag you set is used as both transaction tag and request tag.|
| Commit Read-Write Transaction<br/>or end Read-OnlyTransaction | `COMMIT [TRANSACTION];` | |
| Rollback Read-Write Transaction<br/>or end Read-Only Transaction | `ROLLBACK [TRANSACTION];` | `CLOSE` is a synonym of `ROLLBACK`.|
| Start Read-Only Transaction | `BEGIN RO [TRANSACTION] [{<seconds>\|<RFC3339-formatted time>}] [PRIORITY {HIGH\|MEDIUM\|LOW}];` | `<seconds>` and `<RFC3339-formatted time>` is used for stale read. See [Request Priority](#request-priority) for details on the priority.|
| Test root-partitionable | `TRY PARTITIONED QUERY <sql>` ||
| Show partition tokens of partition query | `PARTITION <sql>` ||
| Perform write mutations | `MUTATE <table_fqn> {INSERT\|UPDATE\|REPLACE\|INSERT_OR_UPDATE} ...`||
Expand Down
88 changes: 37 additions & 51 deletions statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,10 @@ var (
truncateTableRe = regexp.MustCompile(`(?is)^TRUNCATE\s+TABLE\s+(.+)$`)

// Transaction
beginRwRe = regexp.MustCompile(`(?is)^BEGIN(?:\s+RW)?(?:\s+PRIORITY\s+(HIGH|MEDIUM|LOW))?$`)
beginRoRe = regexp.MustCompile(`(?is)^BEGIN\s+RO(?:\s+([^\s]+))?(?:\s+PRIORITY\s+(HIGH|MEDIUM|LOW))?$`)
commitRe = regexp.MustCompile(`(?is)^COMMIT$`)
rollbackRe = regexp.MustCompile(`(?is)^ROLLBACK$`)
closeRe = regexp.MustCompile(`(?is)^CLOSE$`)
beginRwRe = regexp.MustCompile(`(?is)^BEGIN(?:\s+RW)?(?:\s+TRANSACTION)?(?:\s+PRIORITY\s+(HIGH|MEDIUM|LOW))?$`)
beginRoRe = regexp.MustCompile(`(?is)^BEGIN\s+RO(?:\s+([^\s]+))?(?:\s+TRANSACTION)?(?:\s+PRIORITY\s+(HIGH|MEDIUM|LOW))?$`)
commitRe = regexp.MustCompile(`(?is)^COMMIT(?:\s+TRANSACTION)?$`)
rollbackRe = regexp.MustCompile(`(?is)^(?:ROLLBACK|CLOSE)(?:\s+TRANSACTION)?$`)

// Other
exitRe = regexp.MustCompile(`(?is)^EXIT$`)
Expand Down Expand Up @@ -256,8 +255,6 @@ func BuildCLIStatement(trimmed string) (Statement, error) {
return &CommitStatement{}, nil
case rollbackRe.MatchString(trimmed):
return &RollbackStatement{}, nil
case closeRe.MatchString(trimmed):
return &CloseStatement{}, nil
case showVariableRe.MatchString(trimmed):
matched := showVariableRe.FindStringSubmatch(trimmed)
return &ShowVariableStatement{VarName: matched[1]}, nil
Expand Down Expand Up @@ -990,43 +987,52 @@ func (s *BeginRwStatement) Execute(ctx context.Context, session *Session) (*Resu
type CommitStatement struct{}

func (s *CommitStatement) Execute(ctx context.Context, session *Session) (*Result, error) {
if session.InReadOnlyTransaction() {
return nil, errors.New("you're in read-only transaction. Please finish the transaction by 'CLOSE;'")
}

result := &Result{IsMutation: true}
switch {
case !session.InReadWriteTransaction() && !session.InReadOnlyTransaction():
return result, nil
case session.InReadOnlyTransaction():
if err := session.CloseReadOnlyTransaction(); err != nil {
return nil, err
}

if !session.InReadWriteTransaction() {
return result, nil
}
case session.InReadWriteTransaction():
resp, err := session.CommitReadWriteTransaction(ctx)
if err != nil {
return nil, err
}

resp, err := session.CommitReadWriteTransaction(ctx)
if err != nil {
return nil, err
result.Timestamp = resp.CommitTs
result.CommitStats = resp.CommitStats
return result, nil
default:
return nil, errors.New("invalid state")
}

result.Timestamp = resp.CommitTs
result.CommitStats = resp.CommitStats
return result, nil
}

type RollbackStatement struct{}

func (s *RollbackStatement) Execute(ctx context.Context, session *Session) (*Result, error) {
if session.InReadOnlyTransaction() {
return nil, errors.New("you're in read-only transaction. Please finish the transaction by 'CLOSE;'")
}

result := &Result{IsMutation: true}
if !session.InReadWriteTransaction() {
switch {
case !session.InReadWriteTransaction() && !session.InReadOnlyTransaction():
return result, nil
}
case session.InReadOnlyTransaction():
if err := session.CloseReadOnlyTransaction(); err != nil {
return nil, err
}

if err := session.RollbackReadWriteTransaction(ctx); err != nil {
return nil, err
}
return result, nil
case session.InReadWriteTransaction():
if err := session.RollbackReadWriteTransaction(ctx); err != nil {
return nil, err
}

return result, nil
return result, nil
default:
return nil, errors.New("invalid state")
}
}

type timestampBoundType int
Expand Down Expand Up @@ -1082,7 +1088,7 @@ func (s *BeginRoStatement) Execute(ctx context.Context, session *Session) (*Resu
}
if session.InReadOnlyTransaction() {
// close current transaction implicitly
if _, err := (&CloseStatement{}).Execute(ctx, session); err != nil {
if _, err := (&RollbackStatement{}).Execute(ctx, session); err != nil {
return nil, fmt.Errorf("error on close current transaction: %w", err)
}
}
Expand All @@ -1098,26 +1104,6 @@ func (s *BeginRoStatement) Execute(ctx context.Context, session *Session) (*Resu
}, nil
}

type CloseStatement struct{}

func (s *CloseStatement) Execute(ctx context.Context, session *Session) (*Result, error) {
if session.InReadWriteTransaction() {
return nil, errors.New("You're in read-write transaction. Please finish the transaction by 'COMMIT;' or 'ROLLBACK;'")
}

result := &Result{IsMutation: true}

if !session.InReadOnlyTransaction() {
return result, nil
}

if err := session.CloseReadOnlyTransaction(); err != nil {
return nil, err
}

return result, nil
}

type PartitionStatement struct{ SQL string }

func (s *PartitionStatement) Execute(ctx context.Context, session *Session) (*Result, error) {
Expand Down
22 changes: 21 additions & 1 deletion statement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ func TestBuildStatement(t *testing.T) {
input: "BEGIN",
want: &BeginRwStatement{},
},
{
desc: "BEGIN TRANSACTION statement",
input: "BEGIN TRANSACTION",
want: &BeginRwStatement{},
},
{
desc: "BEGIN RW statement",
input: "BEGIN RW",
Expand Down Expand Up @@ -320,15 +325,30 @@ func TestBuildStatement(t *testing.T) {
input: "COMMIT",
want: &CommitStatement{},
},
{
desc: "COMMIT TRANSACTION statement",
input: "COMMIT TRANSACTION",
want: &CommitStatement{},
},
{
desc: "ROLLBACK statement",
input: "ROLLBACK",
want: &RollbackStatement{},
},
{
desc: "ROLLBACK TRANSACTION statement",
input: "ROLLBACK TRANSACTION",
want: &RollbackStatement{},
},
{
desc: "CLOSE statement",
input: "CLOSE",
want: &CloseStatement{},
want: &RollbackStatement{},
},
{
desc: "CLOSE TRANSACTION statement",
input: "CLOSE TRANSACTION",
want: &RollbackStatement{},
},
{
desc: "EXIT statement",
Expand Down

0 comments on commit e536a8d

Please sign in to comment.