Skip to content

Commit 693c1fe

Browse files
authored
Merge pull request #1808 from dolthub/bh/escape-json-special-chars
escape special characters in strings
2 parents 6108505 + cd2c883 commit 693c1fe

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

sql/types/json_encode.go

+43-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,27 @@ import (
88
"strconv"
99
)
1010

11+
var isEscaped = [256]bool{}
12+
var escapeSeq = [256][]byte{}
13+
14+
func init() {
15+
isEscaped[uint8('\b')] = true
16+
isEscaped[uint8('\f')] = true
17+
isEscaped[uint8('\n')] = true
18+
isEscaped[uint8('\r')] = true
19+
isEscaped[uint8('\t')] = true
20+
isEscaped[uint8('"')] = true
21+
isEscaped[uint8('\\')] = true
22+
23+
escapeSeq[uint8('\b')] = []byte("\\b")
24+
escapeSeq[uint8('\f')] = []byte("\\f")
25+
escapeSeq[uint8('\n')] = []byte("\\n")
26+
escapeSeq[uint8('\r')] = []byte("\\r")
27+
escapeSeq[uint8('\t')] = []byte("\\t")
28+
escapeSeq[uint8('"')] = []byte("\\\"")
29+
escapeSeq[uint8('\\')] = []byte("\\\\")
30+
}
31+
1132
type NoCopyBuilder struct {
1233
buffers [][]byte
1334
curr int
@@ -191,7 +212,28 @@ func writeMarshalledValue(writer io.Writer, val interface{}) error {
191212

192213
case string:
193214
writer.Write([]byte{'"'})
194-
writer.Write([]byte(val))
215+
// iterate over each rune in the string to escape any special characters
216+
start := 0
217+
for i, r := range val {
218+
if r > '\\' {
219+
continue
220+
}
221+
222+
b := uint8(r)
223+
if isEscaped[b] {
224+
if start != i {
225+
writer.Write([]byte(val[start:i]))
226+
}
227+
228+
writer.Write(escapeSeq[b])
229+
start = i + 1
230+
}
231+
}
232+
233+
if start != len(val) {
234+
writer.Write([]byte(val[start:]))
235+
}
236+
195237
writer.Write([]byte{'"'})
196238
return nil
197239

sql/types/json_encode_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,30 @@ func TestMarshalToMySqlString(t *testing.T) {
6464
},
6565
expected: `{"baz": "qux", "foo": "bar"}`,
6666
},
67+
{
68+
name: "string formatting",
69+
val: []string{
70+
`simple`,
71+
`With "quotes"`,
72+
`With "quotes" not at the end`,
73+
`with 'single quotes'`,
74+
`with
75+
newlines
76+
`,
77+
`with \n escaped newline`,
78+
`with `,
79+
},
80+
expected: `["simple", "With \"quotes\"", "With \"quotes\" not at the end", "with 'single quotes'", "with\nnewlines\n", "with \\n escaped newline", "with\t"]`,
81+
},
82+
{
83+
name: "complicated string",
84+
val: []string{
85+
`{
86+
"nested": "json",
87+
"nested_escapedQuotes": "here \"you\" go"
88+
}`},
89+
expected: `["{\n\t\"nested\": \"json\",\n\t\"nested_escapedQuotes\": \"here \\\"you\\\" go\"\n}"]`,
90+
},
6791
}
6892

6993
for _, test := range tests {

0 commit comments

Comments
 (0)