From 2fccb87dcfc575b7618e66c52f8304edae2754c7 Mon Sep 17 00:00:00 2001 From: HHans09 <91560812+HHans09@users.noreply.github.com> Date: Tue, 17 Jan 2023 16:58:55 -0500 Subject: [PATCH] Fb 1876 : Implement Replicate() func (#2410) * fb-1876 : creating Replicate fun * fb-1876: creating string replicate func * Fb-1876: String Replicate func * Fb-1876: String Replicate func --- sql3/planner/expression.go | 2 + sql3/planner/expressionanalyzercall.go | 6 ++- sql3/planner/inbuiltfunctionsstring.go | 34 +++++++++++++++- sql3/test/defs/defs_string_functions.go | 54 +++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 3 deletions(-) diff --git a/sql3/planner/expression.go b/sql3/planner/expression.go index 5fb64ec6f..d76a04807 100644 --- a/sql3/planner/expression.go +++ b/sql3/planner/expression.go @@ -1518,6 +1518,8 @@ func (n *callPlanExpression) Evaluate(currentRow []interface{}) (interface{}, er return n.EvaluateSpace(currentRow) case "LEN": return n.EvaluateLen(currentRow) + case "REPLICATE": + return n.EvaluateReplicate(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 b7369f361..dc3cfec7f 100644 --- a/sql3/planner/expressionanalyzercall.go +++ b/sql3/planner/expressionanalyzercall.go @@ -264,13 +264,15 @@ func (p *ExecutionPlanner) analyzeCallExpression(call *parser.Call, scope parser case "LTRIM": return p.analyseFunctionTrim(call, scope) case "SUFFIX": - return p.analyseFunctionPrefixSuffix(call, scope) + return p.analyseFunctionPrefixSuffixReplicate(call, scope) case "PREFIX": - return p.analyseFunctionPrefixSuffix(call, scope) + return p.analyseFunctionPrefixSuffixReplicate(call, scope) case "SPACE": return p.analyseFunctionSpace(call, scope) case "LEN": return p.analyseFunctionLen(call, scope) + case "REPLICATE": + return p.analyseFunctionPrefixSuffixReplicate(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 dca5cda21..ad665623c 100644 --- a/sql3/planner/inbuiltfunctionsstring.go +++ b/sql3/planner/inbuiltfunctionsstring.go @@ -163,7 +163,7 @@ func (p *ExecutionPlanner) analyseFunctionTrim(call *parser.Call, scope parser.S return call, nil } -func (p *ExecutionPlanner) analyseFunctionPrefixSuffix(call *parser.Call, scope parser.Statement) (parser.Expr, error) { +func (p *ExecutionPlanner) analyseFunctionPrefixSuffixReplicate(call *parser.Call, scope parser.Statement) (parser.Expr, error) { if len(call.Args) != 2 { return nil, sql3.NewErrCallParameterCountMismatch(call.Rparen.Line, call.Rparen.Column, call.Name.Name, 2, len(call.Args)) } @@ -612,3 +612,35 @@ func (n *callPlanExpression) EvaluateLen(currentRow []interface{}) (interface{}, return int64(len([]rune(stringArg))), nil } +func (n *callPlanExpression) EvaluateReplicate(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) + } + + argEval, err = n.args[1].Evaluate(currentRow) + if err != nil { + return nil, err + } + if argEval == nil { + return nil, nil + } + intArg, ok := argEval.(int64) + if !ok { + return nil, sql3.NewErrInternalf("unexpected type converion %T", argEval) + } + + if intArg < 0 { + return nil, sql3.NewErrValueOutOfRange(0, 0, intArg) + } + + return strings.Repeat(stringArg, int(intArg)), nil + +} diff --git a/sql3/test/defs/defs_string_functions.go b/sql3/test/defs/defs_string_functions.go index 54529285f..916844b9a 100644 --- a/sql3/test/defs/defs_string_functions.go +++ b/sql3/test/defs/defs_string_functions.go @@ -766,5 +766,59 @@ var stringScalarFunctionsTests = TableTest{ ), Compare: CompareExactOrdered, }, + { + name: "ReplicateString", + SQLs: sqls( + "select replicate('this',2)", + ), + ExpHdrs: hdrs( + hdr("", fldTypeString), + ), + ExpRows: rows( + row(string("thisthis")), + ), + Compare: CompareExactOrdered, + }, + { + name: "ReplicateNull", + SQLs: sqls( + "select replicate(null,null)", + ), + ExpHdrs: hdrs( + hdr("", fldTypeString), + ), + ExpRows: rows( + row(nil), + ), + Compare: CompareExactOrdered, + }, + { + name: "ReplicateincorrectArguments", + SQLs: sqls( + "select replicate('this',)", + ), + ExpErr: "[1:25] 'replicate': count of formal parameters (2) does not match count of actual parameters (1)", + }, + { + name: "ReplicateincorrectTypeofArguments", + SQLs: sqls( + "select replicate(1,2)", + ), + ExpErr: "[1:18] string expression expected", + }, + { + name: "ReplicateincorrectTypeofArguments", + SQLs: sqls( + "select replicate('this','this')", + ), + ExpErr: "[1:25] integer expression expected", + }, + { + name: "ReplicateOutofRange", + SQLs: sqls( + "select replicate('this',-1)", + ), + ExpErr: "[0:0] value '-1' out of range", + }, }, }