From fc1b8fdfb8a3070fb54703f741bbd0661f5796af Mon Sep 17 00:00:00 2001 From: rachithrr Date: Thu, 12 Jan 2023 18:01:06 -0500 Subject: [PATCH] FB-1827: Implement Len() (#2406) (cherry picked from commit f99be656dfdab0cfda3b34d74afa12e6b511d83d) --- sql3/planner/expression.go | 2 ++ sql3/planner/expressionanalyzercall.go | 2 ++ sql3/planner/inbuiltfunctionsstring.go | 32 ++++++++++++++++++++ sql3/test/defs/defs_string_functions.go | 39 +++++++++++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/sql3/planner/expression.go b/sql3/planner/expression.go index e0ad68c1f..2253719a0 100644 --- a/sql3/planner/expression.go +++ b/sql3/planner/expression.go @@ -1516,6 +1516,8 @@ func (n *callPlanExpression) Evaluate(currentRow []interface{}) (interface{}, er return n.EvaluatePrefix(currentRow) case "SPACE": return n.EvaluateSpace(currentRow) + case "LEN": + return n.EvaluateLen(currentRow) default: return nil, sql3.NewErrInternalf("unhandled function name '%s'", n.name) } diff --git a/sql3/planner/expressionanalyzercall.go b/sql3/planner/expressionanalyzercall.go index a2b6d04b1..1b743b913 100644 --- a/sql3/planner/expressionanalyzercall.go +++ b/sql3/planner/expressionanalyzercall.go @@ -269,6 +269,8 @@ func (p *ExecutionPlanner) analyzeCallExpression(call *parser.Call, scope parser return p.analyseFunctionPrefixSuffix(call, scope) case "SPACE": return p.analyseFunctionSpace(call, scope) + case "LEN": + return p.analyseFunctionLen(call, scope) default: return nil, sql3.NewErrCallUnknownFunction(call.Name.NamePos.Line, call.Name.NamePos.Column, call.Name.Name) } diff --git a/sql3/planner/inbuiltfunctionsstring.go b/sql3/planner/inbuiltfunctionsstring.go index fda45b18d..52bb71e28 100644 --- a/sql3/planner/inbuiltfunctionsstring.go +++ b/sql3/planner/inbuiltfunctionsstring.go @@ -195,6 +195,18 @@ func (p *ExecutionPlanner) analyseFunctionSpace(call *parser.Call, scope parser. return call, nil } +func (p *ExecutionPlanner) analyseFunctionLen(call *parser.Call, scope parser.Statement) (parser.Expr, error) { + if len(call.Args) != 1 { + return nil, sql3.NewErrCallParameterCountMismatch(call.Rparen.Line, call.Rparen.Column, call.Name.Name, 1, len(call.Args)) + } + + if !typeIsString(call.Args[0].DataType()) && !typeIsVoid(call.Args[0].DataType()) { + return nil, sql3.NewErrStringExpressionExpected(call.Args[0].Pos().Line, call.Args[0].Pos().Column) + } + call.ResultDataType = parser.NewDataTypeInt() + return call, nil +} + // reverses the string func (n *callPlanExpression) EvaluateReverse(currentRow []interface{}) (interface{}, error) { argEval, err := n.args[0].Evaluate(currentRow) @@ -264,6 +276,10 @@ func (n *callPlanExpression) EvaluateChar(currentRow []interface{}) (interface{} if !ok { return 0, sql3.NewErrInternalf("unexpected type converion %T", argEval) } + // ascii range is [0-255] + if intArg < 0 || intArg > 255 { + return nil, sql3.NewErrValueOutOfRange(0, 0, intArg) + } // Return the character that corresponds to the integer value return string(rune(intArg)), nil @@ -580,3 +596,19 @@ func (n *callPlanExpression) EvaluateSpace(currentRow []interface{}) (interface{ } return spaces, nil } + +func (n *callPlanExpression) EvaluateLen(currentRow []interface{}) (interface{}, error) { + argEval, err := n.args[0].Evaluate(currentRow) + if err != nil { + return nil, err + } + if argEval == nil { + return nil, nil + } + stringArg, ok := argEval.(string) + if !ok { + return nil, sql3.NewErrInternalf("unexpected type converion %T", argEval) + } + + return int64(len([]rune(stringArg))), nil +} diff --git a/sql3/test/defs/defs_string_functions.go b/sql3/test/defs/defs_string_functions.go index 1790fd8e4..54529285f 100644 --- a/sql3/test/defs/defs_string_functions.go +++ b/sql3/test/defs/defs_string_functions.go @@ -727,5 +727,44 @@ var stringScalarFunctionsTests = TableTest{ ), Compare: CompareExactOrdered, }, + { + name: "LenNull", + SQLs: sqls( + "select len(null)", + ), + ExpHdrs: hdrs( + hdr("", fldTypeInt), + ), + ExpRows: rows( + row(nil), + ), + Compare: CompareExactOrdered, + }, + { + name: "LenString", + SQLs: sqls( + "select len(' length ')", + ), + ExpHdrs: hdrs( + hdr("", fldTypeInt), + ), + ExpRows: rows( + row(int64(9)), + ), + Compare: CompareExactOrdered, + }, + { + name: "LenChar", + SQLs: sqls( + "select len(char(114))", + ), + ExpHdrs: hdrs( + hdr("", fldTypeInt), + ), + ExpRows: rows( + row(int64(1)), + ), + Compare: CompareExactOrdered, + }, }, }