You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							354 lines
						
					
					
						
							9.5 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							354 lines
						
					
					
						
							9.5 KiB
						
					
					
				
								package engine
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"fmt"
							 | 
						|
									"math"
							 | 
						|
									"strings"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb/schema_pb"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								// ===============================
							 | 
						|
								// STRING FUNCTIONS
							 | 
						|
								// ===============================
							 | 
						|
								
							 | 
						|
								// Length returns the length of a string
							 | 
						|
								func (e *SQLEngine) Length(value *schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if value == nil {
							 | 
						|
										return nil, fmt.Errorf("LENGTH function requires non-null value")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									str, err := e.valueToString(value)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("LENGTH function conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									length := int64(len(str))
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_Int64Value{Int64Value: length},
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Upper converts a string to uppercase
							 | 
						|
								func (e *SQLEngine) Upper(value *schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if value == nil {
							 | 
						|
										return nil, fmt.Errorf("UPPER function requires non-null value")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									str, err := e.valueToString(value)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("UPPER function conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_StringValue{StringValue: strings.ToUpper(str)},
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Lower converts a string to lowercase
							 | 
						|
								func (e *SQLEngine) Lower(value *schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if value == nil {
							 | 
						|
										return nil, fmt.Errorf("LOWER function requires non-null value")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									str, err := e.valueToString(value)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("LOWER function conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_StringValue{StringValue: strings.ToLower(str)},
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Trim removes leading and trailing whitespace from a string
							 | 
						|
								func (e *SQLEngine) Trim(value *schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if value == nil {
							 | 
						|
										return nil, fmt.Errorf("TRIM function requires non-null value")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									str, err := e.valueToString(value)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("TRIM function conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_StringValue{StringValue: strings.TrimSpace(str)},
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// LTrim removes leading whitespace from a string
							 | 
						|
								func (e *SQLEngine) LTrim(value *schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if value == nil {
							 | 
						|
										return nil, fmt.Errorf("LTRIM function requires non-null value")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									str, err := e.valueToString(value)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("LTRIM function conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_StringValue{StringValue: strings.TrimLeft(str, " \t\n\r")},
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// RTrim removes trailing whitespace from a string
							 | 
						|
								func (e *SQLEngine) RTrim(value *schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if value == nil {
							 | 
						|
										return nil, fmt.Errorf("RTRIM function requires non-null value")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									str, err := e.valueToString(value)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("RTRIM function conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_StringValue{StringValue: strings.TrimRight(str, " \t\n\r")},
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Substring extracts a substring from a string
							 | 
						|
								func (e *SQLEngine) Substring(value *schema_pb.Value, start *schema_pb.Value, length ...*schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if value == nil || start == nil {
							 | 
						|
										return nil, fmt.Errorf("SUBSTRING function requires non-null value and start position")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									str, err := e.valueToString(value)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("SUBSTRING function value conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									startPos, err := e.valueToInt64(start)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("SUBSTRING function start position conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Convert to 0-based indexing (SQL uses 1-based)
							 | 
						|
									if startPos < 1 {
							 | 
						|
										startPos = 1
							 | 
						|
									}
							 | 
						|
									startIdx := int(startPos - 1)
							 | 
						|
								
							 | 
						|
									if startIdx >= len(str) {
							 | 
						|
										return &schema_pb.Value{
							 | 
						|
											Kind: &schema_pb.Value_StringValue{StringValue: ""},
							 | 
						|
										}, nil
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									var result string
							 | 
						|
									if len(length) > 0 && length[0] != nil {
							 | 
						|
										lengthVal, err := e.valueToInt64(length[0])
							 | 
						|
										if err != nil {
							 | 
						|
											return nil, fmt.Errorf("SUBSTRING function length conversion error: %v", err)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if lengthVal <= 0 {
							 | 
						|
											result = ""
							 | 
						|
										} else {
							 | 
						|
											if lengthVal > int64(math.MaxInt) || lengthVal < int64(math.MinInt) {
							 | 
						|
												// If length is out-of-bounds for int, take substring from startIdx to end
							 | 
						|
												result = str[startIdx:]
							 | 
						|
											} else {
							 | 
						|
												// Safe conversion after bounds check
							 | 
						|
												endIdx := startIdx + int(lengthVal)
							 | 
						|
												if endIdx > len(str) {
							 | 
						|
													endIdx = len(str)
							 | 
						|
												}
							 | 
						|
												result = str[startIdx:endIdx]
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									} else {
							 | 
						|
										result = str[startIdx:]
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_StringValue{StringValue: result},
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Concat concatenates multiple strings
							 | 
						|
								func (e *SQLEngine) Concat(values ...*schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if len(values) == 0 {
							 | 
						|
										return &schema_pb.Value{
							 | 
						|
											Kind: &schema_pb.Value_StringValue{StringValue: ""},
							 | 
						|
										}, nil
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									var result strings.Builder
							 | 
						|
									for i, value := range values {
							 | 
						|
										if value == nil {
							 | 
						|
											continue // Skip null values
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										str, err := e.valueToString(value)
							 | 
						|
										if err != nil {
							 | 
						|
											return nil, fmt.Errorf("CONCAT function value %d conversion error: %v", i, err)
							 | 
						|
										}
							 | 
						|
										result.WriteString(str)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_StringValue{StringValue: result.String()},
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Replace replaces all occurrences of a substring with another substring
							 | 
						|
								func (e *SQLEngine) Replace(value, oldStr, newStr *schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if value == nil || oldStr == nil || newStr == nil {
							 | 
						|
										return nil, fmt.Errorf("REPLACE function requires non-null values")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									str, err := e.valueToString(value)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("REPLACE function value conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									old, err := e.valueToString(oldStr)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("REPLACE function old string conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									new, err := e.valueToString(newStr)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("REPLACE function new string conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									result := strings.ReplaceAll(str, old, new)
							 | 
						|
								
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_StringValue{StringValue: result},
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Position returns the position of a substring in a string (1-based, 0 if not found)
							 | 
						|
								func (e *SQLEngine) Position(substring, value *schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if substring == nil || value == nil {
							 | 
						|
										return nil, fmt.Errorf("POSITION function requires non-null values")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									str, err := e.valueToString(value)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("POSITION function string conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									substr, err := e.valueToString(substring)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("POSITION function substring conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									pos := strings.Index(str, substr)
							 | 
						|
									if pos == -1 {
							 | 
						|
										pos = 0 // SQL returns 0 for not found
							 | 
						|
									} else {
							 | 
						|
										pos = pos + 1 // Convert to 1-based indexing
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_Int64Value{Int64Value: int64(pos)},
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Left returns the leftmost characters of a string
							 | 
						|
								func (e *SQLEngine) Left(value *schema_pb.Value, length *schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if value == nil || length == nil {
							 | 
						|
										return nil, fmt.Errorf("LEFT function requires non-null values")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									str, err := e.valueToString(value)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("LEFT function string conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									lengthVal, err := e.valueToInt64(length)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("LEFT function length conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if lengthVal <= 0 {
							 | 
						|
										return &schema_pb.Value{
							 | 
						|
											Kind: &schema_pb.Value_StringValue{StringValue: ""},
							 | 
						|
										}, nil
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if lengthVal > int64(len(str)) {
							 | 
						|
										return &schema_pb.Value{
							 | 
						|
											Kind: &schema_pb.Value_StringValue{StringValue: str},
							 | 
						|
										}, nil
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if lengthVal > int64(math.MaxInt) || lengthVal < int64(math.MinInt) {
							 | 
						|
										return &schema_pb.Value{
							 | 
						|
											Kind: &schema_pb.Value_StringValue{StringValue: str},
							 | 
						|
										}, nil
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Safe conversion after bounds check
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_StringValue{StringValue: str[:int(lengthVal)]},
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Right returns the rightmost characters of a string
							 | 
						|
								func (e *SQLEngine) Right(value *schema_pb.Value, length *schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if value == nil || length == nil {
							 | 
						|
										return nil, fmt.Errorf("RIGHT function requires non-null values")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									str, err := e.valueToString(value)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("RIGHT function string conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									lengthVal, err := e.valueToInt64(length)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("RIGHT function length conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if lengthVal <= 0 {
							 | 
						|
										return &schema_pb.Value{
							 | 
						|
											Kind: &schema_pb.Value_StringValue{StringValue: ""},
							 | 
						|
										}, nil
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if lengthVal > int64(len(str)) {
							 | 
						|
										return &schema_pb.Value{
							 | 
						|
											Kind: &schema_pb.Value_StringValue{StringValue: str},
							 | 
						|
										}, nil
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if lengthVal > int64(math.MaxInt) || lengthVal < int64(math.MinInt) {
							 | 
						|
										return &schema_pb.Value{
							 | 
						|
											Kind: &schema_pb.Value_StringValue{StringValue: str},
							 | 
						|
										}, nil
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Safe conversion after bounds check
							 | 
						|
									startPos := len(str) - int(lengthVal)
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_StringValue{StringValue: str[startPos:]},
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Reverse reverses a string
							 | 
						|
								func (e *SQLEngine) Reverse(value *schema_pb.Value) (*schema_pb.Value, error) {
							 | 
						|
									if value == nil {
							 | 
						|
										return nil, fmt.Errorf("REVERSE function requires non-null value")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									str, err := e.valueToString(value)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("REVERSE function conversion error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Reverse the string rune by rune to handle Unicode correctly
							 | 
						|
									runes := []rune(str)
							 | 
						|
									for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
							 | 
						|
										runes[i], runes[j] = runes[j], runes[i]
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return &schema_pb.Value{
							 | 
						|
										Kind: &schema_pb.Value_StringValue{StringValue: string(runes)},
							 | 
						|
									}, nil
							 | 
						|
								}
							 |