Repository: lf-edge/ekuiper
Pull request #1067 (2021-11-15 02:47:48)

Refactor: remove strings.lowercase
Remove the usage of strings.lowercase as most as possible to boost performance


Patch:
diff --git a/cmd/kuiper/main.go b/cmd/kuiper/main.go
index 983fba96..689c571d 100644
--- a/cmd/kuiper/main.go
+++ b/cmd/kuiper/main.go
@@ -110,7 +110,7 @@ func main() {
 					// convert CRLF to LF
 					text = strings.Replace(text, "\n", "", -1)
 
-					if strings.ToLower(text) == "quit" || strings.ToLower(text) == "exit" {
+					if strings.EqualFold(text, "quit") || strings.EqualFold(text, "exit") {
 						break
 					} else if strings.Trim(text, " ") == "" {
 						continue
@@ -834,7 +834,7 @@ func main() {
 							return nil
 						}
 						ptype := c.Args()[0]
-						if strings.ToLower(ptype) != "function" {
+						if !strings.EqualFold(ptype, "function") {
 							fmt.Printf("Plugin type must be function.\n")
 							return nil
 						}
diff --git a/extensions/sources/random/random.go b/extensions/sources/random/random.go
index a64f7c72..01cb6e75 100644
--- a/extensions/sources/random/random.go
+++ b/extensions/sources/random/random.go
@@ -60,7 +60,7 @@ func (s *randomSource) Configure(topic string, props map[string]interface{}) err
 	if cfg.Seed <= 0 {
 		return fmt.Errorf("source `random` property `seed` must be a positive integer but got %d", cfg.Seed)
 	}
-	if strings.ToLower(cfg.Format) != message.FormatJson {
+	if !strings.EqualFold(cfg.Format, message.FormatJson) {
 		return fmt.Errorf("random source only supports `json` format")
 	}
 	s.conf = cfg
diff --git a/internal/binder/factory.go b/internal/binder/factory.go
index 1bb4186a..011e7401 100644
--- a/internal/binder/factory.go
+++ b/internal/binder/factory.go
@@ -29,6 +29,9 @@ type FuncFactory interface {
 	// HasFunctionSet Some functions are bundled together into a plugin which shares the same json file.
 	// This function can return if the function set name exists.
 	HasFunctionSet(funcName string) bool
+	// ConvName Convert the name of the function usually to lowercase.
+	// This is only be used when parsing the SQL statement.
+	ConvName(funcName string) (string, bool)
 }
 
 type FactoryEntry struct {
diff --git a/internal/binder/function/binder.go b/internal/binder/function/binder.go
index 67337407..ec6bcc13 100644
--- a/internal/binder/function/binder.go
+++ b/internal/binder/function/binder.go
@@ -72,6 +72,16 @@ func HasFunctionSet(name string) bool {
 	return false
 }
 
+func ConvName(name string) (string, bool) {
+	for _, sf := range funcFactories {
+		r, ok := sf.ConvName(name)
+		if ok {
+			return r, ok
+		}
+	}
+	return name, false
+}
+
 type multiAggFunc interface {
 	IsAggregateWithName(name string) bool
 }
diff --git a/internal/binder/function/funcs_agg.go b/internal/binder/function/funcs_agg.go
index 9465ef73..ef45ecdc 100644
--- a/internal/binder/function/funcs_agg.go
+++ b/internal/binder/function/funcs_agg.go
@@ -16,12 +16,10 @@ package function
 
 import (
 	"fmt"
-	"strings"
 )
 
 func aggCall(name string, args []interface{}) (interface{}, bool) {
-	lowerName := strings.ToLower(name)
-	switch lowerName {
+	switch name {
 	case "avg":
 		arg0 := args[0].([]interface{})
 		c := getCount(arg0)
diff --git a/internal/binder/function/funcs_ast_validator.go b/internal/binder/function/funcs_ast_validator.go
index f61ac5e8..fe8dcbd5 100644
--- a/internal/binder/function/funcs_ast_validator.go
+++ b/internal/binder/function/funcs_ast_validator.go
@@ -25,41 +25,40 @@ type AllowTypes struct {
 }
 
 func validateFuncs(funcName string, args []ast.Expr) error {
-	lowerName := strings.ToLower(funcName)
-	switch getFuncType(lowerName) {
+	switch getFuncType(funcName) {
 	case AggFunc:
-		return validateAggFunc(lowerName, args)
+		return validateAggFunc(funcName, args)
 	case MathFunc:
-		return validateMathFunc(lowerName, args)
+		return validateMathFunc(funcName, args)
 	case ConvFunc:
-		return validateConvFunc(lowerName, args)
+		return validateConvFunc(funcName, args)
 	case StrFunc:
-		return validateStrFunc(lowerName, args)
+		return validateStrFunc(funcName, args)
 	case HashFunc:
-		return validateHashFunc(lowerName, args)
+		return validateHashFunc(funcName, args)
 	case JsonFunc:
-		return validateJsonFunc(lowerName, args)
+		return validateJsonFunc(funcName, args)
 	case OtherFunc:
-		return validateOtherFunc(lowerName, args)
+		return validateOtherFunc(funcName, args)
 	default:
 		// should not happen
-		return fmt.Errorf("unkndow function %s", lowerName)
+		return fmt.Errorf("unkndow function %s", funcName)
 	}
 }
 
 func validateMathFunc(name string, args []ast.Expr) error {
-	len := len(args)
+	l := len(args)
 	switch name {
 	case "abs", "acos", "asin", "atan", "ceil", "cos", "cosh", "exp", "ln", "log", "round", "sign", "sin", "sinh",
 		"sqrt", "tan", "tanh":
-		if err := ast.ValidateLen(name, 1, len); err != nil {
+		if err := ast.ValidateLen(name, 1, l); err != nil {
 			return err
 		}
 		if ast.IsStringArg(args[0]) || ast.IsTimeArg(args[0]) || ast.IsBooleanArg(args[0]) {
 			return ast.ProduceErrInfo(name, 0, "number - float or int")
 		}
 	case "bitand", "bitor", "bitxor":
-		if err := ast.ValidateLen(name, 2, len); err != nil {
+		if err := ast.ValidateLen(name, 2, l); err != nil {
 			return err
 		}
 		if ast.IsFloatArg(args[0]) || ast.IsStringArg(args[0]) || ast.IsTimeArg(args[0]) || ast.IsBooleanArg(args[0]) {
@@ -70,7 +69,7 @@ func validateMathFunc(name string, args []ast.Expr) error {
 		}
 
 	case "bitnot":
-		if err := ast.ValidateLen(name, 1, len); err != nil {
+		if err := ast.ValidateLen(name, 1, l); err != nil {
 			return err
 		}
 		if ast.IsFloatArg(args[0]) || ast.IsStringArg(args[0]) || ast.IsTimeArg(args[0]) || ast.IsBooleanArg(args[0]) {
@@ -78,7 +77,7 @@ func validateMathFunc(name string, args []ast.Expr) error {
 		}
 
 	case "atan2", "mod", "power":
-		if err := ast.ValidateLen(name, 2, len); err != nil {
+		if err := ast.ValidateLen(name, 2, l); err != nil {
 			return err
 		}
 		if ast.IsStringArg(args[0]) || ast.IsTimeArg(args[0]) || ast.IsBooleanArg(args[0]) {
@@ -89,7 +88,7 @@ func validateMathFunc(name string, args []ast.Expr) error {
 		}
 
 	case "rand":
-		if err := ast.ValidateLen(name, 0, len); err != nil {
+		if err := ast.ValidateLen(name, 0, l); err != nil {
 			return err
 		}
 	}
@@ -97,10 +96,10 @@ func validateMathFunc(name string, args []ast.Expr) error {
 }
 
 func validateStrFunc(name string, args []ast.Expr) error {
-	len := len(args)
+	l := len(args)
 	switch name {
 	case "concat":
-		if len == 0 {
+		if l == 0 {
 			return fmt.Errorf("The arguments for %s should be at least one.\n", name)
 		}
 		for i, a := range args {
@@ -109,7 +108,7 @@ func validateStrFunc(name string, args []ast.Expr) error {
 			}
 		}
 	case "endswith", "indexof", "regexp_matches", "startswith":
-		if err := ast.ValidateLen(name, 2, len); err != nil {
+		if err := ast.ValidateLen(name, 2, l); err != nil {
 			return err
 		}
 		for i := 0; i < 2; i++ {
@@ -118,7 +117,7 @@ func validateStrFunc(name string, args []ast.Expr) error {
 			}
 		}
 	case "format_time":
-		if err := ast.ValidateLen(name, 2, len); err != nil {
+		if err := ast.ValidateLen(name, 2, l); err != nil {
 			return err
 		}
 
@@ -130,7 +129,7 @@ func validateStrFunc(name string, args []ast.Expr) error {
 		}
 
 	case "regexp_replace":
-		if err := ast.ValidateLen(name, 3, len); err != nil {
+		if err := ast.ValidateLen(name, 3, l); err != nil {
 			return err
 		}
 		for i := 0; i < 3; i++ {
@@ -139,14 +138,14 @@ func validateStrFunc(name string, args []ast.Expr) error {
 			}
 		}
 	case "length", "lower", "ltrim", "numbytes", "rtrim", "trim", "upper":
-		if err := ast.ValidateLen(name, 1, len); err != nil {
+		if err := ast.ValidateLen(name, 1, l); err != nil {
 			return err
 		}
 		if ast.IsNumericArg(args[0]) || ast.IsTimeArg(args[0]) || ast.IsBooleanArg(args[0]) {
 			return ast.ProduceErrInfo(name, 0, "string")
 		}
 	case "lpad", "rpad":
-		if err := ast.ValidateLen(name, 2, len); err != nil {
+		if err := ast.ValidateLen(name, 2, l); err != nil {
 			return err
 		}
 		if ast.IsNumericArg(args[0]) || ast.IsTimeArg(args[0]) || ast.IsBooleanArg(args[0]) {
@@ -156,13 +155,13 @@ func validateStrFunc(name string, args []ast.Expr) error {
 			return ast.ProduceErrInfo(name, 1, "int")
 		}
 	case "substring":
-		if len != 2 && len != 3 {
+		if l != 2 && l != 3 {
 			return fmt.Errorf("the arguments for substring should be 2 or 3")
 		}
 		if ast.IsNumericArg(args[0]) || ast.IsTimeArg(args[0]) || ast.IsBooleanArg(args[0]) {
 			return ast.ProduceErrInfo(name, 0, "string")
 		}
-		for i := 1; i < len; i++ {
+		for i := 1; i < l; i++ {
 			if ast.IsFloatArg(args[i]) || ast.IsTimeArg(args[i]) || ast.IsBooleanArg(args[i]) || ast.IsStringArg(args[i]) {
 				return ast.ProduceErrInfo(name, i, "int")
 			}
@@ -173,7 +172,7 @@ func validateStrFunc(name string, args []ast.Expr) error {
 			if sv < 0 {
 				return fmt.Errorf("The start index should not be a nagtive integer.")
 			}
-			if len == 3 {
+			if l == 3 {
 				if e, ok1 := args[2].(*ast.IntegerLiteral); ok1 {
 					ev := e.Val
 					if ev < sv {
@@ -183,7 +182,7 @@ func validateStrFunc(name string, args []ast.Expr) error {
 			}
 		}
 	case "split_value":
-		if len != 3 {
+		if l != 3 {
 			return fmt.Errorf("the arguments for split_value should be 3")
 		}
 		if ast.IsNumericArg(args[0]) || ast.IsTimeArg(args[0]) || ast.IsBooleanArg(args[0]) {
@@ -205,10 +204,10 @@ func validateStrFunc(name string, args []ast.Expr) error {
 }
 
 func validateConvFunc(name string, args []ast.Expr) error {
-	len := len(args)
+	l := len(args)
 	switch name {
 	case "cast":
-		if err := ast.ValidateLen(name, 2, len); err != nil {
+		if err := ast.ValidateLen(name, 2, l); err != nil {
 			return err
 		}
 		a := args[1]
@@ -221,14 +220,14 @@ func validateConvFunc(name string, args []ast.Expr) error {
 			}
 		}
 	case "chr":
-		if err := ast.ValidateLen(name, 1, len); err != nil {
+		if err := ast.ValidateLen(name, 1, l); err != nil {
 			return err
 		}
 		if ast.IsFloatArg(args[0]) || ast.IsTimeArg(args[0]) || ast.IsBooleanArg(args[0]) {
 			return ast.ProduceErrInfo(name, 0, "int")
 		}
 	case "encode":
-		if err := ast.ValidateLen(name, 2, len); err != nil {
+		if err := ast.ValidateLen(name, 2, l); err != nil {
 			return err
 		}
 
@@ -246,7 +245,7 @@ func validateConvFunc(name string, args []ast.Expr) error {
 			}
 		}
 	case "trunc":
-		if err := ast.ValidateLen(name, 2, len); err != nil {
+		if err := ast.ValidateLen(name, 2, l); err != nil {
 			return err
 		}
 
@@ -262,10 +261,10 @@ func validateConvFunc(name string, args []ast.Expr) error {
 }
 
 func validateHashFunc(name string, args []ast.Expr) error {
-	len := len(args)
+	l := len(args)
 	switch name {
 	case "md5", "sha1", "sha224", "sha256", "sha384", "sha512":
-		if err := ast.ValidateLen(name, 1, len); err != nil {
+		if err := ast.ValidateLen(name, 1, l); err != nil {
 			return err
 		}
 
@@ -277,29 +276,29 @@ func validateHashFunc(name string, args []ast.Expr) error {
 }
 
 func validateOtherFunc(name string, args []ast.Expr) error {
-	len := len(args)
+	l := len(args)
 	switch name {
 	case "isNull":
-		if err := ast.ValidateLen(name, 1, len); err != nil {
+		if err := ast.ValidateLen(name, 1, l); err != nil {
 			return err
 		}
 	case "cardinality":
-		if err := ast.ValidateLen(name, 1, len); err != nil {
+		if err := ast.ValidateLen(name, 1, l); err != nil {
 			return err
 		}
 	case "nanvl":
-		if err := ast.ValidateLen(name, 2, len); err != nil {
+		if err := ast.ValidateLen(name, 2, l); err != nil {
 			return err
 		}
 		if ast.IsIntegerArg(args[0]) || ast.IsTimeArg(args[0]) || ast.IsBooleanArg(args[0]) || ast.IsStringArg(args[0]) {
 			return ast.ProduceErrInfo(name, 1, "float")
 		}
 	case "newuuid":
-		if err := ast.ValidateLen(name, 0, len); err != nil {
+		if err := ast.ValidateLen(name, 0, l); err != nil {
 			return err
 		}
 	case "mqtt":
-		if err := ast.ValidateLen(name, 1, len); err != nil {
+		if err := ast.ValidateLen(name, 1, l); err != nil {
 			return err
 		}
 		if ast.IsIntegerArg(args[0]) || ast.IsTimeArg(args[0]) || ast.IsBooleanArg(args[0]) || ast.IsStringArg(args[0]) || ast.IsFloatArg(args[0]) {
@@ -312,7 +311,7 @@ func validateOtherFunc(name string, args []ast.Expr) error {
 			}
 		}
 	case "meta":
-		if err := ast.ValidateLen(name, 1, len); err != nil {
+		if err := ast.ValidateLen(name, 1, l); err != nil {
 			return err
 		}
 		if _, ok := args[0].(*ast.MetaRef); ok {
@@ -335,8 +334,8 @@ func validateOtherFunc(name string, args []ast.Expr) error {
 }
 
 func validateJsonFunc(name string, args []ast.Expr) error {
-	len := len(args)
-	if err := ast.ValidateLen(name, 2, len); err != nil {
+	l := len(args)
+	if err := ast.ValidateLen(name, 2, l); err != nil {
 		return err
 	}
 	if !ast.IsStringArg(args[1]) {
@@ -346,25 +345,25 @@ func validateJsonFunc(name string, args []ast.Expr) error {
 }
 
 func validateAggFunc(name string, args []ast.Expr) error {
-	len := len(args)
+	l := len(args)
 	switch name {
 	case "avg", "max", "min", "sum":
-		if err := ast.ValidateLen(name, 1, len); err != nil {
+		if err := ast.ValidateLen(name, 1, l); err != nil {
 			return err
 		}
 		if ast.IsStringArg(args[0]) || ast.IsTimeArg(args[0]) || ast.IsBooleanArg(args[0]) {
 			return ast.ProduceErrInfo(name, 0, "number - float or int")
 		}
 	case "count":
-		if err := ast.ValidateLen(name, 1, len); err != nil {
+		if err := ast.ValidateLen(name, 1, l); err != nil {
 			return err
 		}
 	case "collect":
-		if err := ast.ValidateLen(name, 1, len); err != nil {
+		if err := ast.ValidateLen(name, 1, l); err != nil {
 			return err
 		}
 	case "deduplicate":
-		if err := ast.ValidateLen(name, 2, len); err != nil {
+		if err := ast.ValidateLen(name, 2, l); err != nil {
 			return err
 		}
 		if !ast.IsBooleanArg(args[1]) {
diff --git a/internal/binder/function/funcs_misc.go b/internal/binder/function/funcs_misc.go
index 5304ac5f..efd8b845 100644
--- a/internal/binder/function/funcs_misc.go
+++ b/internal/binder/function/funcs_misc.go
@@ -37,7 +37,6 @@ func convCall(name string, args []interface{}) (interface{}, bool) {
 	switch name {
 	case "cast":
 		if v, ok := args[1].(string); ok {
-			v = strings.ToLower(v)
 			switch v {
 			case "bigint":
 				if v1, ok1 := args[0].(int); ok1 {
@@ -149,8 +148,7 @@ func convCall(name string, args []interface{}) (interface{}, bool) {
 		}
 	case "encode":
 		if v, ok := args[1].(string); ok {
-			v = strings.ToLower(v)
-			if v == "base64" {
+			if strings.EqualFold(v, "base64") {
 				if v1, ok1 := args[0].(string); ok1 {
 					return b64.StdEncoding.EncodeToString([]byte(v1)), true
 				} else {
diff --git a/internal/binder/function/function.go b/internal/binder/function/function.go
index 8b5f026a..33487ebc 100644
--- a/internal/binder/function/function.go
+++ b/internal/binder/function/function.go
@@ -96,7 +96,7 @@ var otherFuncMap = map[string]string{"isnull": "",
 
 func getFuncType(name string) funcType {
 	for i, m := range maps {
-		if _, ok := m[strings.ToLower(name)]; ok {
+		if _, ok := m[name]; ok {
 			return funcType(i)
 		}
 	}
@@ -127,22 +127,21 @@ func (f *funcExecutor) Exec(_ []interface{}, _ api.FunctionContext) (interface{}
 }
 
 func (f *funcExecutor) ExecWithName(args []interface{}, ctx api.FunctionContext, name string) (interface{}, bool) {
-	lowerName := strings.ToLower(name)
-	switch getFuncType(lowerName) {
+	switch getFuncType(name) {
 	case AggFunc:
-		return aggCall(lowerName, args)
+		return aggCall(name, args)
 	case MathFunc:
-		return mathCall(lowerName, args)
+		return mathCall(name, args)
 	case ConvFunc:
-		return convCall(lowerName, args)
+		return convCall(name, args)
 	case StrFunc:
-		return strCall(lowerName, args)
+		return strCall(name, args)
 	case HashFunc:
-		return hashCall(lowerName, args)
+		return hashCall(name, args)
 	case JsonFunc:
-		return jsonCall(ctx, lowerName, args)
+		return jsonCall(ctx, name, args)
 	case OtherFunc:
-		return otherCall(lowerName, args)
+		return otherCall(name, args)
 	}
 	return fmt.Errorf("unknow name"), false
 }
@@ -152,8 +151,7 @@ func (f *funcExecutor) IsAggregate() bool {
 }
 
 func (f *funcExecutor) IsAggregateWithName(name string) bool {
-	lowerName := strings.ToLower(name)
-	return getFuncType(lowerName) == AggFunc
+	return getFuncType(name) == AggFunc
 }
 
 var staticFuncExecutor = &funcExecutor{}
@@ -172,6 +170,15 @@ func (m *Manager) HasFunctionSet(name string) bool {
 	return name == "internal"
 }
 
+func (m *Manager) ConvName(n string) (string, bool) {
+	name := strings.ToLower(n)
+	ft := getFuncType(name)
+	if ft != NotFoundFunc {
+		return name, true
+	}
+	return name, false
+}
+
 var m = &Manager{}
 
 func GetManager() *Manager {
diff --git a/internal/binder/mock/mock_factory.go b/internal/binder/mock/mock_factory.go
index 675cfb57..c6918c03 100644
--- a/internal/binder/mock/mock_factory.go
+++ b/internal/binder/mock/mock_factory.go
@@ -51,6 +51,10 @@ func (f *MockFactory) Function(name string) (api.Function, error) {
 	}
 }
 
+func (f *MockFactory) ConvName(name string) (string, bool) {
+	return name, true
+}
+
 func (f *MockFactory) HasFunctionSet(funcName string) bool {
 	if strings.HasPrefix(funcName, "mock") {
 		return true
diff --git a/internal/conf/load.go b/internal/conf/load.go
index af72bbf7..aa83e54c 100644
--- a/internal/conf/load.go
+++ b/internal/conf/load.go
@@ -1,3 +1,17 @@
+// Copyright 2021 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.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // INTECH Process Automation Ltd.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -201,7 +215,7 @@ func applyKey(m map[string]interface{}, key string) {
 		if casted, ok := v.(map[string]interface{}); ok {
 			applyKey(casted, key)
 		}
-		if key != k && strings.ToLower(key) == k {
+		if key != k && strings.EqualFold(key, k) {
 			m[key] = v
 			delete(m, k)
 		}
diff --git a/internal/pkg/jwt/rsa_keys.go b/internal/pkg/jwt/rsa_keys.go
index 5ca86b20..ec4e2cd1 100644
--- a/internal/pkg/jwt/rsa_keys.go
+++ b/internal/pkg/jwt/rsa_keys.go
@@ -1,3 +1,17 @@
+// Copyright 2021 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.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package jwt
 
 import (
@@ -58,7 +72,7 @@ func insensitiveGetFilePath(prikeyName string) (string, error) {
 
 	for _, info := range infos {
 		fileName := info.Name()
-		if strings.ToLower(fileName) == strings.ToLower(prikeyName) {
+		if strings.EqualFold(fileName, prikeyName) {
 			filePath := path.Join(dir, fileName)
 			return filePath, nil
 		}
diff --git a/internal/plugin/native/manager.go b/internal/plugin/native/manager.go
index 5fc2c4ce..05faa446 100644
--- a/internal/plugin/native/manager.go
+++ b/internal/plugin/native/manager.go
@@ -562,6 +562,14 @@ func (rr *Manager) HasFunctionSet(name string) bool {
 	return ok
 }
 
+func (rr *Manager) ConvName(name string) (string, bool) {
+	_, err := rr.Function(name)
+	if err == nil {
+		return name, true
+	}
+	return name, false
+}
+
 // If not found, return nil,nil; Other errors return nil, err
 func (rr *Manager) loadRuntime(t plugin2.PluginType, name string) (plugin.Symbol, error) {
 	ut := ucFirst(name)
diff --git a/internal/plugin/portable/factory.go b/internal/plugin/portable/factory.go
index ac4d18f8..0255ba26 100644
--- a/internal/plugin/portable/factory.go
+++ b/internal/plugin/portable/factory.go
@@ -63,6 +63,11 @@ func (m *Manager) HasFunctionSet(funcName string) bool {
 	return ok
 }
 
+func (m *Manager) ConvName(funcName string) (string, bool) {
+	_, ok := m.GetPluginMeta(plugin.FUNCTION, funcName)
+	return funcName, ok
+}
+
 // Clean up function map
 func (m *Manager) Clean() {
 	funcInsMap.Range(func(_, ins interface{}) bool {
diff --git a/internal/server/rest.go b/internal/server/rest.go
index 21e0520e..5fabdb2a 100644
--- a/internal/server/rest.go
+++ b/internal/server/rest.go
@@ -631,7 +631,7 @@ func prebuildFuncsPlugins(w http.ResponseWriter, r *http.Request) {
 }
 
 func isOffcialDockerImage() bool {
-	if strings.ToLower(os.Getenv("MAINTAINER")) != "emqx.io" {
+	if !strings.EqualFold(os.Getenv("MAINTAINER"), "emqx.io") {
 		return false
 	}
 	return true
diff --git a/internal/service/manager.go b/internal/service/manager.go
index 05f887e9..44413882 100644
--- a/internal/service/manager.go
+++ b/internal/service/manager.go
@@ -182,7 +182,7 @@ func (m *Manager) initFile(baseName string) error {
 
 // Start Implement FunctionFactory
 
-func (m *Manager) HasFunctionSet(name string) bool {
+func (m *Manager) HasFunctionSet(_ string) bool {
 	return false
 }
 
@@ -207,6 +207,11 @@ func (m *Manager) Function(name string) (api.Function, error) {
 	return &ExternalFunc{exe: e, methodName: f.MethodName}, nil
 }
 
+func (m *Manager) ConvName(funcName string) (string, bool) {
+	_, ok := m.getFunction(funcName)
+	return funcName, ok
+}
+
 // End Implement FunctionFactory
 
 func (m *Manager) HasService(name string) bool {
@@ -370,11 +375,11 @@ func (m *Manager) unzip(name, src string) error {
 		return err
 	}
 	defer r.Close()
-	baseName := strings.ToLower(name + ".json")
+	baseName := name + ".json"
 	// Try unzip
 	found := false
 	for _, file := range r.File {
-		if strings.ToLower(file.Name) == baseName {
+		if strings.EqualFold(file.Name, baseName) {
 			found = true
 			break
 		}
diff --git a/internal/topo/operator/field_processor.go b/internal/topo/operator/field_processor.go
index fe9c5f40..10963223 100644
--- a/internal/topo/operator/field_processor.go
+++ b/internal/topo/operator/field_processor.go
@@ -26,7 +26,6 @@ import (
 	"math"
 	"reflect"
 	"strconv"
-	"strings"
 	"time"
 )
 
@@ -204,7 +203,7 @@ func (p *defaultFieldProcessor) addRecField(ft ast.FieldType, r map[string]inter
 			}
 			nextR := make(map[string]interface{})
 			for _, nextF := range st.StreamFields {
-				nextP := strings.ToLower(nextF.Name)
+				nextP := nextF.Name
 				if e := p.addRecField(nextF.FieldType, nextR, nextJ, nextP); e != nil {
 					return e
 				}
diff --git a/internal/topo/operator/preprocessor.go b/internal/topo/operator/preprocessor.go
index eff99ebc..7425926b 100644
--- a/internal/topo/operator/preprocessor.go
+++ b/internal/topo/operator/preprocessor.go
@@ -19,7 +19,6 @@ import (
 	"github.com/lf-edge/ekuiper/internal/xsql"
 	"github.com/lf-edge/ekuiper/pkg/api"
 	"github.com/lf-edge/ekuiper/pkg/cast"
-	"strings"
 )
 
 type Preprocessor struct {
@@ -75,7 +74,7 @@ func (p *Preprocessor) Apply(ctx api.StreamContext, data interface{}, _ *xsql.Fu
 		newMeta := make(xsql.Metadata)
 		for _, f := range p.metaFields {
 			if m, ok := tuple.Metadata.Value(f); ok {
-				newMeta[strings.ToLower(f)] = m
+				newMeta[f] = m
 			}
 		}
 		tuple.Metadata = newMeta
diff --git a/internal/topo/planner/analyzer.go b/internal/topo/planner/analyzer.go
index 5667fa0a..d5fedd52 100644
--- a/internal/topo/planner/analyzer.go
+++ b/internal/topo/planner/analyzer.go
@@ -177,21 +177,23 @@ func newFieldsMap(isSchemaless bool, defaultStream ast.StreamName) *fieldsMap {
 }
 
 func (f *fieldsMap) reserve(fieldName string, streamName ast.StreamName) {
-	if fm, ok := f.content[strings.ToLower(fieldName)]; ok {
+	lname := strings.ToLower(fieldName)
+	if fm, ok := f.content[lname]; ok {
 		fm.add(streamName)
 	} else {
 		fm := newStreamFieldStore(f.isSchemaless, f.defaultStream)
 		fm.add(streamName)
-		f.content[strings.ToLower(fieldName)] = fm
+		f.content[lname] = fm
 	}
 }
 
 func (f *fieldsMap) save(fieldName string, streamName ast.StreamName, field *ast.AliasRef) error {
-	fm, ok := f.content[strings.ToLower(fieldName)]
+	lname := strings.ToLower(fieldName)
+	fm, ok := f.content[lname]
 	if !ok {
 		if streamName == ast.AliasStream || f.isSchemaless {
 			fm = newStreamFieldStore(f.isSchemaless, f.defaultStream)
-			f.content[strings.ToLower(fieldName)] = fm
+			f.content[lname] = fm
 		} else {
 			return fmt.Errorf("unknown field %s", fieldName)
 		}
@@ -204,11 +206,12 @@ func (f *fieldsMap) save(fieldName string, streamName ast.StreamName, field *ast
 }
 
 func (f *fieldsMap) bind(fr *ast.FieldRef) error {
-	fm, ok := f.content[strings.ToLower(fr.Name)]
+	lname := strings.ToLower(fr.Name)
+	fm, ok := f.content[lname]
 	if !ok {
 		if f.isSchemaless && fr.Name != "" {
 			fm = newStreamFieldStore(f.isSchemaless, f.defaultStream)
-			f.content[strings.ToLower(fr.Name)] = fm
+			f.content[lname] = fm
 		} else {
 			return fmt.Errorf("unknown field %s", fr.Name)
 		}
diff --git a/internal/topo/planner/dataSourcePlan.go b/internal/topo/planner/dataSourcePlan.go
index 468a383f..29c1b99f 100644
--- a/internal/topo/planner/dataSourcePlan.go
+++ b/internal/topo/planner/dataSourcePlan.go
@@ -205,7 +205,7 @@ func (p *DataSourcePlan) getProps() error {
 	if p.streamStmt.Options.TIMESTAMP_FORMAT != "" {
 		p.timestampFormat = p.streamStmt.Options.TIMESTAMP_FORMAT
 	}
-	if strings.ToLower(p.streamStmt.Options.FORMAT) == message.FormatBinary {
+	if strings.EqualFold(p.streamStmt.Options.FORMAT, message.FormatBinary) {
 		p.isBinary = true
 	}
 	return nil
diff --git a/internal/topo/source/edgex_source.go b/internal/topo/source/edgex_source.go
index 05e532b6..dd1f26a7 100644
--- a/internal/topo/source/edgex_source.go
+++ b/internal/topo/source/edgex_source.go
@@ -105,7 +105,7 @@ func (es *EdgexSource) Configure(_ string, props map[string]interface{}) error {
 func printConf(mbconf types.MessageBusConfig) {
 	var printableOptional = make(map[string]string)
 	for k, v := range mbconf.Optional {
-		if strings.ToLower(k) == "password" {
+		if strings.EqualFold(k, "password") {
 			printableOptional[k] = "*"
 		} else {
 			printableOptional[k] = v
@@ -166,7 +166,7 @@ func (es *EdgexSource) Open(ctx api.StreamContext, consumer chan<- api.SourceTup
 				if !ok { // the source is closed
 					return
 				}
-				if strings.ToLower(env.ContentType) == "application/json" {
+				if strings.EqualFold(env.ContentType, "application/json") {
 					var r interface{}
 					switch es.messageType {
 					case MessageTypeEvent:
diff --git a/internal/topo/source/mqtt_type.go b/internal/topo/source/mqtt_type.go
index b9daaaae..3c390dbd 100644
--- a/internal/topo/source/mqtt_type.go
+++ b/internal/topo/source/mqtt_type.go
@@ -53,7 +53,7 @@ func (this *device) getName() string {
 }
 func (this *device) findDataType(name string) string {
 	for _, v := range this.Properties {
-		if strings.ToLower(v.getName()) == strings.ToLower(name) {
+		if strings.EqualFold(v.getName(), name) {
 			return v.getDataType()
 		}
 	}
@@ -61,7 +61,7 @@ func (this *device) findDataType(name string) string {
 }
 func (this *deviceModel) findDataType(deviceId, dataName string) string {
 	for _, v := range this.Devices {
-		if strings.ToLower(v.getName()) == strings.ToLower(deviceId) {
+		if strings.EqualFold(v.getName(), deviceId) {
 			return v.findDataType(dataName)
 		}
 	}
diff --git a/internal/xsql/collections.go b/internal/xsql/collections.go
index 02c36e55..ec499bb6 100644
--- a/internal/xsql/collections.go
+++ b/internal/xsql/collections.go
@@ -58,8 +58,7 @@ func (m Message) Value(key string) (interface{}, bool) {
 		conf.Log.Println("Invalid key: " + key + ", expect source.field or field.")
 		return nil, false
 	}
-	key1 := strings.ToLower(colkey)
-	if v, ok := m[key1]; ok {
+	if v, ok := m[colkey]; ok {
 		return v, ok
 	} else {
 		//Only when with 'SELECT * FROM ...'  and 'schemaless', the key in map is not convert to lower case.
@@ -70,9 +69,8 @@ func (m Message) Value(key string) (interface{}, bool) {
 
 func (m Message) getIgnoreCase(key interface{}) (interface{}, bool) {
 	if k, ok := key.(string); ok {
-		key = strings.ToLower(k)
-		for k, v := range m {
-			if strings.ToLower(k) == key {
+		for mk, v := range m {
+			if strings.EqualFold(k, mk) {
 				return v, true
 			}
 		}
diff --git a/internal/xsql/parser.go b/internal/xsql/parser.go
index be24cf7e..15d41e5c 100644
--- a/internal/xsql/parser.go
+++ b/internal/xsql/parser.go
@@ -17,6 +17,7 @@ package xsql
 import (
 	"fmt"
 	"github.com/golang-collections/collections/stack"
+	"github.com/lf-edge/ekuiper/internal/binder/function"
 	"github.com/lf-edge/ekuiper/pkg/ast"
 	"github.com/lf-edge/ekuiper/pkg/message"
 	"io"
@@ -643,8 +644,30 @@ func (p *Parser) parseAs(f *ast.Field) (*ast.Field, error) {
 	return f, nil
 }
 
-func (p *Parser) parseCall(name string) (ast.Expr, error) {
-	if strings.ToLower(name) == "meta" || strings.ToLower(name) == "mqtt" {
+var WindowFuncs = map[string]struct{}{
+	"tumblingwindow": {},
+	"hoppingwindow":  {},
+	"sessionwindow":  {},
+	"slidingwindow":  {},
+	"countwindow":    {},
+}
+
+func convFuncName(n string) (string, bool) {
+	lname := strings.ToLower(n)
+	if _, ok := WindowFuncs[lname]; ok {
+		return lname, ok
+	} else {
+		return function.ConvName(n)
+	}
+}
+
+func (p *Parser) parseCall(n string) (ast.Expr, error) {
+	// Check if n function exists and convert it to lowercase for built-in func
+	name, ok := convFuncName(n)
+	if !ok {
+		return nil, fmt.Errorf("function %s not found", n)
+	}
+	if name == "meta" || name == "mqtt" {
 		p.inmeta = true
 		defer func() {
 			p.inmeta = false
@@ -779,8 +802,7 @@ loop:
 	return c, nil
 }
 
-func validateWindows(name string, args []ast.Expr) (ast.WindowType, error) {
-	fname := strings.ToLower(name)
+func validateWindows(fname string, args []ast.Expr) (ast.WindowType, error) {
 	switch fname {
 	case "tumblingwindow":
 		if err := validateWindow(fname, 2, args); err != nil {
diff --git a/sdk/go/example/mirror/random_source.go b/sdk/go/example/mirror/random_source.go
index ed4ae975..76eebf81 100644
--- a/sdk/go/example/mirror/random_source.go
+++ b/sdk/go/example/mirror/random_source.go
@@ -59,7 +59,7 @@ func (s *randomSource) Configure(_ string, props map[string]interface{}) error {
 	if cfg.Seed <= 0 {
 		return fmt.Errorf("source `random` property `seed` must be a positive integer but got %d", cfg.Seed)
 	}
-	if strings.ToLower(cfg.Format) != "json" {
+	if !strings.EqualFold(cfg.Format, "json") {
 		return fmt.Errorf("random source only supports `json` format")
 	}
 	s.conf = cfg


Test patch:
diff --git a/internal/topo/operator/preprocessor_test.go b/internal/topo/operator/preprocessor_test.go
index 48fe3e9c..db986f0a 100644
--- a/internal/topo/operator/preprocessor_test.go
+++ b/internal/topo/operator/preprocessor_test.go
@@ -101,13 +101,13 @@ func TestPreprocessor_Apply(t *testing.T) {
 				Name: ast.StreamName("demo"),
 				StreamFields: []ast.StreamField{
 					{Name: "abc", FieldType: &ast.BasicType{Type: ast.FLOAT}},
-					{Name: "def", FieldType: &ast.BasicType{Type: ast.STRINGS}},
+					{Name: "dEf", FieldType: &ast.BasicType{Type: ast.STRINGS}},
 				},
 			},
 			data: []byte(`{"abc": 34, "def" : "hello", "ghi": 50}`),
 			result: &xsql.Tuple{Message: xsql.Message{
 				"abc": float64(34),
-				"def": "hello",
+				"dEf": "hello",
 			},
 			},
 		},
diff --git a/internal/topo/planner/planner_test.go b/internal/topo/planner/planner_test.go
index a1cec4c1..241a0221 100644
--- a/internal/topo/planner/planner_test.go
+++ b/internal/topo/planner/planner_test.go
@@ -404,7 +404,7 @@ func Test_createLogicalPlan(t *testing.T) {
 								},
 							},
 							condition: &ast.BinaryExpr{
-								LHS: &ast.Call{Name: "COUNT", Args: []ast.Expr{&ast.Wildcard{
+								LHS: &ast.Call{Name: "count", Args: []ast.Expr{&ast.Wildcard{
 									Token: ast.ASTERISK,
 								}}},
 								OP:  ast.GT,
@@ -1434,7 +1434,7 @@ func Test_createLogicalPlanSchemaless(t *testing.T) {
 								},
 							},
 							condition: &ast.BinaryExpr{
-								LHS: &ast.Call{Name: "COUNT", Args: []ast.Expr{&ast.Wildcard{
+								LHS: &ast.Call{Name: "count", Args: []ast.Expr{&ast.Wildcard{
 									Token: ast.ASTERISK,
 								}}},
 								OP:  ast.GT,
diff --git a/internal/xsql/parser_test.go b/internal/xsql/parser_test.go
index ecc6f120..8ad655d8 100644
--- a/internal/xsql/parser_test.go
+++ b/internal/xsql/parser_test.go
@@ -238,7 +238,7 @@ func TestParser_ParseStatement(t *testing.T) {
 		},
 
 		{
-			s: `SELECT length("test") FROM tbl`,
+			s: `SELECT LenGth("test") FROM tbl`,
 			stmt: &ast.SelectStatement{
 				Fields: []ast.Field{
 					{
@@ -1389,22 +1389,6 @@ func TestParser_ParseStatement(t *testing.T) {
 			stmt: nil,
 			err:  "invalid CASE expression, WHEN expression must be a bool condition",
 		}, {
-			s: `SELECT echo(*) FROM tbl`,
-			stmt: &ast.SelectStatement{
-				Fields: []ast.Field{
-					{
-						AName: "",
-						Name:  "echo",
-						Expr: &ast.Call{
-							Name: "echo",
-							Args: []ast.Expr{&ast.Wildcard{Token: ast.ASTERISK}},
-						},
-					},
-				},
-				Sources: []ast.Source{&ast.Table{Name: "tbl"}},
-			},
-		},
-		{
 			s: `SELECT count(*)-10 FROM demo`,
 			stmt: &ast.SelectStatement{
 				Fields: []ast.Field{