Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"time"
)

Expand Down Expand Up @@ -88,12 +89,12 @@ func serializeValue(v interface{}, qdbType QuestDBType) (string, error) {
case Symbol:
switch val := v.(type) {
case string:
return val, nil
return quoteEscape(val, needsEscapeForSymbol, quoteSymbolFn), nil
}
case String:
switch val := v.(type) {
case string:
return fmt.Sprintf("\"%s\"", val), nil
return quoteEscape(val, needsEscapeForStr, quoteStringFn), nil
}
case Long:
switch val := v.(type) {
Expand Down Expand Up @@ -147,6 +148,37 @@ func serializeValue(v interface{}, qdbType QuestDBType) (string, error) {
return "", fmt.Errorf("type %T is not compatible with %s", v, qdbType)
}

// Quote and escape an ILP input value, returns new string that is properly quoted and escaped.
func quoteEscape(s string, needsEscape func(byte) bool, quoteFn func(*strings.Builder)) string {
var b strings.Builder
quoteFn(&b)
for i := 0; i < len(s); i++ {
if needsEscape(s[i]) {
b.WriteByte('\\')
}
b.WriteByte(s[i])
}
quoteFn(&b)

return b.String()
}

func needsEscapeForStr(c byte) bool {
return c == '\n' || c == '\r' || c == '\\' || c == '"'
}

func quoteStringFn(b *strings.Builder) {
b.WriteByte('"')
}

func needsEscapeForSymbol(c byte) bool {
return c == '\n' || c == '\r' || c == '\\' || c == ' ' || c == ',' || c == '='
}

func quoteSymbolFn(*strings.Builder) {
// no op, symbols are not quoted in ILP format
}

var supportedQDBTypes = []QuestDBType{
Boolean,
Byte,
Expand Down