-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Golang []byte in vertica? #87
Comments
I think the answer is |
The Vertica type varbinary and friends makes sense to me but this driver encodes them as hex on the way out which makes it hard to write Scan and Value function that will also work with the Postgres driver. Here is a patch I'm working on to address this:
|
Example of Value()/Scan() that works with Postgres's JSONB type. I'd like to use these same functions for Vertica's VARBINARY.
|
The strategy above is working great for JSON because it is all printable. It seems that the Vertica on the wire protocol is encoding non-printable binary values in some multi-byte sequence. For example, I can clearly see that a varbinary column has a length of 691 using a select in vsql. However, the slice returned from bedatarowmsg.Chunk() has a length of 1982. Does anyone know what this encoding is and how to decode it in Go? |
This seems to do the trick. The varbinary data is coming out of bedatarowmsg.Chunk() with non-printables escaped as backslash octals with backslash itself escaped as double backslash. index 7f6ffcb..455b4d0 100644
--- a/rows.go
+++ b/rows.go
@@ -110,8 +110,25 @@ func (r *rows) Next(dest []driver.Value) error {
dest[idx], _ = parseTimestampTZColumn(string(colVal) + r.tzOffset)
case common.ColTypeTimestampTZ:
dest[idx], _ = parseTimestampTZColumn(string(colVal))
- case common.ColTypeVarBinary, common.ColTypeLongVarBinary, common.ColTypeBinary: // to []byte - this one's easy
- dest[idx] = colVal
+ case common.ColTypeVarBinary, common.ColTypeLongVarBinary, common.ColTypeBinary:
+ // to []byte; convert escaped octal (eg \261) into byte with \\ for \
+ var out []byte
+ for len(colVal) > 0 {
+ c := colVal[0]
+ if c == '\\' {
+ if colVal[1] == '\\' {
+ colVal = colVal[2:]
+ } else {
+ x, _ := strconv.ParseInt(string(colVal[1:4]), 8, 32)
+ c = byte(x)
+ colVal = colVal[4:]
+ }
+ } else {
+ colVal = colVal[1:]
+ }
+ out = append(out, c)
+ }
+ dest[idx] = out
default:
dest[idx] = string(colVal)
} |
The challenge with the above solution is writing the varbinary values from []byte in Go using database/sql. Using param substitution with jmoiron/sqlx I got an error from Vertica. I was able to get it working by using the hex input syntax X'' and converting my []byte field to hex in the client code: fmt.Sprintf("update table1 set col1=X'%s'", hex.EncodeToString(field1)) |
Again, examining the python driver I found a solution: index 33ee85b..e2a373f 100644
--- a/msgs/febindmsg.go
+++ b/msgs/febindmsg.go
@@ -33,6 +33,7 @@ package msgs
// THE SOFTWARE.
import (
+ "bytes"
"database/sql"
"database/sql/driver"
"fmt"
@@ -85,7 +86,10 @@ func (m *FEBindMsg) Flatten() ([]byte, byte) {
case time.Time:
strVal = v.Format("2006-01-02T15:04:05.999999Z07:00")
case []uint8:
- strVal = string(v)
+ v = bytes.ReplaceAll(v, []byte("\\"), []byte("\\134"))
+ buf.appendUint32(uint32(len(v)))
+ buf.appendBytes(v)
+ continue
default:
strVal = "??HELP??"
} |
@watercraft Thanks for the discussion. There are two directions of [var]binary data transfer:
|
@watercraft UPDATE: Our team discussed the second issue in my above comment. We decide to change the server to send binary data directly, in a future release. We also have to adding a fix for python and go client because of backwards compatibility for old servers, which can be done right now. It would be a big help if you can create a pull request for the last two code snippets you provide and add some tests. Thank you! |
What is the vertica data type for golang []byte?
The text was updated successfully, but these errors were encountered: