github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/lex/predicates.go (about) 1 // Copyright 2017 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 lex 12 13 import ( 14 "unicode" 15 "unicode/utf8" 16 ) 17 18 // isASCII returns true if all the characters in s are ASCII. 19 func isASCII(s string) bool { 20 for _, c := range s { 21 if c > unicode.MaxASCII { 22 return false 23 } 24 } 25 return true 26 } 27 28 // IsDigit returns true if the character is between 0 and 9. 29 func IsDigit(ch int) bool { 30 return ch >= '0' && ch <= '9' 31 } 32 33 // IsHexDigit returns true if the character is a valid hexadecimal digit. 34 func IsHexDigit(ch int) bool { 35 return (ch >= '0' && ch <= '9') || 36 (ch >= 'a' && ch <= 'f') || 37 (ch >= 'A' && ch <= 'F') 38 } 39 40 // reservedOrLookaheadKeywords are the reserved keywords plus those keywords for 41 // which we need one token of lookahead extra to determine their token type. 42 var reservedOrLookaheadKeywords = make(map[string]struct{}) 43 44 func init() { 45 for s := range reservedKeywords { 46 reservedOrLookaheadKeywords[s] = struct{}{} 47 } 48 for _, s := range []string{ 49 "between", 50 "ilike", 51 "in", 52 "like", 53 "of", 54 "ordinality", 55 "similar", 56 "time", 57 "generated", 58 } { 59 reservedOrLookaheadKeywords[s] = struct{}{} 60 } 61 } 62 63 // isReservedKeyword returns true if the keyword is reserved, or needs 64 // one extra token of lookahead. 65 func isReservedKeyword(s string) bool { 66 _, ok := reservedOrLookaheadKeywords[s] 67 return ok 68 } 69 70 // isBareIdentifier returns true if the input string is a permissible bare SQL 71 // identifier. 72 func isBareIdentifier(s string) bool { 73 if len(s) == 0 || !IsIdentStart(int(s[0])) || (s[0] >= 'A' && s[0] <= 'Z') { 74 return false 75 } 76 // Keep track of whether the input string is all ASCII. If it is, we don't 77 // have to bother running the full Normalize() function at the end, which is 78 // quite expensive. 79 isASCII := s[0] < utf8.RuneSelf 80 for i := 1; i < len(s); i++ { 81 if !IsIdentMiddle(int(s[i])) { 82 return false 83 } 84 if s[i] >= 'A' && s[i] <= 'Z' { 85 // Non-lowercase identifiers aren't permissible. 86 return false 87 } 88 if s[i] >= utf8.RuneSelf { 89 isASCII = false 90 } 91 } 92 return isASCII || NormalizeName(s) == s 93 } 94 95 // IsIdentStart returns true if the character is valid at the start of an identifier. 96 func IsIdentStart(ch int) bool { 97 return (ch >= 'A' && ch <= 'Z') || 98 (ch >= 'a' && ch <= 'z') || 99 (ch >= 128 && ch <= 255) || 100 (ch == '_') 101 } 102 103 // IsIdentMiddle returns true if the character is valid inside an identifier. 104 func IsIdentMiddle(ch int) bool { 105 return IsIdentStart(ch) || IsDigit(ch) || ch == '$' 106 }