Skip to content

Commit ed619ad

Browse files
authored
perf: decode values directly from row (#795)
Remove the intermediate step of first decoding a value to a GenericColumnValue, and then into the Go type that should be returned. This reduces the number of memory allocations needed to decode query results.
1 parent 62279ea commit ed619ad

1 file changed

Lines changed: 62 additions & 48 deletions

File tree

rows.go

Lines changed: 62 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ type rows struct {
6464
colsOnce sync.Once
6565
dirtyErr error
6666
cols []string
67+
colTypes []*sppb.Type
6768

6869
state *connectionstate.ConnectionState
6970
decodeOption DecodeOption
@@ -155,8 +156,10 @@ func (r *rows) getColumns() {
155156
}
156157
rowType := metadata.RowType
157158
r.cols = make([]string, len(rowType.Fields))
159+
r.colTypes = make([]*sppb.Type, len(rowType.Fields))
158160
for i, c := range rowType.Fields {
159161
r.cols[i] = c.Name
162+
r.colTypes[i] = c.Type
160163
}
161164
})
162165
}
@@ -203,19 +206,23 @@ func (r *rows) Next(dest []driver.Value) error {
203206
}
204207
}
205208

209+
if r.colTypes == nil {
210+
return fmt.Errorf("spanner: missing column types metadata")
211+
}
212+
206213
for i := 0; i < row.Size(); i++ {
207-
var col spanner.GenericColumnValue
208-
if err := row.Column(i, &col); err != nil {
209-
return err
210-
}
211214
if r.decodeOption == DecodeOptionProto {
215+
var col spanner.GenericColumnValue
216+
if err := row.Column(i, &col); err != nil {
217+
return err
218+
}
212219
dest[i] = col
213220
continue
214221
}
215-
switch col.Type.Code {
222+
switch r.colTypes[i].Code {
216223
case sppb.TypeCode_INT64, sppb.TypeCode_ENUM:
217224
var v spanner.NullInt64
218-
if err := col.Decode(&v); err != nil {
225+
if err := row.Column(i, &v); err != nil {
219226
return err
220227
}
221228
if v.Valid {
@@ -225,7 +232,7 @@ func (r *rows) Next(dest []driver.Value) error {
225232
}
226233
case sppb.TypeCode_FLOAT32:
227234
var v spanner.NullFloat32
228-
if err := col.Decode(&v); err != nil {
235+
if err := row.Column(i, &v); err != nil {
229236
return err
230237
}
231238
if v.Valid {
@@ -235,7 +242,7 @@ func (r *rows) Next(dest []driver.Value) error {
235242
}
236243
case sppb.TypeCode_FLOAT64:
237244
var v spanner.NullFloat64
238-
if err := col.Decode(&v); err != nil {
245+
if err := row.Column(i, &v); err != nil {
239246
return err
240247
}
241248
if v.Valid {
@@ -245,15 +252,19 @@ func (r *rows) Next(dest []driver.Value) error {
245252
}
246253
case sppb.TypeCode_NUMERIC:
247254
if propertyDecodeNumericToString.GetValueOrDefault(r.state) {
255+
var col spanner.GenericColumnValue
256+
if err := row.Column(i, &col); err != nil {
257+
return err
258+
}
248259
if _, ok := col.Value.Kind.(*structpb.Value_NullValue); ok {
249260
dest[i] = nil
250261
} else {
251262
dest[i] = col.Value.GetStringValue()
252263
}
253264
} else {
254-
if col.Type.TypeAnnotation == sppb.TypeAnnotationCode_PG_NUMERIC {
265+
if r.colTypes[i].TypeAnnotation == sppb.TypeAnnotationCode_PG_NUMERIC {
255266
var v spanner.PGNumeric
256-
if err := col.Decode(&v); err != nil {
267+
if err := row.Column(i, &v); err != nil {
257268
return err
258269
}
259270
if v.Valid {
@@ -263,7 +274,7 @@ func (r *rows) Next(dest []driver.Value) error {
263274
}
264275
} else {
265276
var v spanner.NullNumeric
266-
if err := col.Decode(&v); err != nil {
277+
if err := row.Column(i, &v); err != nil {
267278
return err
268279
}
269280
if v.Valid {
@@ -275,7 +286,7 @@ func (r *rows) Next(dest []driver.Value) error {
275286
}
276287
case sppb.TypeCode_STRING:
277288
var v spanner.NullString
278-
if err := col.Decode(&v); err != nil {
289+
if err := row.Column(i, &v); err != nil {
279290
return err
280291
}
281292
if v.Valid {
@@ -284,15 +295,15 @@ func (r *rows) Next(dest []driver.Value) error {
284295
dest[i] = nil
285296
}
286297
case sppb.TypeCode_JSON:
287-
if col.Type.TypeAnnotation == sppb.TypeAnnotationCode_PG_JSONB {
298+
if r.colTypes[i].TypeAnnotation == sppb.TypeAnnotationCode_PG_JSONB {
288299
var v spanner.PGJsonB
289-
if err := col.Decode(&v); err != nil {
300+
if err := row.Column(i, &v); err != nil {
290301
return err
291302
}
292303
dest[i] = v
293304
} else {
294305
var v spanner.NullJSON
295-
if err := col.Decode(&v); err != nil {
306+
if err := row.Column(i, &v); err != nil {
296307
return err
297308
}
298309
// We always assign `v` to dest[i] here because there is no native type
@@ -302,7 +313,7 @@ func (r *rows) Next(dest []driver.Value) error {
302313
}
303314
case sppb.TypeCode_UUID:
304315
var v spanner.NullUUID
305-
if err := col.Decode(&v); err != nil {
316+
if err := row.Column(i, &v); err != nil {
306317
return err
307318
}
308319
if v.Valid {
@@ -313,13 +324,13 @@ func (r *rows) Next(dest []driver.Value) error {
313324
case sppb.TypeCode_BYTES, sppb.TypeCode_PROTO:
314325
// The column value is a base64 encoded string.
315326
var v []byte
316-
if err := col.Decode(&v); err != nil {
327+
if err := row.Column(i, &v); err != nil {
317328
return err
318329
}
319330
dest[i] = v
320331
case sppb.TypeCode_BOOL:
321332
var v spanner.NullBool
322-
if err := col.Decode(&v); err != nil {
333+
if err := row.Column(i, &v); err != nil {
323334
return err
324335
}
325336
if v.Valid {
@@ -328,15 +339,18 @@ func (r *rows) Next(dest []driver.Value) error {
328339
dest[i] = nil
329340
}
330341
case sppb.TypeCode_DATE:
331-
_, isNull := col.Value.Kind.(*structpb.Value_NullValue)
332-
if isNull {
342+
var col spanner.GenericColumnValue
343+
if err := row.Column(i, &col); err != nil {
344+
return err
345+
}
346+
if _, ok := col.Value.Kind.(*structpb.Value_NullValue); ok {
333347
dest[i] = nil
334348
} else {
335349
dest[i] = col.Value.GetStringValue()
336350
}
337351
case sppb.TypeCode_TIMESTAMP:
338352
var v spanner.NullTime
339-
if err := col.Decode(&v); err != nil {
353+
if err := row.Column(i, &v); err != nil {
340354
return err
341355
}
342356
if v.Valid {
@@ -345,85 +359,85 @@ func (r *rows) Next(dest []driver.Value) error {
345359
dest[i] = nil
346360
}
347361
case sppb.TypeCode_ARRAY:
348-
switch col.Type.ArrayElementType.Code {
362+
switch r.colTypes[i].ArrayElementType.Code {
349363
case sppb.TypeCode_INT64, sppb.TypeCode_ENUM:
350364
if r.decodeToNativeArrays {
351365
var v []int64
352-
if err := col.Decode(&v); err != nil {
366+
if err := row.Column(i, &v); err != nil {
353367
return err
354368
}
355369
dest[i] = v
356370
} else {
357371
var v []spanner.NullInt64
358-
if err := col.Decode(&v); err != nil {
372+
if err := row.Column(i, &v); err != nil {
359373
return err
360374
}
361375
dest[i] = v
362376
}
363377
case sppb.TypeCode_FLOAT32:
364378
if r.decodeToNativeArrays {
365379
var v []float32
366-
if err := col.Decode(&v); err != nil {
380+
if err := row.Column(i, &v); err != nil {
367381
return err
368382
}
369383
dest[i] = v
370384
} else {
371385
var v []spanner.NullFloat32
372-
if err := col.Decode(&v); err != nil {
386+
if err := row.Column(i, &v); err != nil {
373387
return err
374388
}
375389
dest[i] = v
376390
}
377391
case sppb.TypeCode_FLOAT64:
378392
if r.decodeToNativeArrays {
379393
var v []float64
380-
if err := col.Decode(&v); err != nil {
394+
if err := row.Column(i, &v); err != nil {
381395
return err
382396
}
383397
dest[i] = v
384398
} else {
385399
var v []spanner.NullFloat64
386-
if err := col.Decode(&v); err != nil {
400+
if err := row.Column(i, &v); err != nil {
387401
return err
388402
}
389403
dest[i] = v
390404
}
391405
case sppb.TypeCode_NUMERIC:
392-
if col.Type.ArrayElementType.TypeAnnotation == sppb.TypeAnnotationCode_PG_NUMERIC {
406+
if r.colTypes[i].ArrayElementType.TypeAnnotation == sppb.TypeAnnotationCode_PG_NUMERIC {
393407
var v []spanner.PGNumeric
394-
if err := col.Decode(&v); err != nil {
408+
if err := row.Column(i, &v); err != nil {
395409
return err
396410
}
397411
dest[i] = v
398412
} else {
399413
var v []spanner.NullNumeric
400-
if err := col.Decode(&v); err != nil {
414+
if err := row.Column(i, &v); err != nil {
401415
return err
402416
}
403417
dest[i] = v
404418
}
405419
case sppb.TypeCode_STRING:
406420
if r.decodeToNativeArrays {
407421
var v []string
408-
if err := col.Decode(&v); err != nil {
422+
if err := row.Column(i, &v); err != nil {
409423
return err
410424
}
411425
dest[i] = v
412426
} else {
413427
var v []spanner.NullString
414-
if err := col.Decode(&v); err != nil {
428+
if err := row.Column(i, &v); err != nil {
415429
return err
416430
}
417431
dest[i] = v
418432
}
419433
case sppb.TypeCode_JSON:
420-
if col.Type.ArrayElementType.TypeAnnotation == sppb.TypeAnnotationCode_PG_JSONB {
434+
if r.colTypes[i].ArrayElementType.TypeAnnotation == sppb.TypeAnnotationCode_PG_JSONB {
421435
var v []spanner.PGJsonB
422-
if err := col.Decode(&v); err != nil {
436+
if err := row.Column(i, &v); err != nil {
423437
// Workaround for https://github.com/googleapis/google-cloud-go/pull/13602
424438
if spanner.ErrCode(err) == codes.InvalidArgument && err.Error() == "spanner: code = \"InvalidArgument\", desc = \"type *[]spanner.PGJsonB cannot be used for decoding ARRAY[JSON]\"" {
425439
var tmp []spanner.NullJSON
426-
if err := col.Decode(&tmp); err != nil {
440+
if err := row.Column(i, &tmp); err != nil {
427441
return err
428442
}
429443
v = make([]spanner.PGJsonB, 0, len(tmp))
@@ -437,82 +451,82 @@ func (r *rows) Next(dest []driver.Value) error {
437451
dest[i] = v
438452
} else {
439453
var v []spanner.NullJSON
440-
if err := col.Decode(&v); err != nil {
454+
if err := row.Column(i, &v); err != nil {
441455
return err
442456
}
443457
dest[i] = v
444458
}
445459
case sppb.TypeCode_UUID:
446460
if r.decodeToNativeArrays {
447461
var v []uuid.UUID
448-
if err := col.Decode(&v); err != nil {
462+
if err := row.Column(i, &v); err != nil {
449463
return err
450464
}
451465
dest[i] = v
452466
} else {
453467
var v []spanner.NullUUID
454-
if err := col.Decode(&v); err != nil {
468+
if err := row.Column(i, &v); err != nil {
455469
return err
456470
}
457471
dest[i] = v
458472
}
459473
case sppb.TypeCode_BYTES, sppb.TypeCode_PROTO:
460474
var v [][]byte
461-
if err := col.Decode(&v); err != nil {
475+
if err := row.Column(i, &v); err != nil {
462476
return err
463477
}
464478
dest[i] = v
465479
case sppb.TypeCode_BOOL:
466480
if r.decodeToNativeArrays {
467481
var v []bool
468-
if err := col.Decode(&v); err != nil {
482+
if err := row.Column(i, &v); err != nil {
469483
return err
470484
}
471485
dest[i] = v
472486
} else {
473487
var v []spanner.NullBool
474-
if err := col.Decode(&v); err != nil {
488+
if err := row.Column(i, &v); err != nil {
475489
return err
476490
}
477491
dest[i] = v
478492
}
479493
case sppb.TypeCode_DATE:
480494
if r.decodeToNativeArrays {
481495
var v []civil.Date
482-
if err := col.Decode(&v); err != nil {
496+
if err := row.Column(i, &v); err != nil {
483497
return err
484498
}
485499
dest[i] = v
486500
} else {
487501
var v []spanner.NullDate
488-
if err := col.Decode(&v); err != nil {
502+
if err := row.Column(i, &v); err != nil {
489503
return err
490504
}
491505
dest[i] = v
492506
}
493507
case sppb.TypeCode_TIMESTAMP:
494508
if r.decodeToNativeArrays {
495509
var v []time.Time
496-
if err := col.Decode(&v); err != nil {
510+
if err := row.Column(i, &v); err != nil {
497511
return err
498512
}
499513
dest[i] = v
500514
} else {
501515
var v []spanner.NullTime
502-
if err := col.Decode(&v); err != nil {
516+
if err := row.Column(i, &v); err != nil {
503517
return err
504518
}
505519
dest[i] = v
506520
}
507521
default:
508522
return fmt.Errorf("unsupported array element type ARRAY<%v>, "+
509523
"use spannerdriver.ExecOptions{DecodeOption: spannerdriver.DecodeOptionProto} "+
510-
"to return the underlying protobuf value", col.Type.ArrayElementType.Code)
524+
"to return the underlying protobuf value", r.colTypes[i].ArrayElementType.Code)
511525
}
512526
default:
513527
return fmt.Errorf("unsupported type %v, "+
514528
"use spannerdriver.ExecOptions{DecodeOption: spannerdriver.DecodeOptionProto} "+
515-
"to return the underlying protobuf value", col.Type.Code)
529+
"to return the underlying protobuf value", r.colTypes[i].Code)
516530
}
517531
}
518532
return nil

0 commit comments

Comments
 (0)