Implement STARTSWITH, ENDSWITH functions

This commit is contained in:
Pijus Kamandulis 2024-02-21 20:46:08 +02:00
parent 9bf3dc22ed
commit 790192bf5a
7 changed files with 460 additions and 203 deletions

View File

@ -84,6 +84,8 @@ const (
FunctionCallIsDefined FunctionCallType = "IsDefined" FunctionCallIsDefined FunctionCallType = "IsDefined"
FunctionCallConcat FunctionCallType = "Concat" FunctionCallConcat FunctionCallType = "Concat"
FunctionCallContains FunctionCallType = "Contains" FunctionCallContains FunctionCallType = "Contains"
FunctionCallEndsWith FunctionCallType = "EndsWith"
FunctionCallStartsWith FunctionCallType = "StartsWith"
) )
type FunctionCall struct { type FunctionCall struct {

View File

@ -1587,110 +1587,123 @@ var g = &grammar{
alternatives: []any{ alternatives: []any{
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 309, col: 17, offset: 8931}, pos: position{line: 309, col: 17, offset: 8931},
name: "StringEqualsExpression", name: "StringFunctions",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 309, col: 42, offset: 8956}, pos: position{line: 309, col: 35, offset: 8949},
name: "ConcatExpression",
},
&ruleRefExpr{
pos: position{line: 309, col: 61, offset: 8975},
name: "ContainsExpression",
},
&ruleRefExpr{
pos: position{line: 309, col: 82, offset: 8996},
name: "IsDefined", name: "IsDefined",
}, },
}, },
}, },
}, },
{ {
name: "StringFunctions",
pos: position{line: 311, col: 1, offset: 8960},
expr: &choiceExpr{
pos: position{line: 311, col: 20, offset: 8979},
alternatives: []any{
&ruleRefExpr{
pos: position{line: 311, col: 20, offset: 8979},
name: "StringEqualsExpression", name: "StringEqualsExpression",
pos: position{line: 311, col: 1, offset: 9007}, },
&ruleRefExpr{
pos: position{line: 312, col: 7, offset: 9008},
name: "ConcatExpression",
},
&ruleRefExpr{
pos: position{line: 313, col: 7, offset: 9031},
name: "ThreeArgumentStringFunctionExpression",
},
},
},
},
{
name: "StringEqualsExpression",
pos: position{line: 315, col: 1, offset: 9070},
expr: &actionExpr{ expr: &actionExpr{
pos: position{line: 311, col: 27, offset: 9033}, pos: position{line: 315, col: 27, offset: 9096},
run: (*parser).callonStringEqualsExpression1, run: (*parser).callonStringEqualsExpression1,
expr: &seqExpr{ expr: &seqExpr{
pos: position{line: 311, col: 27, offset: 9033}, pos: position{line: 315, col: 27, offset: 9096},
exprs: []any{ exprs: []any{
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 311, col: 27, offset: 9033}, pos: position{line: 315, col: 27, offset: 9096},
name: "StringEquals", name: "StringEquals",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 311, col: 40, offset: 9046}, pos: position{line: 315, col: 40, offset: 9109},
name: "ws", name: "ws",
}, },
&litMatcher{ &litMatcher{
pos: position{line: 311, col: 43, offset: 9049}, pos: position{line: 315, col: 43, offset: 9112},
val: "(", val: "(",
ignoreCase: false, ignoreCase: false,
want: "\"(\"", want: "\"(\"",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 311, col: 47, offset: 9053}, pos: position{line: 315, col: 47, offset: 9116},
name: "ws", name: "ws",
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 311, col: 50, offset: 9056}, pos: position{line: 315, col: 50, offset: 9119},
label: "ex1", label: "ex1",
expr: &ruleRefExpr{ expr: &ruleRefExpr{
pos: position{line: 311, col: 54, offset: 9060}, pos: position{line: 315, col: 54, offset: 9123},
name: "SelectItem", name: "SelectItem",
}, },
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 311, col: 65, offset: 9071}, pos: position{line: 315, col: 65, offset: 9134},
name: "ws", name: "ws",
}, },
&litMatcher{ &litMatcher{
pos: position{line: 311, col: 68, offset: 9074}, pos: position{line: 315, col: 68, offset: 9137},
val: ",", val: ",",
ignoreCase: false, ignoreCase: false,
want: "\",\"", want: "\",\"",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 311, col: 72, offset: 9078}, pos: position{line: 315, col: 72, offset: 9141},
name: "ws", name: "ws",
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 311, col: 75, offset: 9081}, pos: position{line: 315, col: 75, offset: 9144},
label: "ex2", label: "ex2",
expr: &ruleRefExpr{ expr: &ruleRefExpr{
pos: position{line: 311, col: 79, offset: 9085}, pos: position{line: 315, col: 79, offset: 9148},
name: "SelectItem", name: "SelectItem",
}, },
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 311, col: 90, offset: 9096}, pos: position{line: 315, col: 90, offset: 9159},
name: "ws", name: "ws",
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 311, col: 93, offset: 9099}, pos: position{line: 315, col: 93, offset: 9162},
label: "ignoreCase", label: "ignoreCase",
expr: &zeroOrOneExpr{ expr: &zeroOrOneExpr{
pos: position{line: 311, col: 104, offset: 9110}, pos: position{line: 315, col: 104, offset: 9173},
expr: &actionExpr{ expr: &actionExpr{
pos: position{line: 311, col: 105, offset: 9111}, pos: position{line: 315, col: 105, offset: 9174},
run: (*parser).callonStringEqualsExpression17, run: (*parser).callonStringEqualsExpression17,
expr: &seqExpr{ expr: &seqExpr{
pos: position{line: 311, col: 105, offset: 9111}, pos: position{line: 315, col: 105, offset: 9174},
exprs: []any{ exprs: []any{
&litMatcher{ &litMatcher{
pos: position{line: 311, col: 105, offset: 9111}, pos: position{line: 315, col: 105, offset: 9174},
val: ",", val: ",",
ignoreCase: false, ignoreCase: false,
want: "\",\"", want: "\",\"",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 311, col: 109, offset: 9115}, pos: position{line: 315, col: 109, offset: 9178},
name: "ws", name: "ws",
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 311, col: 112, offset: 9118}, pos: position{line: 315, col: 112, offset: 9181},
label: "boolean", label: "boolean",
expr: &ruleRefExpr{ expr: &ruleRefExpr{
pos: position{line: 311, col: 120, offset: 9126}, pos: position{line: 315, col: 120, offset: 9189},
name: "SelectItem", name: "SelectItem",
}, },
}, },
@ -1700,7 +1713,7 @@ var g = &grammar{
}, },
}, },
&litMatcher{ &litMatcher{
pos: position{line: 311, col: 157, offset: 9163}, pos: position{line: 315, col: 157, offset: 9226},
val: ")", val: ")",
ignoreCase: false, ignoreCase: false,
want: "\")\"", want: "\")\"",
@ -1711,71 +1724,71 @@ var g = &grammar{
}, },
{ {
name: "ConcatExpression", name: "ConcatExpression",
pos: position{line: 315, col: 1, offset: 9297}, pos: position{line: 319, col: 1, offset: 9360},
expr: &actionExpr{ expr: &actionExpr{
pos: position{line: 315, col: 21, offset: 9317}, pos: position{line: 319, col: 21, offset: 9380},
run: (*parser).callonConcatExpression1, run: (*parser).callonConcatExpression1,
expr: &seqExpr{ expr: &seqExpr{
pos: position{line: 315, col: 21, offset: 9317}, pos: position{line: 319, col: 21, offset: 9380},
exprs: []any{ exprs: []any{
&litMatcher{ &litMatcher{
pos: position{line: 315, col: 21, offset: 9317}, pos: position{line: 319, col: 21, offset: 9380},
val: "concat", val: "concat",
ignoreCase: true, ignoreCase: true,
want: "\"CONCAT\"i", want: "\"CONCAT\"i",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 315, col: 31, offset: 9327}, pos: position{line: 319, col: 31, offset: 9390},
name: "ws", name: "ws",
}, },
&litMatcher{ &litMatcher{
pos: position{line: 315, col: 34, offset: 9330}, pos: position{line: 319, col: 34, offset: 9393},
val: "(", val: "(",
ignoreCase: false, ignoreCase: false,
want: "\"(\"", want: "\"(\"",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 315, col: 38, offset: 9334}, pos: position{line: 319, col: 38, offset: 9397},
name: "ws", name: "ws",
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 315, col: 41, offset: 9337}, pos: position{line: 319, col: 41, offset: 9400},
label: "ex1", label: "ex1",
expr: &ruleRefExpr{ expr: &ruleRefExpr{
pos: position{line: 315, col: 45, offset: 9341}, pos: position{line: 319, col: 45, offset: 9404},
name: "SelectItem", name: "SelectItem",
}, },
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 315, col: 56, offset: 9352}, pos: position{line: 319, col: 56, offset: 9415},
label: "others", label: "others",
expr: &oneOrMoreExpr{ expr: &oneOrMoreExpr{
pos: position{line: 315, col: 63, offset: 9359}, pos: position{line: 319, col: 63, offset: 9422},
expr: &actionExpr{ expr: &actionExpr{
pos: position{line: 315, col: 64, offset: 9360}, pos: position{line: 319, col: 64, offset: 9423},
run: (*parser).callonConcatExpression11, run: (*parser).callonConcatExpression11,
expr: &seqExpr{ expr: &seqExpr{
pos: position{line: 315, col: 64, offset: 9360}, pos: position{line: 319, col: 64, offset: 9423},
exprs: []any{ exprs: []any{
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 315, col: 64, offset: 9360}, pos: position{line: 319, col: 64, offset: 9423},
name: "ws", name: "ws",
}, },
&litMatcher{ &litMatcher{
pos: position{line: 315, col: 67, offset: 9363}, pos: position{line: 319, col: 67, offset: 9426},
val: ",", val: ",",
ignoreCase: false, ignoreCase: false,
want: "\",\"", want: "\",\"",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 315, col: 71, offset: 9367}, pos: position{line: 319, col: 71, offset: 9430},
name: "ws", name: "ws",
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 315, col: 74, offset: 9370}, pos: position{line: 319, col: 74, offset: 9433},
label: "ex", label: "ex",
expr: &ruleRefExpr{ expr: &ruleRefExpr{
pos: position{line: 315, col: 77, offset: 9373}, pos: position{line: 319, col: 77, offset: 9436},
name: "SelectItem", name: "SelectItem",
}, },
}, },
@ -1785,11 +1798,11 @@ var g = &grammar{
}, },
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 315, col: 109, offset: 9405}, pos: position{line: 319, col: 109, offset: 9468},
name: "ws", name: "ws",
}, },
&litMatcher{ &litMatcher{
pos: position{line: 315, col: 112, offset: 9408}, pos: position{line: 319, col: 112, offset: 9471},
val: ")", val: ")",
ignoreCase: false, ignoreCase: false,
want: "\")\"", want: "\")\"",
@ -1799,94 +1812,96 @@ var g = &grammar{
}, },
}, },
{ {
name: "ContainsExpression", name: "ThreeArgumentStringFunctionExpression",
pos: position{line: 320, col: 1, offset: 9581}, pos: position{line: 324, col: 1, offset: 9644},
expr: &actionExpr{ expr: &actionExpr{
pos: position{line: 320, col: 23, offset: 9603}, pos: position{line: 324, col: 42, offset: 9685},
run: (*parser).callonContainsExpression1, run: (*parser).callonThreeArgumentStringFunctionExpression1,
expr: &seqExpr{ expr: &seqExpr{
pos: position{line: 320, col: 23, offset: 9603}, pos: position{line: 324, col: 42, offset: 9685},
exprs: []any{ exprs: []any{
&litMatcher{ &labeledExpr{
pos: position{line: 320, col: 23, offset: 9603}, pos: position{line: 324, col: 42, offset: 9685},
val: "contains", label: "function",
ignoreCase: true, expr: &ruleRefExpr{
want: "\"CONTAINS\"i", pos: position{line: 324, col: 51, offset: 9694},
name: "ThreeArgumentStringFunction",
},
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 320, col: 35, offset: 9615}, pos: position{line: 324, col: 79, offset: 9722},
name: "ws", name: "ws",
}, },
&litMatcher{ &litMatcher{
pos: position{line: 320, col: 38, offset: 9618}, pos: position{line: 324, col: 82, offset: 9725},
val: "(", val: "(",
ignoreCase: false, ignoreCase: false,
want: "\"(\"", want: "\"(\"",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 320, col: 42, offset: 9622}, pos: position{line: 324, col: 86, offset: 9729},
name: "ws", name: "ws",
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 320, col: 45, offset: 9625}, pos: position{line: 324, col: 89, offset: 9732},
label: "ex1", label: "ex1",
expr: &ruleRefExpr{ expr: &ruleRefExpr{
pos: position{line: 320, col: 49, offset: 9629}, pos: position{line: 324, col: 93, offset: 9736},
name: "SelectItem", name: "SelectItem",
}, },
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 320, col: 60, offset: 9640}, pos: position{line: 324, col: 104, offset: 9747},
name: "ws", name: "ws",
}, },
&litMatcher{ &litMatcher{
pos: position{line: 320, col: 63, offset: 9643}, pos: position{line: 324, col: 107, offset: 9750},
val: ",", val: ",",
ignoreCase: false, ignoreCase: false,
want: "\",\"", want: "\",\"",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 320, col: 67, offset: 9647}, pos: position{line: 324, col: 111, offset: 9754},
name: "ws", name: "ws",
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 320, col: 70, offset: 9650}, pos: position{line: 324, col: 114, offset: 9757},
label: "ex2", label: "ex2",
expr: &ruleRefExpr{ expr: &ruleRefExpr{
pos: position{line: 320, col: 74, offset: 9654}, pos: position{line: 324, col: 118, offset: 9761},
name: "SelectItem", name: "SelectItem",
}, },
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 320, col: 85, offset: 9665}, pos: position{line: 324, col: 129, offset: 9772},
name: "ws", name: "ws",
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 320, col: 88, offset: 9668}, pos: position{line: 324, col: 132, offset: 9775},
label: "ignoreCase", label: "ignoreCase",
expr: &zeroOrOneExpr{ expr: &zeroOrOneExpr{
pos: position{line: 320, col: 99, offset: 9679}, pos: position{line: 324, col: 143, offset: 9786},
expr: &actionExpr{ expr: &actionExpr{
pos: position{line: 320, col: 100, offset: 9680}, pos: position{line: 324, col: 144, offset: 9787},
run: (*parser).callonContainsExpression17, run: (*parser).callonThreeArgumentStringFunctionExpression18,
expr: &seqExpr{ expr: &seqExpr{
pos: position{line: 320, col: 100, offset: 9680}, pos: position{line: 324, col: 144, offset: 9787},
exprs: []any{ exprs: []any{
&litMatcher{ &litMatcher{
pos: position{line: 320, col: 100, offset: 9680}, pos: position{line: 324, col: 144, offset: 9787},
val: ",", val: ",",
ignoreCase: false, ignoreCase: false,
want: "\",\"", want: "\",\"",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 320, col: 104, offset: 9684}, pos: position{line: 324, col: 148, offset: 9791},
name: "ws", name: "ws",
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 320, col: 107, offset: 9687}, pos: position{line: 324, col: 151, offset: 9794},
label: "boolean", label: "boolean",
expr: &ruleRefExpr{ expr: &ruleRefExpr{
pos: position{line: 320, col: 115, offset: 9695}, pos: position{line: 324, col: 159, offset: 9802},
name: "SelectItem", name: "SelectItem",
}, },
}, },
@ -1896,7 +1911,7 @@ var g = &grammar{
}, },
}, },
&litMatcher{ &litMatcher{
pos: position{line: 320, col: 152, offset: 9732}, pos: position{line: 324, col: 196, offset: 9839},
val: ")", val: ")",
ignoreCase: false, ignoreCase: false,
want: "\")\"", want: "\")\"",
@ -1905,49 +1920,76 @@ var g = &grammar{
}, },
}, },
}, },
{
name: "ThreeArgumentStringFunction",
pos: position{line: 340, col: 1, offset: 10313},
expr: &choiceExpr{
pos: position{line: 340, col: 32, offset: 10344},
alternatives: []any{
&litMatcher{
pos: position{line: 340, col: 32, offset: 10344},
val: "contains",
ignoreCase: true,
want: "\"CONTAINS\"i",
},
&litMatcher{
pos: position{line: 340, col: 46, offset: 10358},
val: "endswith",
ignoreCase: true,
want: "\"ENDSWITH\"i",
},
&litMatcher{
pos: position{line: 340, col: 60, offset: 10372},
val: "startswith",
ignoreCase: true,
want: "\"STARTSWITH\"i",
},
},
},
},
{ {
name: "IsDefined", name: "IsDefined",
pos: position{line: 324, col: 1, offset: 9862}, pos: position{line: 342, col: 1, offset: 10387},
expr: &actionExpr{ expr: &actionExpr{
pos: position{line: 324, col: 14, offset: 9875}, pos: position{line: 342, col: 14, offset: 10400},
run: (*parser).callonIsDefined1, run: (*parser).callonIsDefined1,
expr: &seqExpr{ expr: &seqExpr{
pos: position{line: 324, col: 14, offset: 9875}, pos: position{line: 342, col: 14, offset: 10400},
exprs: []any{ exprs: []any{
&litMatcher{ &litMatcher{
pos: position{line: 324, col: 14, offset: 9875}, pos: position{line: 342, col: 14, offset: 10400},
val: "is_defined", val: "is_defined",
ignoreCase: true, ignoreCase: true,
want: "\"IS_DEFINED\"i", want: "\"IS_DEFINED\"i",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 324, col: 28, offset: 9889}, pos: position{line: 342, col: 28, offset: 10414},
name: "ws", name: "ws",
}, },
&litMatcher{ &litMatcher{
pos: position{line: 324, col: 31, offset: 9892}, pos: position{line: 342, col: 31, offset: 10417},
val: "(", val: "(",
ignoreCase: false, ignoreCase: false,
want: "\"(\"", want: "\"(\"",
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 324, col: 35, offset: 9896}, pos: position{line: 342, col: 35, offset: 10421},
name: "ws", name: "ws",
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 324, col: 38, offset: 9899}, pos: position{line: 342, col: 38, offset: 10424},
label: "ex", label: "ex",
expr: &ruleRefExpr{ expr: &ruleRefExpr{
pos: position{line: 324, col: 41, offset: 9902}, pos: position{line: 342, col: 41, offset: 10427},
name: "SelectItem", name: "SelectItem",
}, },
}, },
&ruleRefExpr{ &ruleRefExpr{
pos: position{line: 324, col: 52, offset: 9913}, pos: position{line: 342, col: 52, offset: 10438},
name: "ws", name: "ws",
}, },
&litMatcher{ &litMatcher{
pos: position{line: 324, col: 55, offset: 9916}, pos: position{line: 342, col: 55, offset: 10441},
val: ")", val: ")",
ignoreCase: false, ignoreCase: false,
want: "\")\"", want: "\")\"",
@ -1958,14 +2000,14 @@ var g = &grammar{
}, },
{ {
name: "Integer", name: "Integer",
pos: position{line: 328, col: 1, offset: 10029}, pos: position{line: 346, col: 1, offset: 10554},
expr: &actionExpr{ expr: &actionExpr{
pos: position{line: 328, col: 12, offset: 10040}, pos: position{line: 346, col: 12, offset: 10565},
run: (*parser).callonInteger1, run: (*parser).callonInteger1,
expr: &oneOrMoreExpr{ expr: &oneOrMoreExpr{
pos: position{line: 328, col: 12, offset: 10040}, pos: position{line: 346, col: 12, offset: 10565},
expr: &charClassMatcher{ expr: &charClassMatcher{
pos: position{line: 328, col: 12, offset: 10040}, pos: position{line: 346, col: 12, offset: 10565},
val: "[0-9]", val: "[0-9]",
ranges: []rune{'0', '9'}, ranges: []rune{'0', '9'},
ignoreCase: false, ignoreCase: false,
@ -1976,29 +2018,29 @@ var g = &grammar{
}, },
{ {
name: "StringCharacter", name: "StringCharacter",
pos: position{line: 332, col: 1, offset: 10092}, pos: position{line: 350, col: 1, offset: 10617},
expr: &choiceExpr{ expr: &choiceExpr{
pos: position{line: 332, col: 20, offset: 10111}, pos: position{line: 350, col: 20, offset: 10636},
alternatives: []any{ alternatives: []any{
&actionExpr{ &actionExpr{
pos: position{line: 332, col: 20, offset: 10111}, pos: position{line: 350, col: 20, offset: 10636},
run: (*parser).callonStringCharacter2, run: (*parser).callonStringCharacter2,
expr: &seqExpr{ expr: &seqExpr{
pos: position{line: 332, col: 20, offset: 10111}, pos: position{line: 350, col: 20, offset: 10636},
exprs: []any{ exprs: []any{
&notExpr{ &notExpr{
pos: position{line: 332, col: 20, offset: 10111}, pos: position{line: 350, col: 20, offset: 10636},
expr: &choiceExpr{ expr: &choiceExpr{
pos: position{line: 332, col: 22, offset: 10113}, pos: position{line: 350, col: 22, offset: 10638},
alternatives: []any{ alternatives: []any{
&litMatcher{ &litMatcher{
pos: position{line: 332, col: 22, offset: 10113}, pos: position{line: 350, col: 22, offset: 10638},
val: "\"", val: "\"",
ignoreCase: false, ignoreCase: false,
want: "\"\\\"\"", want: "\"\\\"\"",
}, },
&litMatcher{ &litMatcher{
pos: position{line: 332, col: 28, offset: 10119}, pos: position{line: 350, col: 28, offset: 10644},
val: "\\", val: "\\",
ignoreCase: false, ignoreCase: false,
want: "\"\\\\\"", want: "\"\\\\\"",
@ -2007,28 +2049,28 @@ var g = &grammar{
}, },
}, },
&anyMatcher{ &anyMatcher{
line: 332, col: 34, offset: 10125, line: 350, col: 34, offset: 10650,
}, },
}, },
}, },
}, },
&actionExpr{ &actionExpr{
pos: position{line: 333, col: 5, offset: 10162}, pos: position{line: 351, col: 5, offset: 10687},
run: (*parser).callonStringCharacter9, run: (*parser).callonStringCharacter9,
expr: &seqExpr{ expr: &seqExpr{
pos: position{line: 333, col: 5, offset: 10162}, pos: position{line: 351, col: 5, offset: 10687},
exprs: []any{ exprs: []any{
&litMatcher{ &litMatcher{
pos: position{line: 333, col: 5, offset: 10162}, pos: position{line: 351, col: 5, offset: 10687},
val: "\\", val: "\\",
ignoreCase: false, ignoreCase: false,
want: "\"\\\\\"", want: "\"\\\\\"",
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 333, col: 10, offset: 10167}, pos: position{line: 351, col: 10, offset: 10692},
label: "seq", label: "seq",
expr: &ruleRefExpr{ expr: &ruleRefExpr{
pos: position{line: 333, col: 14, offset: 10171}, pos: position{line: 351, col: 14, offset: 10696},
name: "EscapeSequenceCharacter", name: "EscapeSequenceCharacter",
}, },
}, },
@ -2040,85 +2082,85 @@ var g = &grammar{
}, },
{ {
name: "EscapeSequenceCharacter", name: "EscapeSequenceCharacter",
pos: position{line: 335, col: 1, offset: 10216}, pos: position{line: 353, col: 1, offset: 10741},
expr: &labeledExpr{ expr: &labeledExpr{
pos: position{line: 335, col: 28, offset: 10243}, pos: position{line: 353, col: 28, offset: 10768},
label: "char", label: "char",
expr: &ruleRefExpr{ expr: &ruleRefExpr{
pos: position{line: 335, col: 33, offset: 10248}, pos: position{line: 353, col: 33, offset: 10773},
name: "EscapeCharacter", name: "EscapeCharacter",
}, },
}, },
}, },
{ {
name: "EscapeCharacter", name: "EscapeCharacter",
pos: position{line: 337, col: 1, offset: 10265}, pos: position{line: 355, col: 1, offset: 10790},
expr: &choiceExpr{ expr: &choiceExpr{
pos: position{line: 337, col: 20, offset: 10284}, pos: position{line: 355, col: 20, offset: 10809},
alternatives: []any{ alternatives: []any{
&litMatcher{ &litMatcher{
pos: position{line: 337, col: 20, offset: 10284}, pos: position{line: 355, col: 20, offset: 10809},
val: "'", val: "'",
ignoreCase: false, ignoreCase: false,
want: "\"'\"", want: "\"'\"",
}, },
&litMatcher{ &litMatcher{
pos: position{line: 338, col: 5, offset: 10292}, pos: position{line: 356, col: 5, offset: 10817},
val: "\"", val: "\"",
ignoreCase: false, ignoreCase: false,
want: "\"\\\"\"", want: "\"\\\"\"",
}, },
&litMatcher{ &litMatcher{
pos: position{line: 339, col: 5, offset: 10300}, pos: position{line: 357, col: 5, offset: 10825},
val: "\\", val: "\\",
ignoreCase: false, ignoreCase: false,
want: "\"\\\\\"", want: "\"\\\\\"",
}, },
&actionExpr{ &actionExpr{
pos: position{line: 340, col: 5, offset: 10309}, pos: position{line: 358, col: 5, offset: 10834},
run: (*parser).callonEscapeCharacter5, run: (*parser).callonEscapeCharacter5,
expr: &litMatcher{ expr: &litMatcher{
pos: position{line: 340, col: 5, offset: 10309}, pos: position{line: 358, col: 5, offset: 10834},
val: "b", val: "b",
ignoreCase: false, ignoreCase: false,
want: "\"b\"", want: "\"b\"",
}, },
}, },
&actionExpr{ &actionExpr{
pos: position{line: 341, col: 5, offset: 10338}, pos: position{line: 359, col: 5, offset: 10863},
run: (*parser).callonEscapeCharacter7, run: (*parser).callonEscapeCharacter7,
expr: &litMatcher{ expr: &litMatcher{
pos: position{line: 341, col: 5, offset: 10338}, pos: position{line: 359, col: 5, offset: 10863},
val: "f", val: "f",
ignoreCase: false, ignoreCase: false,
want: "\"f\"", want: "\"f\"",
}, },
}, },
&actionExpr{ &actionExpr{
pos: position{line: 342, col: 5, offset: 10367}, pos: position{line: 360, col: 5, offset: 10892},
run: (*parser).callonEscapeCharacter9, run: (*parser).callonEscapeCharacter9,
expr: &litMatcher{ expr: &litMatcher{
pos: position{line: 342, col: 5, offset: 10367}, pos: position{line: 360, col: 5, offset: 10892},
val: "n", val: "n",
ignoreCase: false, ignoreCase: false,
want: "\"n\"", want: "\"n\"",
}, },
}, },
&actionExpr{ &actionExpr{
pos: position{line: 343, col: 5, offset: 10396}, pos: position{line: 361, col: 5, offset: 10921},
run: (*parser).callonEscapeCharacter11, run: (*parser).callonEscapeCharacter11,
expr: &litMatcher{ expr: &litMatcher{
pos: position{line: 343, col: 5, offset: 10396}, pos: position{line: 361, col: 5, offset: 10921},
val: "r", val: "r",
ignoreCase: false, ignoreCase: false,
want: "\"r\"", want: "\"r\"",
}, },
}, },
&actionExpr{ &actionExpr{
pos: position{line: 344, col: 5, offset: 10425}, pos: position{line: 362, col: 5, offset: 10950},
run: (*parser).callonEscapeCharacter13, run: (*parser).callonEscapeCharacter13,
expr: &litMatcher{ expr: &litMatcher{
pos: position{line: 344, col: 5, offset: 10425}, pos: position{line: 362, col: 5, offset: 10950},
val: "t", val: "t",
ignoreCase: false, ignoreCase: false,
want: "\"t\"", want: "\"t\"",
@ -2129,25 +2171,25 @@ var g = &grammar{
}, },
{ {
name: "non_escape_character", name: "non_escape_character",
pos: position{line: 346, col: 1, offset: 10451}, pos: position{line: 364, col: 1, offset: 10976},
expr: &actionExpr{ expr: &actionExpr{
pos: position{line: 346, col: 25, offset: 10475}, pos: position{line: 364, col: 25, offset: 11000},
run: (*parser).callonnon_escape_character1, run: (*parser).callonnon_escape_character1,
expr: &seqExpr{ expr: &seqExpr{
pos: position{line: 346, col: 25, offset: 10475}, pos: position{line: 364, col: 25, offset: 11000},
exprs: []any{ exprs: []any{
&notExpr{ &notExpr{
pos: position{line: 346, col: 25, offset: 10475}, pos: position{line: 364, col: 25, offset: 11000},
expr: &ruleRefExpr{ expr: &ruleRefExpr{
pos: position{line: 346, col: 27, offset: 10477}, pos: position{line: 364, col: 27, offset: 11002},
name: "escape_character", name: "escape_character",
}, },
}, },
&labeledExpr{ &labeledExpr{
pos: position{line: 346, col: 45, offset: 10495}, pos: position{line: 364, col: 45, offset: 11020},
label: "char", label: "char",
expr: &anyMatcher{ expr: &anyMatcher{
line: 346, col: 50, offset: 10500, line: 364, col: 50, offset: 11025,
}, },
}, },
}, },
@ -2156,11 +2198,11 @@ var g = &grammar{
}, },
{ {
name: "ws", name: "ws",
pos: position{line: 349, col: 1, offset: 10539}, pos: position{line: 367, col: 1, offset: 11064},
expr: &zeroOrMoreExpr{ expr: &zeroOrMoreExpr{
pos: position{line: 349, col: 7, offset: 10545}, pos: position{line: 367, col: 7, offset: 11070},
expr: &charClassMatcher{ expr: &charClassMatcher{
pos: position{line: 349, col: 7, offset: 10545}, pos: position{line: 367, col: 7, offset: 11070},
val: "[ \\t\\n\\r]", val: "[ \\t\\n\\r]",
chars: []rune{' ', '\t', '\n', '\r'}, chars: []rune{' ', '\t', '\n', '\r'},
ignoreCase: false, ignoreCase: false,
@ -2170,11 +2212,11 @@ var g = &grammar{
}, },
{ {
name: "EOF", name: "EOF",
pos: position{line: 351, col: 1, offset: 10557}, pos: position{line: 369, col: 1, offset: 11082},
expr: &notExpr{ expr: &notExpr{
pos: position{line: 351, col: 8, offset: 10564}, pos: position{line: 369, col: 8, offset: 11089},
expr: &anyMatcher{ expr: &anyMatcher{
line: 351, col: 9, offset: 10565, line: 369, col: 9, offset: 11090,
}, },
}, },
}, },
@ -2654,24 +2696,36 @@ func (p *parser) callonConcatExpression1() (any, error) {
return p.cur.onConcatExpression1(stack["ex1"], stack["others"]) return p.cur.onConcatExpression1(stack["ex1"], stack["others"])
} }
func (c *current) onContainsExpression17(boolean any) (any, error) { func (c *current) onThreeArgumentStringFunctionExpression18(boolean any) (any, error) {
return boolean, nil return boolean, nil
} }
func (p *parser) callonContainsExpression17() (any, error) { func (p *parser) callonThreeArgumentStringFunctionExpression18() (any, error) {
stack := p.vstack[len(p.vstack)-1] stack := p.vstack[len(p.vstack)-1]
_ = stack _ = stack
return p.cur.onContainsExpression17(stack["boolean"]) return p.cur.onThreeArgumentStringFunctionExpression18(stack["boolean"])
} }
func (c *current) onContainsExpression1(ex1, ex2, ignoreCase any) (any, error) { func (c *current) onThreeArgumentStringFunctionExpression1(function, ex1, ex2, ignoreCase any) (any, error) {
return parsers.FunctionCall{Type: parsers.FunctionCallContains, Arguments: []interface{}{ex1, ex2, ignoreCase}}, nil var functionType parsers.FunctionCallType
lowerFunction := strings.ToUpper(function.(string))
switch lowerFunction {
case "CONTAINS":
functionType = parsers.FunctionCallContains
case "ENDSWITH":
functionType = parsers.FunctionCallEndsWith
case "STARTSWITH":
functionType = parsers.FunctionCallStartsWith
} }
func (p *parser) callonContainsExpression1() (any, error) { return parsers.FunctionCall{Type: functionType, Arguments: []interface{}{ex1, ex2, ignoreCase}}, nil
}
func (p *parser) callonThreeArgumentStringFunctionExpression1() (any, error) {
stack := p.vstack[len(p.vstack)-1] stack := p.vstack[len(p.vstack)-1]
_ = stack _ = stack
return p.cur.onContainsExpression1(stack["ex1"], stack["ex2"], stack["ignoreCase"]) return p.cur.onThreeArgumentStringFunctionExpression1(stack["function"], stack["ex1"], stack["ex2"], stack["ignoreCase"])
} }
func (c *current) onIsDefined1(ex any) (any, error) { func (c *current) onIsDefined1(ex any) (any, error) {

View File

@ -306,7 +306,11 @@ BooleanLiteral <- ("true"i / "false"i) {
return parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: boolValue}, nil return parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: boolValue}, nil
} }
FunctionCall <- StringEqualsExpression / ConcatExpression / ContainsExpression / IsDefined FunctionCall <- StringFunctions / IsDefined
StringFunctions <- StringEqualsExpression
/ ConcatExpression
/ ThreeArgumentStringFunctionExpression
StringEqualsExpression <- StringEquals ws "(" ws ex1:SelectItem ws "," ws ex2:SelectItem ws ignoreCase:("," ws boolean:SelectItem { return boolean, nil })? ")" { StringEqualsExpression <- StringEquals ws "(" ws ex1:SelectItem ws "," ws ex2:SelectItem ws ignoreCase:("," ws boolean:SelectItem { return boolean, nil })? ")" {
return parsers.FunctionCall{Type: parsers.FunctionCallStringEquals, Arguments: []interface{}{ex1, ex2, ignoreCase}}, nil return parsers.FunctionCall{Type: parsers.FunctionCallStringEquals, Arguments: []interface{}{ex1, ex2, ignoreCase}}, nil
@ -317,10 +321,24 @@ ConcatExpression <- "CONCAT"i ws "(" ws ex1:SelectItem others:(ws "," ws ex:Sele
return parsers.FunctionCall{Type: parsers.FunctionCallConcat, Arguments: arguments}, nil return parsers.FunctionCall{Type: parsers.FunctionCallConcat, Arguments: arguments}, nil
} }
ContainsExpression <- "CONTAINS"i ws "(" ws ex1:SelectItem ws "," ws ex2:SelectItem ws ignoreCase:("," ws boolean:SelectItem { return boolean, nil })? ")" { ThreeArgumentStringFunctionExpression <- function:ThreeArgumentStringFunction ws "(" ws ex1:SelectItem ws "," ws ex2:SelectItem ws ignoreCase:("," ws boolean:SelectItem { return boolean, nil })? ")" {
return parsers.FunctionCall{Type: parsers.FunctionCallContains, Arguments: []interface{}{ex1, ex2, ignoreCase}}, nil var functionType parsers.FunctionCallType
lowerFunction := strings.ToUpper(function.(string))
switch lowerFunction {
case "CONTAINS":
functionType = parsers.FunctionCallContains
case "ENDSWITH":
functionType = parsers.FunctionCallEndsWith
case "STARTSWITH":
functionType = parsers.FunctionCallStartsWith
} }
return parsers.FunctionCall{Type: functionType, Arguments: []interface{}{ex1, ex2, ignoreCase}}, nil
}
ThreeArgumentStringFunction <- "CONTAINS"i / "ENDSWITH"i / "STARTSWITH"i
IsDefined <- "IS_DEFINED"i ws "(" ws ex:SelectItem ws ")" { IsDefined <- "IS_DEFINED"i ws "(" ws ex:SelectItem ws ")" {
return parsers.FunctionCall{Type: parsers.FunctionCallIsDefined, Arguments: []interface{}{ex}}, nil return parsers.FunctionCall{Type: parsers.FunctionCallIsDefined, Arguments: []interface{}{ex}}, nil
} }

View File

@ -150,4 +150,80 @@ func Test_Execute_StringFunctions(t *testing.T) {
}, },
) )
}) })
t.Run("Should parse function ENDSWITH()", func(t *testing.T) {
testQueryParse(
t,
`SELECT ENDSWITH(c.id, "123", true) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallEndsWith,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "123",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: true,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function STARTSWITH()", func(t *testing.T) {
testQueryParse(
t,
`SELECT STARTSWITH(c.id, "123", true) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallStartsWith,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "123",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: true,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
} }

View File

@ -180,6 +180,10 @@ func getFieldValue(field parsers.SelectItem, queryParameters map[string]interfac
return strings_StringEquals(typedValue.Arguments, queryParameters, row) return strings_StringEquals(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallContains: case parsers.FunctionCallContains:
return strings_Contains(typedValue.Arguments, queryParameters, row) return strings_Contains(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallEndsWith:
return strings_EndsWith(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallStartsWith:
return strings_StartsWith(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallConcat: case parsers.FunctionCallConcat:
return strings_Concat(typedValue.Arguments, queryParameters, row) return strings_Concat(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallIsDefined: case parsers.FunctionCallIsDefined:

View File

@ -199,4 +199,100 @@ func Test_Execute_StringFunctions(t *testing.T) {
}, },
) )
}) })
t.Run("Should execute function ENDSWITH()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "ends",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallEndsWith,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "3",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: true,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "123", "ends": true},
map[string]interface{}{"id": "456", "ends": false},
map[string]interface{}{"id": "789", "ends": false},
},
)
})
t.Run("Should execute function STARTSWITH()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "starts",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallStartsWith,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "1",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: true,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "123", "starts": true},
map[string]interface{}{"id": "456", "starts": false},
map[string]interface{}{"id": "789", "starts": false},
},
)
})
} }

View File

@ -8,30 +8,9 @@ import (
) )
func strings_StringEquals(arguments []interface{}, queryParameters map[string]interface{}, row RowType) bool { func strings_StringEquals(arguments []interface{}, queryParameters map[string]interface{}, row RowType) bool {
ignoreCase := false str1 := parseString(arguments[0], queryParameters, row)
if len(arguments) > 2 && arguments[2] != nil { str2 := parseString(arguments[1], queryParameters, row)
ignoreCaseItem := arguments[2].(parsers.SelectItem) ignoreCase := getBoolFlag(arguments, queryParameters, row)
if value, ok := getFieldValue(ignoreCaseItem, queryParameters, row).(bool); ok {
ignoreCase = value
}
}
ex1Item := arguments[0].(parsers.SelectItem)
ex2Item := arguments[1].(parsers.SelectItem)
ex1 := getFieldValue(ex1Item, queryParameters, row)
ex2 := getFieldValue(ex2Item, queryParameters, row)
var ok bool
var str1 string
var str2 string
if str1, ok = ex1.(string); !ok {
fmt.Println("StringEquals got parameters of wrong type")
}
if str2, ok = ex2.(string); !ok {
fmt.Println("StringEquals got parameters of wrong type")
}
if ignoreCase { if ignoreCase {
return strings.EqualFold(str1, str2) return strings.EqualFold(str1, str2)
@ -41,30 +20,9 @@ func strings_StringEquals(arguments []interface{}, queryParameters map[string]in
} }
func strings_Contains(arguments []interface{}, queryParameters map[string]interface{}, row RowType) bool { func strings_Contains(arguments []interface{}, queryParameters map[string]interface{}, row RowType) bool {
ignoreCase := false str1 := parseString(arguments[0], queryParameters, row)
if len(arguments) > 2 && arguments[2] != nil { str2 := parseString(arguments[1], queryParameters, row)
ignoreCaseItem := arguments[2].(parsers.SelectItem) ignoreCase := getBoolFlag(arguments, queryParameters, row)
if value, ok := getFieldValue(ignoreCaseItem, queryParameters, row).(bool); ok {
ignoreCase = value
}
}
ex1Item := arguments[0].(parsers.SelectItem)
ex2Item := arguments[1].(parsers.SelectItem)
ex1 := getFieldValue(ex1Item, queryParameters, row)
ex2 := getFieldValue(ex2Item, queryParameters, row)
var ok bool
var str1 string
var str2 string
if str1, ok = ex1.(string); !ok {
fmt.Println("StringEquals got parameters of wrong type")
}
if str2, ok = ex2.(string); !ok {
fmt.Println("StringEquals got parameters of wrong type")
}
if ignoreCase { if ignoreCase {
str1 = strings.ToLower(str1) str1 = strings.ToLower(str1)
@ -74,6 +32,32 @@ func strings_Contains(arguments []interface{}, queryParameters map[string]interf
return strings.Contains(str1, str2) return strings.Contains(str1, str2)
} }
func strings_EndsWith(arguments []interface{}, queryParameters map[string]interface{}, row RowType) bool {
str1 := parseString(arguments[0], queryParameters, row)
str2 := parseString(arguments[1], queryParameters, row)
ignoreCase := getBoolFlag(arguments, queryParameters, row)
if ignoreCase {
str1 = strings.ToLower(str1)
str2 = strings.ToLower(str2)
}
return strings.HasSuffix(str1, str2)
}
func strings_StartsWith(arguments []interface{}, queryParameters map[string]interface{}, row RowType) bool {
str1 := parseString(arguments[0], queryParameters, row)
str2 := parseString(arguments[1], queryParameters, row)
ignoreCase := getBoolFlag(arguments, queryParameters, row)
if ignoreCase {
str1 = strings.ToLower(str1)
str2 = strings.ToLower(str2)
}
return strings.HasPrefix(str1, str2)
}
func strings_Concat(arguments []interface{}, queryParameters map[string]interface{}, row RowType) string { func strings_Concat(arguments []interface{}, queryParameters map[string]interface{}, row RowType) string {
result := "" result := ""
@ -87,6 +71,29 @@ func strings_Concat(arguments []interface{}, queryParameters map[string]interfac
return result return result
} }
func getBoolFlag(arguments []interface{}, queryParameters map[string]interface{}, row RowType) bool {
ignoreCase := false
if len(arguments) > 2 && arguments[2] != nil {
ignoreCaseItem := arguments[2].(parsers.SelectItem)
if value, ok := getFieldValue(ignoreCaseItem, queryParameters, row).(bool); ok {
ignoreCase = value
}
}
return ignoreCase
}
func parseString(argument interface{}, queryParameters map[string]interface{}, row RowType) string {
exItem := argument.(parsers.SelectItem)
ex := getFieldValue(exItem, queryParameters, row)
if str1, ok := ex.(string); ok {
fmt.Println("StringEquals got parameters of wrong type")
return str1
}
return ""
}
func convertToString(value interface{}) string { func convertToString(value interface{}) string {
switch v := value.(type) { switch v := value.(type) {
case string: case string: