github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/parse_array.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package tree 12 13 import ( 14 "bytes" 15 "strings" 16 "unicode" 17 "unicode/utf8" 18 19 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 20 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 21 "github.com/cockroachdb/cockroach/pkg/sql/types" 22 "github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented" 23 ) 24 25 var enclosingError = pgerror.Newf(pgcode.InvalidTextRepresentation, "array must be enclosed in { and }") 26 var extraTextError = pgerror.Newf(pgcode.InvalidTextRepresentation, "extra text after closing right brace") 27 var nestedArraysNotSupportedError = unimplemented.NewWithIssueDetail(32552, "strcast", "nested arrays not supported") 28 var malformedError = pgerror.Newf(pgcode.InvalidTextRepresentation, "malformed array") 29 30 var isQuoteChar = func(ch byte) bool { 31 return ch == '"' 32 } 33 34 var isControlChar = func(ch byte) bool { 35 return ch == '{' || ch == '}' || ch == ',' || ch == '"' 36 } 37 38 var isElementChar = func(r rune) bool { 39 return r != '{' && r != '}' && r != ',' 40 } 41 42 // gobbleString advances the parser for the remainder of the current string 43 // until it sees a non-escaped termination character, as specified by 44 // isTerminatingChar, returning the resulting string, not including the 45 // termination character. 46 func (p *parseState) gobbleString(isTerminatingChar func(ch byte) bool) (out string, err error) { 47 var result bytes.Buffer 48 start := 0 49 i := 0 50 for i < len(p.s) && !isTerminatingChar(p.s[i]) { 51 // In these strings, we just encode directly the character following a 52 // '\', even if it would normally be an escape sequence. 53 if i < len(p.s) && p.s[i] == '\\' { 54 result.WriteString(p.s[start:i]) 55 i++ 56 if i < len(p.s) { 57 result.WriteByte(p.s[i]) 58 i++ 59 } 60 start = i 61 } else { 62 i++ 63 } 64 } 65 if i >= len(p.s) { 66 return "", malformedError 67 } 68 result.WriteString(p.s[start:i]) 69 p.s = p.s[i:] 70 return result.String(), nil 71 } 72 73 type parseState struct { 74 s string 75 ctx ParseTimeContext 76 result *DArray 77 t *types.T 78 } 79 80 func (p *parseState) advance() { 81 _, l := utf8.DecodeRuneInString(p.s) 82 p.s = p.s[l:] 83 } 84 85 func (p *parseState) eatWhitespace() { 86 for unicode.IsSpace(p.peek()) { 87 p.advance() 88 } 89 } 90 91 func (p *parseState) peek() rune { 92 r, _ := utf8.DecodeRuneInString(p.s) 93 return r 94 } 95 96 func (p *parseState) eof() bool { 97 return len(p.s) == 0 98 } 99 100 func (p *parseState) parseQuotedString() (string, error) { 101 return p.gobbleString(isQuoteChar) 102 } 103 104 func (p *parseState) parseUnquotedString() (string, error) { 105 out, err := p.gobbleString(isControlChar) 106 if err != nil { 107 return "", err 108 } 109 return strings.TrimSpace(out), nil 110 } 111 112 func (p *parseState) parseElement() error { 113 var next string 114 var err error 115 r := p.peek() 116 switch r { 117 case '{': 118 return nestedArraysNotSupportedError 119 case '"': 120 p.advance() 121 next, err = p.parseQuotedString() 122 if err != nil { 123 return err 124 } 125 p.advance() 126 default: 127 if !isElementChar(r) { 128 return malformedError 129 } 130 next, err = p.parseUnquotedString() 131 if err != nil { 132 return err 133 } 134 if strings.EqualFold(next, "null") { 135 return p.result.Append(DNull) 136 } 137 } 138 139 d, err := ParseAndRequireString(p.t, next, p.ctx) 140 if err != nil { 141 return err 142 } 143 return p.result.Append(d) 144 } 145 146 // ParseDArrayFromString parses the string-form of constructing arrays, handling 147 // cases such as `'{1,2,3}'::INT[]`. The input type t is the type of the 148 // parameter of the array to parse. 149 func ParseDArrayFromString(ctx ParseTimeContext, s string, t *types.T) (*DArray, error) { 150 ret, err := doParseDArrayFromString(ctx, s, t) 151 if err != nil { 152 return ret, makeParseError(s, types.MakeArray(t), err) 153 } 154 return ret, nil 155 } 156 157 // doParseDArraryFromString does most of the work of ParseDArrayFromString, 158 // except the error it returns isn't prettified as a parsing error. 159 func doParseDArrayFromString(ctx ParseTimeContext, s string, t *types.T) (*DArray, error) { 160 parser := parseState{ 161 s: s, 162 ctx: ctx, 163 result: NewDArray(t), 164 t: t, 165 } 166 167 parser.eatWhitespace() 168 if parser.peek() != '{' { 169 return nil, enclosingError 170 } 171 parser.advance() 172 parser.eatWhitespace() 173 if parser.peek() != '}' { 174 if err := parser.parseElement(); err != nil { 175 return nil, err 176 } 177 parser.eatWhitespace() 178 for parser.peek() == ',' { 179 parser.advance() 180 parser.eatWhitespace() 181 if err := parser.parseElement(); err != nil { 182 return nil, err 183 } 184 } 185 } 186 parser.eatWhitespace() 187 if parser.eof() { 188 return nil, enclosingError 189 } 190 if parser.peek() != '}' { 191 return nil, malformedError 192 } 193 parser.advance() 194 parser.eatWhitespace() 195 if !parser.eof() { 196 return nil, extraTextError 197 } 198 199 return parser.result, nil 200 }