Skip to content

Commit 19238ce

Browse files
authored
feat: return metadata and stats for client-side statements (#470)
Return ResultSetMetadata and ResultSetStats as separate result sets if the caller has requested this in the ExecOptions. This ensures that client-side statements behave the same as statements that are executed by Spanner.
1 parent 570063a commit 19238ce

File tree

5 files changed

+221
-88
lines changed

5 files changed

+221
-88
lines changed

client_side_statement.go

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ import (
1818
"context"
1919
"database/sql/driver"
2020
"fmt"
21-
"io"
2221
"regexp"
2322
"strconv"
2423
"strings"
2524
"time"
2625

2726
"cloud.google.com/go/spanner"
2827
"cloud.google.com/go/spanner/apiv1/spannerpb"
28+
"google.golang.org/api/iterator"
2929
"google.golang.org/grpc/codes"
3030
"google.golang.org/grpc/status"
3131
)
@@ -48,7 +48,7 @@ import (
4848
type statementExecutor struct {
4949
}
5050

51-
func (s *statementExecutor) ShowCommitTimestamp(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Rows, error) {
51+
func (s *statementExecutor) ShowCommitTimestamp(_ context.Context, c *conn, _ string, opts ExecOptions, _ []driver.NamedValue) (driver.Rows, error) {
5252
ts, err := c.CommitTimestamp()
5353
var commitTs *time.Time
5454
if err == nil {
@@ -58,106 +58,106 @@ func (s *statementExecutor) ShowCommitTimestamp(_ context.Context, c *conn, _ st
5858
if err != nil {
5959
return nil, err
6060
}
61-
return &rows{it: it}, nil
61+
return createRows(it, opts), nil
6262
}
6363

64-
func (s *statementExecutor) ShowRetryAbortsInternally(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Rows, error) {
64+
func (s *statementExecutor) ShowRetryAbortsInternally(_ context.Context, c *conn, _ string, opts ExecOptions, _ []driver.NamedValue) (driver.Rows, error) {
6565
it, err := createBooleanIterator("RetryAbortsInternally", c.RetryAbortsInternally())
6666
if err != nil {
6767
return nil, err
6868
}
69-
return &rows{it: it}, nil
69+
return createRows(it, opts), nil
7070
}
7171

72-
func (s *statementExecutor) ShowAutoBatchDml(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Rows, error) {
72+
func (s *statementExecutor) ShowAutoBatchDml(_ context.Context, c *conn, _ string, opts ExecOptions, _ []driver.NamedValue) (driver.Rows, error) {
7373
it, err := createBooleanIterator("AutoBatchDml", c.AutoBatchDml())
7474
if err != nil {
7575
return nil, err
7676
}
77-
return &rows{it: it}, nil
77+
return createRows(it, opts), nil
7878
}
7979

80-
func (s *statementExecutor) ShowAutoBatchDmlUpdateCount(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Rows, error) {
80+
func (s *statementExecutor) ShowAutoBatchDmlUpdateCount(_ context.Context, c *conn, _ string, opts ExecOptions, _ []driver.NamedValue) (driver.Rows, error) {
8181
it, err := createInt64Iterator("AutoBatchDmlUpdateCount", c.AutoBatchDmlUpdateCount())
8282
if err != nil {
8383
return nil, err
8484
}
85-
return &rows{it: it}, nil
85+
return createRows(it, opts), nil
8686
}
8787

88-
func (s *statementExecutor) ShowAutoBatchDmlUpdateCountVerification(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Rows, error) {
88+
func (s *statementExecutor) ShowAutoBatchDmlUpdateCountVerification(_ context.Context, c *conn, opts ExecOptions, _ string, _ []driver.NamedValue) (driver.Rows, error) {
8989
it, err := createBooleanIterator("AutoBatchDmlUpdateCountVerification", c.AutoBatchDmlUpdateCountVerification())
9090
if err != nil {
9191
return nil, err
9292
}
93-
return &rows{it: it}, nil
93+
return createRows(it, opts), nil
9494
}
9595

96-
func (s *statementExecutor) ShowAutocommitDmlMode(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Rows, error) {
96+
func (s *statementExecutor) ShowAutocommitDmlMode(_ context.Context, c *conn, _ string, opts ExecOptions, _ []driver.NamedValue) (driver.Rows, error) {
9797
it, err := createStringIterator("AutocommitDMLMode", c.AutocommitDMLMode().String())
9898
if err != nil {
9999
return nil, err
100100
}
101-
return &rows{it: it}, nil
101+
return createRows(it, opts), nil
102102
}
103103

104-
func (s *statementExecutor) ShowReadOnlyStaleness(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Rows, error) {
104+
func (s *statementExecutor) ShowReadOnlyStaleness(_ context.Context, c *conn, _ string, opts ExecOptions, _ []driver.NamedValue) (driver.Rows, error) {
105105
it, err := createStringIterator("ReadOnlyStaleness", c.ReadOnlyStaleness().String())
106106
if err != nil {
107107
return nil, err
108108
}
109-
return &rows{it: it}, nil
109+
return createRows(it, opts), nil
110110
}
111111

112-
func (s *statementExecutor) ShowExcludeTxnFromChangeStreams(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Rows, error) {
112+
func (s *statementExecutor) ShowExcludeTxnFromChangeStreams(_ context.Context, c *conn, _ string, opts ExecOptions, _ []driver.NamedValue) (driver.Rows, error) {
113113
it, err := createBooleanIterator("ExcludeTxnFromChangeStreams", c.ExcludeTxnFromChangeStreams())
114114
if err != nil {
115115
return nil, err
116116
}
117-
return &rows{it: it}, nil
117+
return createRows(it, opts), nil
118118
}
119119

120-
func (s *statementExecutor) ShowMaxCommitDelay(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Rows, error) {
120+
func (s *statementExecutor) ShowMaxCommitDelay(_ context.Context, c *conn, _ string, opts ExecOptions, _ []driver.NamedValue) (driver.Rows, error) {
121121
it, err := createStringIterator("MaxCommitDelay", c.MaxCommitDelay().String())
122122
if err != nil {
123123
return nil, err
124124
}
125-
return &rows{it: it}, nil
125+
return createRows(it, opts), nil
126126
}
127127

128-
func (s *statementExecutor) ShowTransactionTag(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Rows, error) {
128+
func (s *statementExecutor) ShowTransactionTag(_ context.Context, c *conn, _ string, opts ExecOptions, _ []driver.NamedValue) (driver.Rows, error) {
129129
it, err := createStringIterator("TransactionTag", c.TransactionTag())
130130
if err != nil {
131131
return nil, err
132132
}
133-
return &rows{it: it}, nil
133+
return createRows(it, opts), nil
134134
}
135135

136-
func (s *statementExecutor) ShowStatementTag(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Rows, error) {
136+
func (s *statementExecutor) ShowStatementTag(_ context.Context, c *conn, _ string, opts ExecOptions, _ []driver.NamedValue) (driver.Rows, error) {
137137
it, err := createStringIterator("StatementTag", c.StatementTag())
138138
if err != nil {
139139
return nil, err
140140
}
141-
return &rows{it: it}, nil
141+
return createRows(it, opts), nil
142142
}
143143

144-
func (s *statementExecutor) StartBatchDdl(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Result, error) {
144+
func (s *statementExecutor) StartBatchDdl(_ context.Context, c *conn, _ string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
145145
return c.startBatchDDL()
146146
}
147147

148-
func (s *statementExecutor) StartBatchDml(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Result, error) {
148+
func (s *statementExecutor) StartBatchDml(_ context.Context, c *conn, _ string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
149149
return c.startBatchDML( /* automatic = */ false)
150150
}
151151

152-
func (s *statementExecutor) RunBatch(ctx context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Result, error) {
152+
func (s *statementExecutor) RunBatch(ctx context.Context, c *conn, _ string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
153153
return c.runBatch(ctx)
154154
}
155155

156-
func (s *statementExecutor) AbortBatch(_ context.Context, c *conn, _ string, _ []driver.NamedValue) (driver.Result, error) {
156+
func (s *statementExecutor) AbortBatch(_ context.Context, c *conn, _ string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
157157
return c.abortBatch()
158158
}
159159

160-
func (s *statementExecutor) SetRetryAbortsInternally(_ context.Context, c *conn, params string, _ []driver.NamedValue) (driver.Result, error) {
160+
func (s *statementExecutor) SetRetryAbortsInternally(_ context.Context, c *conn, params string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
161161
if params == "" {
162162
return nil, spanner.ToSpannerError(status.Error(codes.InvalidArgument, "no value given for RetryAbortsInternally"))
163163
}
@@ -168,19 +168,19 @@ func (s *statementExecutor) SetRetryAbortsInternally(_ context.Context, c *conn,
168168
return c.setRetryAbortsInternally(retry)
169169
}
170170

171-
func (s *statementExecutor) SetAutoBatchDml(_ context.Context, c *conn, params string, _ []driver.NamedValue) (driver.Result, error) {
171+
func (s *statementExecutor) SetAutoBatchDml(_ context.Context, c *conn, params string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
172172
return setBoolVariable("AutoBatchDml", func(value bool) (driver.Result, error) {
173173
return driver.ResultNoRows, c.SetAutoBatchDml(value)
174174
}, params)
175175
}
176176

177-
func (s *statementExecutor) SetAutoBatchDmlUpdateCount(_ context.Context, c *conn, params string, _ []driver.NamedValue) (driver.Result, error) {
177+
func (s *statementExecutor) SetAutoBatchDmlUpdateCount(_ context.Context, c *conn, params string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
178178
return setInt64Variable("AutoBatchDmlUpdateCount", func(value int64) (driver.Result, error) {
179179
return driver.ResultNoRows, c.SetAutoBatchDmlUpdateCount(value)
180180
}, params)
181181
}
182182

183-
func (s *statementExecutor) SetAutoBatchDmlUpdateCountVerification(_ context.Context, c *conn, params string, _ []driver.NamedValue) (driver.Result, error) {
183+
func (s *statementExecutor) SetAutoBatchDmlUpdateCountVerification(_ context.Context, c *conn, params string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
184184
return setBoolVariable("AutoBatchDmlUpdateCountVerification", func(value bool) (driver.Result, error) {
185185
return driver.ResultNoRows, c.SetAutoBatchDmlUpdateCountVerification(value)
186186
}, params)
@@ -208,7 +208,7 @@ func setInt64Variable(name string, f func(value int64) (driver.Result, error), p
208208
return f(value)
209209
}
210210

211-
func (s *statementExecutor) SetAutocommitDmlMode(_ context.Context, c *conn, params string, _ []driver.NamedValue) (driver.Result, error) {
211+
func (s *statementExecutor) SetAutocommitDmlMode(_ context.Context, c *conn, params string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
212212
if params == "" {
213213
return nil, spanner.ToSpannerError(status.Error(codes.InvalidArgument, "no value given for AutocommitDMLMode"))
214214
}
@@ -224,7 +224,7 @@ func (s *statementExecutor) SetAutocommitDmlMode(_ context.Context, c *conn, par
224224
return c.setAutocommitDMLMode(mode)
225225
}
226226

227-
func (s *statementExecutor) SetExcludeTxnFromChangeStreams(_ context.Context, c *conn, params string, _ []driver.NamedValue) (driver.Result, error) {
227+
func (s *statementExecutor) SetExcludeTxnFromChangeStreams(_ context.Context, c *conn, params string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
228228
if params == "" {
229229
return nil, spanner.ToSpannerError(status.Error(codes.InvalidArgument, "no value given for ExcludeTxnFromChangeStreams"))
230230
}
@@ -237,23 +237,23 @@ func (s *statementExecutor) SetExcludeTxnFromChangeStreams(_ context.Context, c
237237

238238
var maxCommitDelayRegexp = regexp.MustCompile(`(?i)^\s*('(?P<duration>(\d{1,19})(s|ms|us|ns))'|(?P<number>\d{1,19})|(?P<null>NULL))\s*$`)
239239

240-
func (s *statementExecutor) SetMaxCommitDelay(_ context.Context, c *conn, params string, _ []driver.NamedValue) (driver.Result, error) {
240+
func (s *statementExecutor) SetMaxCommitDelay(_ context.Context, c *conn, params string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
241241
duration, err := parseDuration(maxCommitDelayRegexp, "max_commit_delay", params)
242242
if err != nil {
243243
return nil, err
244244
}
245245
return c.setMaxCommitDelay(duration)
246246
}
247247

248-
func (s *statementExecutor) SetTransactionTag(_ context.Context, c *conn, params string, _ []driver.NamedValue) (driver.Result, error) {
248+
func (s *statementExecutor) SetTransactionTag(_ context.Context, c *conn, params string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
249249
tag, err := parseTag(params)
250250
if err != nil {
251251
return nil, err
252252
}
253253
return c.setTransactionTag(tag)
254254
}
255255

256-
func (s *statementExecutor) SetStatementTag(_ context.Context, c *conn, params string, _ []driver.NamedValue) (driver.Result, error) {
256+
func (s *statementExecutor) SetStatementTag(_ context.Context, c *conn, params string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
257257
tag, err := parseTag(params)
258258
if err != nil {
259259
return nil, err
@@ -280,7 +280,7 @@ var maxStalenessRegexp = regexp.MustCompile(`(?i)'(?P<type>MAX_STALENESS)[\t ]+(
280280
var readTimestampRegexp = regexp.MustCompile(`(?i)'(?P<type>READ_TIMESTAMP)[\t ]+(?P<timestamp>(\d{4})-(\d{2})-(\d{2})([Tt](\d{2}):(\d{2}):(\d{2})(\.\d{1,9})?)([Zz]|([+-])(\d{2}):(\d{2})))'`)
281281
var minReadTimestampRegexp = regexp.MustCompile(`(?i)'(?P<type>MIN_READ_TIMESTAMP)[\t ]+(?P<timestamp>(\d{4})-(\d{2})-(\d{2})([Tt](\d{2}):(\d{2}):(\d{2})(\.\d{1,9})?)([Zz]|([+-])(\d{2}):(\d{2})))'`)
282282

283-
func (s *statementExecutor) SetReadOnlyStaleness(_ context.Context, c *conn, params string, _ []driver.NamedValue) (driver.Result, error) {
283+
func (s *statementExecutor) SetReadOnlyStaleness(_ context.Context, c *conn, params string, _ ExecOptions, _ []driver.NamedValue) (driver.Result, error) {
284284
if params == "" {
285285
return nil, spanner.ToSpannerError(status.Error(codes.InvalidArgument, "no value given for ReadOnlyStaleness"))
286286
}
@@ -369,6 +369,17 @@ func matchesToMap(re *regexp.Regexp, s string) map[string]string {
369369
return matches
370370
}
371371

372+
func createEmptyIterator() *clientSideIterator {
373+
return &clientSideIterator{
374+
metadata: &spannerpb.ResultSetMetadata{
375+
RowType: &spannerpb.StructType{
376+
Fields: []*spannerpb.StructType_Field{},
377+
},
378+
},
379+
rows: []*spanner.Row{},
380+
}
381+
}
382+
372383
// createBooleanIterator creates a row iterator with a single BOOL column with
373384
// one row. This is used for client side statements that return a result set
374385
// containing a BOOL value.
@@ -428,7 +439,7 @@ type clientSideIterator struct {
428439

429440
func (t *clientSideIterator) Next() (*spanner.Row, error) {
430441
if t.index == len(t.rows) {
431-
return nil, io.EOF
442+
return nil, iterator.Done
432443
}
433444
row := t.rows[t.index]
434445
t.index++

0 commit comments

Comments
 (0)