Fb-1816 : CharIndex str func (#2216)

* rebased and updated

* Updates per review comments

Rebased

* Rebased

* Rebased
This commit is contained in:
HHans09 2023-01-30 16:08:46 -05:00 committed by GitHub
parent 188b61b3cd
commit b12c90fdd1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 172 additions and 1 deletions

View file

@ -1522,6 +1522,8 @@ func (n *callPlanExpression) Evaluate(currentRow []interface{}) (interface{}, er
return n.EvaluateReplicate(currentRow)
case "FORMAT":
return n.EvaluateFormat(currentRow)
case "CHARINDEX":
return n.EvaluateCharIndex(currentRow)
default:
return nil, sql3.NewErrInternalf("unhandled function name '%s'", n.name)
}

View file

@ -275,6 +275,8 @@ func (p *ExecutionPlanner) analyzeCallExpression(call *parser.Call, scope parser
return p.analyseFunctionPrefixSuffixReplicate(call, scope)
case "FORMAT":
return p.analyseFunctionFormat(call, scope)
case "CHARINDEX":
return p.analyseFunctionCharIndex(call, scope)
default:
return nil, sql3.NewErrCallUnknownFunction(call.Name.NamePos.Line, call.Name.NamePos.Column, call.Name.Name)
}

View file

@ -214,7 +214,6 @@ func (p *ExecutionPlanner) analyseFunctionFormat(call *parser.Call, scope parser
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)
}
@ -228,6 +227,31 @@ func (p *ExecutionPlanner) analyseFunctionFormat(call *parser.Call, scope parser
return call, nil
}
// charindex(substring, str, pos)
func (p *ExecutionPlanner) analyseFunctionCharIndex(call *parser.Call, scope parser.Statement) (parser.Expr, error) {
if len(call.Args) <= 1 || len(call.Args) > 3 {
return nil, sql3.NewErrCallParameterCountMismatch(call.Rparen.Line, call.Rparen.Column, call.Name.Name, 3, len(call.Args))
}
// first paramater is the substring for which index needs to be identified
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)
}
// second paramater is the string where we search the position of the substring ( first param)
if !typeIsString(call.Args[1].DataType()) && !typeIsVoid(call.Args[1].DataType()) {
return nil, sql3.NewErrStringExpressionExpected(call.Args[1].Pos().Line, call.Args[1].Pos().Column)
}
// the third parameter is the position. optional, defaults to 0
if len(call.Args) == 3 && !typeIsInteger(call.Args[2].DataType()) && !typeIsVoid(call.Args[2].DataType()) {
return nil, sql3.NewErrIntExpressionExpected(call.Args[2].Pos().Line, call.Args[2].Pos().Column)
}
call.ResultDataType = parser.NewDataTypeInt()
return call, nil
}
// EvaluateReverse reverses the string
func (n *callPlanExpression) EvaluateReverse(currentRow []interface{}) (interface{}, error) {
argEval, err := n.args[0].Evaluate(currentRow)
@ -697,3 +721,65 @@ func (n *callPlanExpression) EvaluateFormat(currentRow []interface{}) (interface
}
return fmt.Sprintf(formatString, args...), nil
}
// EvaluateCharIndex function gets the position of the substring in the given string
func (n *callPlanExpression) EvaluateCharIndex(currentRow []interface{}) (interface{}, error) {
argEval, err := n.args[0].Evaluate(currentRow)
if err != nil {
return nil, err
}
if argEval == nil {
return nil, nil
}
subString, ok := argEval.(string)
if !ok {
return nil, sql3.NewErrUnexpectedTypeConversion(0, 0, argEval)
}
argEval, err = n.args[1].Evaluate(currentRow)
if err != nil {
return nil, err
}
if argEval == nil {
return nil, nil
}
inputString, ok := argEval.(string)
if !ok {
return nil, sql3.NewErrUnexpectedTypeConversion(0, 0, argEval)
}
if len(n.args) == 2 {
res := strings.Index(inputString, subString)
if res > -1 {
newpos := int64(res)
return int64(newpos), nil
}
return int64(res), nil
}
argEval, err = n.args[2].Evaluate(currentRow)
if err != nil {
return nil, err
}
if argEval == nil {
return nil, nil
}
pos, ok := argEval.(int64)
if !ok {
return nil, sql3.NewErrUnexpectedTypeConversion(0, 0, argEval)
}
if pos < 0 {
return nil, sql3.NewErrValueOutOfRange(0, 0, pos)
}
if pos >= int64(len(inputString)) {
return nil, sql3.NewErrValueOutOfRange(0, 0, pos)
}
res := strings.Index(inputString[pos:], subString)
if res > -1 {
newpos := int64(res) + pos
return int64(newpos), nil
}
return int64(res), nil
}

View file

@ -901,5 +901,86 @@ var stringScalarFunctionsTests = TableTest{
),
Compare: CompareExactOrdered,
},
{
name: "CharIndexIncorrectNumberofArguments",
SQLs: sqls(
"select charindex('is','this is great',3,4)",
),
ExpErr: "'charindex': count of formal parameters (3) does not match count of actual parameters (4)",
},
{
name: "CharIndexIncorrectTypeofArgumentsforPosition",
SQLs: sqls(
"select charindex('is','this is great','you')",
),
ExpErr: "integer expression expected",
},
{
name: "CharIndexIncorrectTypeofArgumentsforInputString",
SQLs: sqls(
"select charindex('is',23,3)",
),
ExpErr: "string expression expected",
},
{
name: "CharIndexIncorrectTypeofArgumentsforSubString",
SQLs: sqls(
"select charindex(1,'this is great',3)",
),
ExpErr: "string expression expected",
},
{
name: "CharIndexPositionOutofRangewithNegativePosition",
SQLs: sqls(
"select charindex('is','this is great',-1)",
),
ExpErr: "value '-1' out of range",
},
{
name: "CharIndexPositionOutofRange",
SQLs: sqls(
"select charindex('is','this is great',15)",
),
ExpErr: "value '15' out of range",
},
{
name: "CharIndexofSubstring",
SQLs: sqls(
"Select charindex('is','this is great')",
),
ExpHdrs: hdrs(
hdr("", fldTypeInt),
),
ExpRows: rows(
row(int64(2)),
),
Compare: CompareExactOrdered,
},
{
name: "CharIndexofSubstringwithPosition",
SQLs: sqls(
"Select charindex('is','this is great',3)",
),
ExpHdrs: hdrs(
hdr("", fldTypeInt),
),
ExpRows: rows(
row(int64(5)),
),
Compare: CompareExactOrdered,
},
{
name: "CharIndexSubstringNotfound",
SQLs: sqls(
"Select charindex('abc','this is great',3)",
),
ExpHdrs: hdrs(
hdr("", fldTypeInt),
),
ExpRows: rows(
row(int64(-1)),
),
Compare: CompareExactOrdered,
},
},
}