@@ -3,6 +3,7 @@ package main
3
3
import (
4
4
"context"
5
5
"encoding/binary"
6
+ "errors"
6
7
"flag"
7
8
"fmt"
8
9
"os"
@@ -14,9 +15,11 @@ import (
14
15
)
15
16
16
17
var (
17
- connStr string
18
- selGenericSQL string
19
- reqFlags = []string {"dsn" , "sql" }
18
+ connStr string
19
+ selGenericSQL string
20
+ reqFlags = []string {"dsn" , "sql" }
21
+ ci = pgtype .NewConnInfo ()
22
+ errUnsupportedOID = errors .New ("unsupported OID" )
20
23
)
21
24
22
25
func init () {
@@ -54,6 +57,43 @@ func checkFlags() error {
54
57
return nil
55
58
}
56
59
60
+ // getRaw returns a raw bytes of value and a new type start.
61
+ func getRaw (bytes []byte , start , len uint32 ) ([]byte , uint32 ) {
62
+ if len == 0xFFFFFFFF {
63
+ len = 0
64
+ }
65
+
66
+ return bytes [start + 8 : start + 8 + len ], start + len + 8
67
+ }
68
+
69
+ // scanRaw returns the value and a name of the type.
70
+ func scanRaw (oid uint32 , bytes []byte , len uint32 ) (value interface {}, name string , err error ) {
71
+ dt , ok := ci .DataTypeForOID (oid )
72
+ if ! ok {
73
+ return nil , "" , errUnsupportedOID
74
+ }
75
+ name = dt .Name
76
+
77
+ if len == 0xFFFFFFFF {
78
+ value = "(NULL)"
79
+ } else {
80
+ if bd , ok := dt .Value .(pgtype.BinaryDecoder ); ok {
81
+ if err := bd .DecodeBinary (ci , bytes ); err != nil {
82
+ return nil , "" , err
83
+ }
84
+ } else {
85
+ return nil , "" , fmt .Errorf ("%T is not binary pgtype.BinaryDecoder" , dt .Value )
86
+ }
87
+
88
+ value = dt .Value .Get ()
89
+ if value == "(NULL)" {
90
+ value = `"(NULL)"`
91
+ }
92
+ }
93
+
94
+ return
95
+ }
96
+
57
97
func main () {
58
98
flag .Parse ()
59
99
if err := checkFlags (); err != nil {
@@ -69,6 +109,16 @@ func main() {
69
109
}
70
110
defer pool .Close ()
71
111
112
+ genericText := & pgtype.GenericText {}
113
+ if err := pool .QueryRow (
114
+ ctx ,
115
+ selGenericSQL ,
116
+ pgx.QueryResultFormats {pgx .TextFormatCode },
117
+ ).Scan (genericText ); err != nil {
118
+ fmt .Println (err )
119
+ os .Exit (1 )
120
+ }
121
+
72
122
genericBinary := & pgtype.GenericBinary {}
73
123
if err := pool .QueryRow (
74
124
ctx ,
@@ -79,6 +129,7 @@ func main() {
79
129
os .Exit (1 )
80
130
}
81
131
132
+ fmt .Printf ("Text: %v\n " , genericText .String )
82
133
fmt .Printf ("Binary: %v\n " , genericBinary .Bytes )
83
134
84
135
rawNumOfFields := genericBinary .Bytes [:4 ]
@@ -88,73 +139,45 @@ func main() {
88
139
rawNumOfFields ,
89
140
numOfFieldsUint32 ,
90
141
)
91
- ci := pgtype .NewConnInfo ()
92
142
93
143
// The first 4 bytes are the number of fields - 2
94
144
// Then for each field:
95
145
// 4 bytes - OID
96
146
// 4 bytes - length of value
97
147
// x bytes - value
98
- typeStart := 4
148
+ typeStart := uint32 ( 4 )
99
149
for i := uint32 (0 ); i < numOfFieldsUint32 ; i ++ {
100
150
rawOID := genericBinary .Bytes [typeStart : typeStart + 4 ]
101
151
oid := binary .BigEndian .Uint32 (rawOID )
152
+
102
153
rawValLen := genericBinary .Bytes [typeStart + 4 : typeStart + 8 ]
103
154
valLen := binary .BigEndian .Uint32 (rawValLen )
104
- rawVal := genericBinary .Bytes [typeStart + 8 : typeStart + 8 + int (valLen )]
105
- typeStart += int (valLen ) + 8
106
-
107
- var scanned interface {}
108
- var typeName string
109
-
110
- switch oid {
111
- case pgtype .Int4OID :
112
- typeName = "int4 (int32)"
113
- var dst int32
114
- if err := ci .Scan (oid , pgtype .BinaryFormatCode , rawVal , & dst ); err != nil {
115
- fmt .Println (err )
116
- os .Exit (1 )
117
- }
118
-
119
- scanned = dst
120
-
121
- case pgtype .Float8OID :
122
- typeName = "float8 (double)"
123
- var dst float64
124
- if err := ci .Scan (oid , pgtype .BinaryFormatCode , rawVal , & dst ); err != nil {
125
- fmt .Println (err )
126
- os .Exit (1 )
127
- }
128
155
129
- scanned = dst
156
+ var rawVal []byte
157
+ rawVal , typeStart = getRaw (genericBinary .Bytes , typeStart , valLen )
130
158
131
- case pgtype .TextOID :
132
- typeName = "text"
133
- var dst string
134
- if err := ci .Scan (oid , pgtype .BinaryFormatCode , rawVal , & dst ); err != nil {
159
+ value , typeName , err := scanRaw (oid , rawVal , valLen )
160
+ if err != nil {
161
+ if ! errors .Is (err , errUnsupportedOID ) {
135
162
fmt .Println (err )
136
163
os .Exit (1 )
137
164
}
138
165
139
- scanned = dst
140
-
141
- default :
142
- typeName = "Unsupported"
143
- scanned = "Unsupported"
166
+ value , typeName = "Unsupported" , "Unsupported"
144
167
}
145
168
146
169
fmt .Printf (
147
170
`Field %d:
148
- OID: %d
171
+ OID: %d (%v)
149
172
Type %s
150
- Length: %d
151
- Value: %v
173
+ Length: %d (%v)
174
+ Value: %v (%v)
152
175
` ,
153
176
i ,
154
- oid ,
177
+ oid , rawOID ,
155
178
typeName ,
156
- valLen ,
157
- scanned ,
179
+ valLen , rawValLen ,
180
+ value , rawVal ,
158
181
)
159
182
}
160
183
0 commit comments