Repository: influxdata/influxdb-client-go (go)

Issue:
`(*api.QueryTableResult).Record().Table()` doesn't return the right value when compared with `(*api.QueryTableResult).Record().String()`
<!--

Thank you for reporting a bug. 

* Please add a :+1: or comment on a similar existing bug report instead of opening a new one.
    * https://github.com/influxdata/influxdb-client-go/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+is%3Aclosed+sort%3Aupdated-desc+label%3Abug+
* Please check whether the bug can be reproduced with the latest release.
* The fastest way to fix a bug is to open a Pull Request.
    * https://github.com/influxdata/influxdb-client-go/pulls

-->

__Steps to reproduce:__
List the minimal actions needed to reproduce the behavior.

Writer:
```go
var client influxdb.Client
writeAPI := client.WriteAPIBlocking(org, "public")
point := influxdb2.NewPoint(
  "DEBUG",
  map[string]string{
	"request_id":  "900f983d-d90d-4bee-8cc7-db0dbddc1381",
	"application": "core",
	"environment": "UNSET",
  },
  map[string]interface{}{
	"language": "Hello",
	"message":  "61207461736b206a7573742073746172746564",
	"body":     "7b2248656c6c6f223a2248656c6c6f227d",
  },
  time.Now(),
)
err = writeAPI.WritePoint(ctx, point)
```

Query for reader:
```
from(bucket: "public")
|> range(start: 0)
|> sort(columns: ["_time"])
|> group(columns: ["request_id", "_time"])
|> filter(fn: (r) => r["_measurement"] == "DEBUG")
|> yield()
```

Then:
```go
rows, err := queryAPI.Query(ctx, query)

for rows.Next() {
  fmt.Println(rows.Record().Table())
  fmt.Println(rows.Record().String())
}
```

__Expected behavior:__
`Table()` should reflect the `table` value from the `String()` method. So, suppose I got this from `rows.Record().String()`:
```
environment:UNSET,request_id:900f983d-d90d-4bee-8cc7-db0dbddc1381,_start:1970-01-01 00:00:00 +0000 UTC,_stop:2022-01-13 15:11:04.5872657 +0000 UTC,_time:2022-01-13 14:34:00.5584149 +0000 UTC,_field:message,application:worker,result:_result,table:2,_value:61207461736b206a7573742073746172746564,_measurement:DEBUG
```

Then `rows.Record().Table()` should returns 2 as the value on the `table` key is 2, right?

__Actual behavior:__
Instead, it returns 0.
```
0
environment:UNSET,request_id:900f983d-d90d-4bee-8cc7-db0dbddc1381,_start:1970-01-01 00:00:00 +0000 UTC,_stop:2022-01-13 15:11:04.5872657 +0000 UTC,_time:2022-01-13 14:34:00.5584149 +0000 UTC,_field:message,application:worker,result:_result,table:2,_value:61207461736b206a7573742073746172746564,_measurement:DEBUG
```

__Specifications:__
 - Client Version: 2.6.0
 - InfluxDB Version: Docker 2.1.1-alpine
 - Platform: Debian 10


Pull request description:
fix: FluxRecord.Table() returns value of the table column
Closes #301

## Proposed Changes

- `FluxRecord.Table()` returns value of the `table` column.
- `FluxRecord.String()` returns values sorted  by key.

## Checklist

<!-- Checkboxes below this note can be erased if not applicable to your Pull Request. -->

- [X] CHANGELOG.md updated
- [X] Rebased/mergeable
- [X] A test has been added if appropriate
- [X] Tests pass
- [X] Commit messages are in [semantic format](https://seesparkbox.com/foundry/semantic_commit_messages)



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 879ce78..fdb19f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,11 @@
 ## unreleased
 ### Features 
-- [#297](https://github.com/influxdata/influxdb-client-go/pull/297),[#298](https://github.com/influxdata/influxdb-client-go/pull/298) Optimized `WriteRecord` of [WriteAPIBlocking](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#WriteAPIBlocking). 
+- [#297](https://github.com/influxdata/influxdb-client-go/pull/297),[#298](https://github.com/influxdata/influxdb-client-go/pull/298) Optimized `WriteRecord` of [WriteAPIBlocking](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#WriteAPIBlocking). Custom batch can be written by single argument. 
 
 ### Bug fixes
 - [#294](https://github.com/influxdata/influxdb-client-go/pull/294)  `WritePoint` and `WriteRecord` of [WriteAPIBlocking](https://pkg.go.dev/github.com/influxdata/influxdb-client-go/v2/api#WriteAPIBlocking) returns always full error information.
-
+- [300](https://github.com/influxdata/influxdb-client-go/pull/300) Closing the response body after write batch.
+- [302](https://github.com/influxdata/influxdb-client-go/pull/302) FluxRecord.Table() returns value of the table column.
 
 ## 2.6.0[2021-11-26]
 ### Features
diff --git a/api/query/table.go b/api/query/table.go
index 7d8bf6c..58cd3d9 100644
--- a/api/query/table.go
+++ b/api/query/table.go
@@ -7,6 +7,7 @@ package query
 
 import (
 	"fmt"
+	"sort"
 	"strings"
 	"time"
 )
@@ -146,9 +147,10 @@ func NewFluxRecord(table int, values map[string]interface{}) *FluxRecord {
 	return &FluxRecord{table: table, values: values}
 }
 
-// Table returns index of the table record belongs to
+// Table returns value of the table column
+// It returns zero if the table column is not found
 func (r *FluxRecord) Table() int {
-	return r.table
+	return int(intValue(r.values, "table"))
 }
 
 // Start returns the inclusive lower time bound of all records in the current table.
@@ -204,15 +206,23 @@ func (r *FluxRecord) ValueByKey(key string) interface{} {
 
 // String returns FluxRecord string dump
 func (r *FluxRecord) String() string {
-	var buffer strings.Builder
+	if len(r.values) == 0 {
+		return ""
+	}
+
 	i := 0
-	for k, v := range r.values {
-		if i > 0 {
-			buffer.WriteString(",")
-		}
-		buffer.WriteString(fmt.Sprintf("%s:%v", k, v))
+	keys := make([]string, len(r.values))
+	for k := range r.values {
+		keys[i] = k
 		i++
 	}
+	sort.Strings(keys)
+	var buffer strings.Builder
+	buffer.WriteString(fmt.Sprintf("%s:%v", keys[0], r.values[keys[0]]))
+	for _, k := range keys[1:] {
+		buffer.WriteString(",")
+		buffer.WriteString(fmt.Sprintf("%s:%v", k, r.values[k]))
+	}
 	return buffer.String()
 }
 
@@ -227,7 +237,7 @@ func timeValue(values map[string]interface{}, key string) time.Time {
 	return time.Time{}
 }
 
-// timeValue returns string value from values map according to the key
+// stringValue returns string value from values map according to the key
 // Empty string is returned if key is not found
 func stringValue(values map[string]interface{}, key string) string {
 	if val, ok := values[key]; ok {
@@ -237,3 +247,14 @@ func stringValue(values map[string]interface{}, key string) string {
 	}
 	return ""
 }
+
+// intValue returns int64 value from values map according to the key
+// Zero value is returned if key is not found
+func intValue(values map[string]interface{}, key string) int64 {
+	if val, ok := values[key]; ok {
+		if i, ok := val.(int64); ok {
+			return i
+		}
+	}
+	return 0
+}


Test patch:
diff --git a/api/query/table_test.go b/api/query/table_test.go
index 6adefb7..96beeec 100644
--- a/api/query/table_test.go
+++ b/api/query/table_test.go
@@ -70,7 +70,7 @@ func TestRecord(t *testing.T) {
 	record := &FluxRecord{table: 2,
 		values: map[string]interface{}{
 			"result":       "_result",
-			"_table":       int64(0),
+			"table":        int64(2),
 			"_start":       mustParseTime("2020-02-17T22:19:49.747562847Z"),
 			"_stop":        mustParseTime("2020-02-18T22:19:49.747562847Z"),
 			"_time":        mustParseTime("2020-02-18T10:34:08.135814545Z"),
diff --git a/api/query_test.go b/api/query_test.go
index 971df3a..8dd8654 100644
--- a/api/query_test.go
+++ b/api/query_test.go
@@ -35,8 +35,8 @@ func TestQueryCVSResultSingleTable(t *testing.T) {
 #group,false,false,true,true,false,false,true,true,true,true
 #default,_result,,,,,,,,,
 ,result,table,_start,_stop,_time,_value,_field,_measurement,a,b
-,,0,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T10:34:08.135814545Z,1.4,f,test,1,adsfasdf
-,,0,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T22:08:44.850214724Z,6.6,f,test,1,adsfasdf
+,,1,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T10:34:08.135814545Z,1.4,f,test,1,adsfasdf
+,,1,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T22:08:44.850214724Z,6.6,f,test,1,adsfasdf
 
 `
 	expectedTable := query.NewFluxTableMetadataFull(0,
@@ -56,7 +56,7 @@ func TestQueryCVSResultSingleTable(t *testing.T) {
 	expectedRecord1 := query.NewFluxRecord(0,
 		map[string]interface{}{
 			"result":       "_result",
-			"table":        int64(0),
+			"table":        int64(1),
 			"_start":       mustParseTime("2020-02-17T22:19:49.747562847Z"),
 			"_stop":        mustParseTime("2020-02-18T22:19:49.747562847Z"),
 			"_time":        mustParseTime("2020-02-18T10:34:08.135814545Z"),
@@ -71,7 +71,7 @@ func TestQueryCVSResultSingleTable(t *testing.T) {
 	expectedRecord2 := query.NewFluxRecord(0,
 		map[string]interface{}{
 			"result":       "_result",
-			"table":        int64(0),
+			"table":        int64(1),
 			"_start":       mustParseTime("2020-02-17T22:19:49.747562847Z"),
 			"_stop":        mustParseTime("2020-02-18T22:19:49.747562847Z"),
 			"_time":        mustParseTime("2020-02-18T22:08:44.850214724Z"),
@@ -100,6 +100,8 @@ func TestQueryCVSResultSingleTable(t *testing.T) {
 	assert.False(t, queryResult.tableChanged)
 	require.NotNil(t, queryResult.Record())
 	require.Equal(t, queryResult.Record(), expectedRecord2)
+	assert.Equal(t, "_result", queryResult.Record().Result())
+	assert.Equal(t, 1, queryResult.Record().Table())
 
 	require.False(t, queryResult.Next())
 	require.Nil(t, queryResult.Err())
@@ -383,6 +385,7 @@ func TestQueryCVSResultMultiTables(t *testing.T) {
 	require.NotNil(t, queryResult.Record())
 	require.Equal(t, queryResult.Record(), expectedRecord42)
 	assert.Equal(t, "_result4", queryResult.Record().Result())
+	assert.Equal(t, 3, queryResult.Record().Table())
 
 	require.False(t, queryResult.Next())
 	require.Nil(t, queryResult.Err())