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  }