Browse Source

feat: support single quote string literal (#1974)

* support singlequote

Signed-off-by: Rui-Gan <1171530954@qq.com>

* fix singlequote

Signed-off-by: Rui-Gan <1171530954@qq.com>

---------

Signed-off-by: Rui-Gan <1171530954@qq.com>
Regina 1 year ago
parent
commit
6a30abee44
4 changed files with 797 additions and 25 deletions
  1. 22 6
      internal/xsql/lexical.go
  2. 1 1
      internal/xsql/parser.go
  3. 760 6
      internal/xsql/parser_test.go
  4. 14 12
      pkg/ast/token.go

+ 22 - 6
internal/xsql/lexical.go

@@ -1,4 +1,4 @@
-// Copyright 2021-2022 EMQ Technologies Co., Ltd.
+// Copyright 2021-2023 EMQ Technologies Co., Ltd.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@ func (s *Scanner) Scan() (tok ast.Token, lit string) {
 		return s.ScanIdent()
 	} else if isQuotation(ch) {
 		s.unread()
-		return s.ScanString()
+		return s.ScanString(ch == '\'')
 	} else if isDigit(ch) {
 		s.unread()
 		return s.ScanNumber(false, false)
@@ -244,15 +244,28 @@ func (s *Scanner) ScanIdent() (tok ast.Token, lit string) {
 	return ast.IDENT, buf.String()
 }
 
-func (s *Scanner) ScanString() (tok ast.Token, lit string) {
+func (s *Scanner) ScanString(isSingle bool) (tok ast.Token, lit string) {
 	var buf bytes.Buffer
 	ch := s.read()
-	buf.WriteRune(ch)
+	if ch == '\'' && isSingle {
+		buf.WriteRune('"')
+	} else {
+		buf.WriteRune(ch)
+	}
 	escape := false
 	for {
 		ch = s.read()
 		if ch == '"' && !escape {
-			buf.WriteRune(ch)
+			if !isSingle {
+				buf.WriteRune(ch)
+				break
+			} else {
+				escape = false
+				buf.WriteRune('\\')
+				buf.WriteRune(ch)
+			}
+		} else if ch == '\'' && !escape && isSingle {
+			buf.WriteRune('"')
 			break
 		} else if ch == eof {
 			return ast.BADSTRING, buf.String()
@@ -268,6 +281,9 @@ func (s *Scanner) ScanString() (tok ast.Token, lit string) {
 	if err != nil {
 		return ast.ILLEGAL, "invalid string: " + buf.String()
 	}
+	if isSingle {
+		return ast.SINGLEQUOTE, r
+	}
 	return ast.STRING, r
 }
 
@@ -396,6 +412,6 @@ func isLetter(ch rune) bool { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && c
 
 func isDigit(ch rune) bool { return ch >= '0' && ch <= '9' }
 
-func isQuotation(ch rune) bool { return ch == '"' }
+func isQuotation(ch rune) bool { return ch == '"' || ch == '\'' }
 
 func isBackquote(ch rune) bool { return ch == '`' }

+ 1 - 1
internal/xsql/parser.go

@@ -686,7 +686,7 @@ func (p *Parser) parseUnaryExpr(isSubField bool) (ast.Expr, error) {
 				return &ast.FieldRef{StreamName: ast.DefaultStream, Name: n[0]}, nil
 			}
 		}
-	} else if tok == ast.STRING {
+	} else if tok == ast.STRING || tok == ast.SINGLEQUOTE {
 		return &ast.StringLiteral{Val: lit}, nil
 	} else if tok == ast.INTEGER {
 		val, _ := strconv.Atoi(lit)

File diff suppressed because it is too large
+ 760 - 6
internal/xsql/parser_test.go


+ 14 - 12
pkg/ast/token.go

@@ -29,10 +29,11 @@ const (
 	// Literals
 	IDENT // main
 
-	INTEGER   // 12345
-	NUMBER    // 12345.67
-	STRING    // "abc"
-	BADSTRING // "abc
+	INTEGER     // 12345
+	NUMBER      // 12345.67
+	STRING      // "abc"
+	SINGLEQUOTE // 'abc'
+	BADSTRING   // "abc
 
 	operatorBeg
 	// ADD and the following are InfluxQL Operators
@@ -117,14 +118,15 @@ const (
 )
 
 var Tokens = []string{
-	ILLEGAL: "ILLEGAL",
-	EOF:     "EOF",
-	AS:      "AS",
-	WS:      "WS",
-	IDENT:   "IDENT",
-	INTEGER: "INTEGER",
-	NUMBER:  "NUMBER",
-	STRING:  "STRING",
+	ILLEGAL:     "ILLEGAL",
+	EOF:         "EOF",
+	AS:          "AS",
+	WS:          "WS",
+	IDENT:       "IDENT",
+	INTEGER:     "INTEGER",
+	NUMBER:      "NUMBER",
+	STRING:      "STRING",
+	SINGLEQUOTE: "SINGLEQUOTE",
 
 	ADD:         "+",
 	SUB:         "-",