forked from GiteaTest2015/streamsql
106 lines
3.1 KiB
Go
106 lines
3.1 KiB
Go
package rsql
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/rulego/streamsql/functions"
|
|
)
|
|
|
|
// FunctionValidator validates SQL functions in expressions
|
|
type FunctionValidator struct {
|
|
errorRecovery *ErrorRecovery
|
|
}
|
|
|
|
// NewFunctionValidator creates a new function validator
|
|
func NewFunctionValidator(errorRecovery *ErrorRecovery) *FunctionValidator {
|
|
return &FunctionValidator{
|
|
errorRecovery: errorRecovery,
|
|
}
|
|
}
|
|
|
|
// ValidateExpression validates functions within expressions
|
|
func (fv *FunctionValidator) ValidateExpression(expression string, position int) {
|
|
functionCalls := fv.extractFunctionCalls(expression)
|
|
|
|
for _, funcCall := range functionCalls {
|
|
funcName := funcCall.Name
|
|
|
|
// Check if function exists in registry
|
|
if _, exists := functions.Get(funcName); !exists {
|
|
// Check if it's a built-in function
|
|
if !fv.isBuiltinFunction(funcName) {
|
|
// Check if it's an expr-lang function
|
|
bridge := functions.GetExprBridge()
|
|
if !bridge.IsExprLangFunction(funcName) {
|
|
// Create unknown function error
|
|
err := CreateUnknownFunctionError(funcName, position+funcCall.Position)
|
|
fv.errorRecovery.AddError(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// FunctionCall contains function call information
|
|
type FunctionCall struct {
|
|
Name string
|
|
Position int
|
|
}
|
|
|
|
// extractFunctionCalls extracts function calls from expressions
|
|
func (fv *FunctionValidator) extractFunctionCalls(expression string) []FunctionCall {
|
|
var functionCalls []FunctionCall
|
|
|
|
// Use regex to match function call patterns: identifier(
|
|
funcPattern := regexp.MustCompile(`([a-zA-Z_][a-zA-Z0-9_]*)\s*\(`)
|
|
matches := funcPattern.FindAllStringSubmatchIndex(expression, -1)
|
|
|
|
for _, match := range matches {
|
|
// match[0] is the start position of entire match
|
|
// match[1] is the end position of entire match
|
|
// match[2] is the start position of first capture group (function name)
|
|
// match[3] is the end position of first capture group (function name)
|
|
funcName := expression[match[2]:match[3]]
|
|
position := match[2]
|
|
|
|
// Filter out keywords (like CASE, IF, etc.)
|
|
if !fv.isKeyword(funcName) {
|
|
functionCalls = append(functionCalls, FunctionCall{
|
|
Name: funcName,
|
|
Position: position,
|
|
})
|
|
}
|
|
}
|
|
|
|
return functionCalls
|
|
}
|
|
|
|
// isBuiltinFunction checks if it's a built-in function using the unified function registry
|
|
// 使用统一的函数注册系统检查函数是否存在
|
|
func (fv *FunctionValidator) isBuiltinFunction(funcName string) bool {
|
|
// 检查函数是否在统一的函数注册系统中
|
|
_, exists := functions.Get(strings.ToLower(funcName))
|
|
return exists
|
|
}
|
|
|
|
// isKeyword checks if it's an SQL keyword
|
|
func (fv *FunctionValidator) isKeyword(word string) bool {
|
|
keywords := []string{
|
|
"SELECT", "FROM", "WHERE", "GROUP", "BY", "HAVING", "ORDER",
|
|
"AS", "DISTINCT", "LIMIT", "WITH", "TIMESTAMP", "TIMEUNIT",
|
|
"TUMBLINGWINDOW", "SLIDINGWINDOW", "COUNTINGWINDOW", "SESSIONWINDOW",
|
|
"AND", "OR", "NOT", "IN", "LIKE", "IS", "NULL", "TRUE", "FALSE",
|
|
"BETWEEN", "IS", "NULL", "TRUE", "FALSE", "CASE", "WHEN",
|
|
"THEN", "ELSE", "END", "IF", "CAST", "CONVERT",
|
|
}
|
|
|
|
wordUpper := strings.ToUpper(word)
|
|
for _, keyword := range keywords {
|
|
if wordUpper == keyword {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|