-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathencode.go
More file actions
235 lines (208 loc) · 6.93 KB
/
encode.go
File metadata and controls
235 lines (208 loc) · 6.93 KB
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in licenses/BSD-vitess.txt.
// Portions of this file are additionally subject to the following
// license and copyright.
//
// Copyright 2020 The Cockroach Authors.
//
// Use of this software is governed by the CockroachDB Software License
// included in the /LICENSE file.
// This code was derived from https://github.com/youtube/vitess.
// Package lexbase contains utilities for lexing sql.
package lexbase
import (
"bytes"
"unicode/utf8"
"github.com/cockroachdb/cockroachdb-parser/pkg/util/stringencoding"
)
// EncodeFlags influence the formatting of strings and identifiers.
type EncodeFlags int
// HasFlags tests whether the given flags are set.
func (f EncodeFlags) HasFlags(subset EncodeFlags) bool {
return f&subset == subset
}
const (
// EncNoFlags indicates nothing special should happen while encoding.
EncNoFlags EncodeFlags = 0
// EncBareStrings indicates that strings will be rendered without
// wrapping quotes if they contain no special characters.
EncBareStrings EncodeFlags = 1 << iota
// EncBareIdentifiers indicates that identifiers will be rendered
// without wrapping quotes.
EncBareIdentifiers
// EncBareReservedKeywords indicates that reserved keywords will be rendered
// without wrapping quotes.
EncBareReservedKeywords
// EncAlwaysQuoted makes sure the string is always wrapped with quotes.
// This is used only to construct a statement against Oracle source,
// as Oracle is case insensitive if object name is not quoted.
EncAlwaysQuoted
// EncFirstFreeFlagBit needs to remain unused; it is used as base
// bit offset for tree.FmtFlags.
EncFirstFreeFlagBit
)
// EncodeRestrictedSQLIdent writes the identifier in s to buf. The
// identifier is quoted if either the flags ask for it, the identifier
// contains special characters, or the identifier is a reserved SQL
// keyword.
func EncodeRestrictedSQLIdent(buf *bytes.Buffer, s string, flags EncodeFlags) {
if !flags.HasFlags(EncAlwaysQuoted) && (flags.HasFlags(EncBareIdentifiers) || (!isReservedKeyword(s) && IsBareIdentifier(s))) {
buf.WriteString(s)
return
}
EncodeEscapedSQLIdent(buf, s)
}
// EncodeUnrestrictedSQLIdent writes the identifier in s to buf.
// The identifier is only quoted if the flags don't tell otherwise and
// the identifier contains special characters.
func EncodeUnrestrictedSQLIdent(buf *bytes.Buffer, s string, flags EncodeFlags) {
if !flags.HasFlags(EncAlwaysQuoted) && (flags.HasFlags(EncBareIdentifiers) || IsBareIdentifier(s)) {
buf.WriteString(s)
return
}
EncodeEscapedSQLIdent(buf, s)
}
// EscapeSQLIdent ensures that the potential identifier in s is fully
// quoted, so that any special character it contains is not at risk
// of "spilling" in the surrounding syntax.
func EscapeSQLIdent(s string) string {
var buf bytes.Buffer
EncodeEscapedSQLIdent(&buf, s)
return buf.String()
}
// EncodeEscapedSQLIdent writes the identifier in s to buf. The
// identifier is always quoted. Double quotes inside the identifier
// are escaped.
func EncodeEscapedSQLIdent(buf *bytes.Buffer, s string) {
buf.WriteByte('"')
start := 0
for i, n := 0, len(s); i < n; i++ {
ch := s[i]
// The only character that requires escaping is a double quote.
if ch == '"' {
if start != i {
buf.WriteString(s[start:i])
}
start = i + 1
buf.WriteByte(ch)
buf.WriteByte(ch) // add extra copy of ch
}
}
if start < len(s) {
buf.WriteString(s[start:])
}
buf.WriteByte('"')
}
const (
minPrintableChar = 0x20 // ' '
maxPrintableChar = 0x7E // '~'
)
var mustQuoteMap = [maxPrintableChar + 1]bool{
' ': true,
',': true,
'{': true,
'}': true,
}
// EncodeSQLString writes a string literal to buf. All unicode and
// non-printable characters are escaped.
func EncodeSQLString(buf *bytes.Buffer, in string) {
EncodeSQLStringWithFlags(buf, in, EncNoFlags)
}
// EscapeSQLString returns an escaped SQL representation of the given
// string. This is suitable for safely producing a SQL string valid
// for input to the parser.
func EscapeSQLString(in string) string {
var buf bytes.Buffer
EncodeSQLString(&buf, in)
return buf.String()
}
// EncodeSQLStringWithFlags writes a string literal to buf. All
// unicode and non-printable characters are escaped. flags controls
// the output format: if encodeBareString is set, the output string
// will not be wrapped in quotes if the strings contains no special
// characters.
func EncodeSQLStringWithFlags(buf *bytes.Buffer, in string, flags EncodeFlags) {
// See http://www.postgresql.org/docs/9.4/static/sql-syntax-lexical.html
start := 0
escapedString := false
bareStrings := flags.HasFlags(EncBareStrings)
// Loop through each unicode code point.
for i, r := range in {
if i < start {
continue
}
ch := byte(r)
if r >= minPrintableChar && r <= maxPrintableChar {
if mustQuoteMap[ch] {
// We have to quote this string - ignore bareStrings setting
bareStrings = false
}
if !stringencoding.NeedEscape(ch) && ch != '\'' {
continue
}
}
if !escapedString {
buf.WriteString("e'") // begin e'xxx' string
escapedString = true
}
buf.WriteString(in[start:i])
ln := utf8.RuneLen(r)
if ln < 0 {
start = i + 1
} else {
start = i + ln
}
stringencoding.EncodeEscapedChar(buf, in, r, ch, i, '\'')
}
quote := !escapedString && !bareStrings
if quote {
buf.WriteByte('\'') // begin 'xxx' string if nothing was escaped
}
if start < len(in) {
buf.WriteString(in[start:])
}
if escapedString || quote {
buf.WriteByte('\'')
}
}
// EncodeSQLBytes encodes the SQL byte array in 'in' to buf, to a
// format suitable for re-scanning. We don't use a straightforward hex
// encoding here with x'...' because the result would be less
// compact. We are trading a little more time during the encoding to
// have a little less bytes on the wire.
func EncodeSQLBytes(buf *bytes.Buffer, in string) {
buf.WriteString("b'")
EncodeSQLBytesInner(buf, in)
buf.WriteByte('\'')
}
// EncodeSQLBytesInner is like EncodeSQLBytes but does not include the
// outer quote delimiter and the 'b' prefix.
func EncodeSQLBytesInner(buf *bytes.Buffer, in string) {
start := 0
// Loop over the bytes of the string (i.e., don't use range over unicode
// code points).
for i, n := 0, len(in); i < n; i++ {
ch := in[i]
if encodedChar := stringencoding.EncodeMap[ch]; encodedChar != stringencoding.DontEscape {
buf.WriteString(in[start:i])
buf.WriteByte('\\')
buf.WriteByte(encodedChar)
start = i + 1
} else if ch == '\'' {
// We can't just fold this into stringencoding.EncodeMap because
// stringencoding.EncodeMap is also used for strings which
// aren't quoted with single-quotes
buf.WriteString(in[start:i])
buf.WriteByte('\\')
buf.WriteByte(ch)
start = i + 1
} else if ch < 0x20 || ch >= 0x7F {
buf.WriteString(in[start:i])
// Escape non-printable characters.
buf.Write(stringencoding.HexMap[ch])
start = i + 1
}
}
buf.WriteString(in[start:])
}