-
Notifications
You must be signed in to change notification settings - Fork 1
/
getmany.go
120 lines (91 loc) · 2.71 KB
/
getmany.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package pgkebab
import (
"context"
"database/sql"
"fmt"
"log"
"strconv"
"time"
)
func rowsClose(rows *sql.Rows) {
_ = rows.Close()
}
func decimalAsFloat64(databaseType string, value interface{}) (float64, bool) {
if databaseType == "NUMERIC" || databaseType == "DECIMAL" {
if x, ok := value.([]uint8); ok {
f, _ := strconv.ParseFloat(string(x), 64)
return f, true
}
}
return 0, false
}
// GetMany returns sql query result as an array of map[string]string
func (l *DBLink) GetMany(sqlQuery string, args ...interface{}) (Resultset, error) {
if !l.supposedReady {
return Resultset{}, fmt.Errorf("connection not properly initialized")
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(l.executionTimeoutSeconds)*time.Second)
defer cancel()
rows, err := l.db.QueryContext(ctx, sqlQuery, args...)
if err != nil {
if l.debugPrint {
log.Printf(`pgkebab.GetMany.db.Query("%s") has failed: "%v"\n`, sqlQuery, err)
}
return Resultset{err: err}, err
}
defer rowsClose(rows)
cols, err0 := rows.Columns()
if err0 != nil {
if l.debugPrint {
log.Printf(`pgkebab.GetMany.rows.Columns("%s") has failed: "%v"\n`, sqlQuery, err0)
}
return Resultset{err: err0}, err0
}
rs := Resultset{
pointer: resultsetFirstRow,
columns: cols,
debugPrint: l.debugPrint,
}
if colTypes, err8 := rows.ColumnTypes(); err8 != nil {
if l.debugPrint {
log.Printf(`pgkebab.GetMany.rows.ColumnTypes("%s") has failed: "%v"\n`, sqlQuery, err8)
}
} else {
for _, x := range colTypes {
rs.columnTypes = append(rs.columnTypes, x.DatabaseTypeName())
}
}
howManyCols := len(cols)
// Byte array for store values
rs.records = make([]Row, 0)
for rows.Next() {
// Once Rows.Scan() expects []interface{} ( slice of interfaces ), we need one of these as a temporary buffer
fakeDest := make([]interface{}, howManyCols)
// Here we set the real []interface's address for each interface instance
realDest := make([]interface{}, howManyCols)
for i := range realDest {
fakeDest[i] = &realDest[i]
}
if er2 := rows.Scan(fakeDest...); er2 != nil {
if l.debugPrint {
log.Printf(`pgkebab.GetMany.rows.Scan("%s") has failed: "%v"\n`, sqlQuery, er2)
}
return Resultset{}, er2
}
rw := Row{tuple: make(map[string]interface{})}
for i, v := range realDest {
col := cols[i]
// Try to intercept and handle float64
// To know further: https://stackoverflow.com/questions/31946344/why-does-go-treat-a-postgresql-numeric-decimal-columns-as-uint8
if len(rs.columnTypes) >= i {
if f, ok := decimalAsFloat64(rs.columnTypes[i], v); ok {
rw.tuple[col] = f
} else {
rw.tuple[col] = v
}
}
}
rs.records = append(rs.records, rw)
}
return rs, nil
}