Implement IIF function; Fix empty object select

This commit is contained in:
Pijus Kamandulis 2025-05-14 18:48:20 +03:00
parent 30195fae96
commit 7e0c10479b
10 changed files with 1865 additions and 1581 deletions

View File

@ -79,7 +79,7 @@ Cosmium strives to support the core features of Cosmos DB, including:
| Function | Implemented | | Function | Implemented |
| -------- | ----------- | | -------- | ----------- |
| IIF | No | | IIF | Yes |
### Date and time Functions ### Date and time Functions

View File

@ -134,6 +134,8 @@ const (
FunctionCallSetIntersect FunctionCallType = "SetIntersect" FunctionCallSetIntersect FunctionCallType = "SetIntersect"
FunctionCallSetUnion FunctionCallType = "SetUnion" FunctionCallSetUnion FunctionCallType = "SetUnion"
FunctionCallIif FunctionCallType = "Iif"
FunctionCallMathAbs FunctionCallType = "MathAbs" FunctionCallMathAbs FunctionCallType = "MathAbs"
FunctionCallMathAcos FunctionCallType = "MathAcos" FunctionCallMathAcos FunctionCallType = "MathAcos"
FunctionCallMathAsin FunctionCallType = "MathAsin" FunctionCallMathAsin FunctionCallType = "MathAsin"

View File

@ -163,4 +163,27 @@ func Test_Parse(t *testing.T) {
}, },
) )
}) })
t.Run("Should parse IIF function", func(t *testing.T) {
testQueryParse(
t,
`SELECT IIF(true, c.pk, c.id) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallIif,
Arguments: []interface{}{
testutils.SelectItem_Constant_Bool(true),
testutils.SelectItem_Path("c", "pk"),
testutils.SelectItem_Path("c", "id"),
},
},
},
},
Table: parsers.Table{SelectItem: testutils.SelectItem_Path("c")},
},
)
})
} }

File diff suppressed because it is too large Load Diff

View File

@ -289,6 +289,11 @@ SelectArray <- "[" ws columns:ColumnList ws "]" {
SelectObject <- "{" ws field:SelectObjectField ws other_fields:(ws "," ws coll:SelectObjectField {return coll, nil })* ws "}" { SelectObject <- "{" ws field:SelectObjectField ws other_fields:(ws "," ws coll:SelectObjectField {return coll, nil })* ws "}" {
return makeSelectObject(field, other_fields) return makeSelectObject(field, other_fields)
} / "{" ws "}" {
return parsers.SelectItem{
SelectItems: []parsers.SelectItem{},
Type: parsers.SelectItemTypeObject,
}, nil
} }
SelectObjectField <- name:(Identifier / "\"" key:Identifier "\"" { return key, nil }) ws ":" ws selectItem:SelectItem { SelectObjectField <- name:(Identifier / "\"" key:Identifier "\"" { return key, nil }) ws ":" ws selectItem:SelectItem {
@ -442,6 +447,7 @@ BooleanLiteral <- ("true"i / "false"i) {
FunctionCall <- StringFunctions FunctionCall <- StringFunctions
/ TypeCheckingFunctions / TypeCheckingFunctions
/ ArrayFunctions / ArrayFunctions
/ ConditionalFunctions
/ InFunction / InFunction
/ AggregateFunctions / AggregateFunctions
/ MathFunctions / MathFunctions
@ -489,6 +495,8 @@ ArrayFunctions <- ArrayConcatExpression
/ SetIntersectExpression / SetIntersectExpression
/ SetUnionExpression / SetUnionExpression
ConditionalFunctions <- IifExpression
MathFunctions <- MathAbsExpression MathFunctions <- MathAbsExpression
/ MathAcosExpression / MathAcosExpression
/ MathAsinExpression / MathAsinExpression
@ -681,6 +689,10 @@ SetUnionExpression <- "SetUnion"i ws "(" ws set1:SelectItem ws "," ws set2:Selec
return createFunctionCall(parsers.FunctionCallSetUnion, []interface{}{set1, set2}) return createFunctionCall(parsers.FunctionCallSetUnion, []interface{}{set1, set2})
} }
IifExpression <- "IIF"i ws "(" ws condition:SelectItem ws "," ws trueValue:SelectItem ws "," ws falseValue:SelectItem ws ")" {
return createFunctionCall(parsers.FunctionCallIif, []interface{}{condition, trueValue, falseValue})
}
MathAbsExpression <- "ABS"i ws "(" ws ex:SelectItem ws ")" { return createFunctionCall(parsers.FunctionCallMathAbs, []interface{}{ex}) } MathAbsExpression <- "ABS"i ws "(" ws ex:SelectItem ws ")" { return createFunctionCall(parsers.FunctionCallMathAbs, []interface{}{ex}) }
MathAcosExpression <- "ACOS"i ws "(" ws ex:SelectItem ws ")" { return createFunctionCall(parsers.FunctionCallMathAcos, []interface{}{ex}) } MathAcosExpression <- "ACOS"i ws "(" ws ex:SelectItem ws ")" { return createFunctionCall(parsers.FunctionCallMathAcos, []interface{}{ex}) }
MathAsinExpression <- "ASIN"i ws "(" ws ex:SelectItem ws ")" { return createFunctionCall(parsers.FunctionCallMathAsin, []interface{}{ex}) } MathAsinExpression <- "ASIN"i ws "(" ws ex:SelectItem ws ")" { return createFunctionCall(parsers.FunctionCallMathAsin, []interface{}{ex}) }

View File

@ -178,4 +178,21 @@ func Test_Parse_Select(t *testing.T) {
}, },
) )
}) })
t.Run("Should parse SELECT empty object", func(t *testing.T) {
testQueryParse(
t,
`SELECT {} AS obj FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Alias: "obj",
Type: parsers.SelectItemTypeObject,
SelectItems: []parsers.SelectItem{},
},
},
Table: parsers.Table{SelectItem: testutils.SelectItem_Path("c")},
},
)
})
} }

View File

@ -209,6 +209,9 @@ func (r rowContext) selectItem_SelectItemTypeFunctionCall(functionCall parsers.F
case parsers.FunctionCallSetUnion: case parsers.FunctionCallSetUnion:
return r.set_Union(functionCall.Arguments) return r.set_Union(functionCall.Arguments)
case parsers.FunctionCallIif:
return r.misc_Iif(functionCall.Arguments)
case parsers.FunctionCallMathAbs: case parsers.FunctionCallMathAbs:
return r.math_Abs(functionCall.Arguments) return r.math_Abs(functionCall.Arguments)
case parsers.FunctionCallMathAcos: case parsers.FunctionCallMathAcos:

View File

@ -16,3 +16,16 @@ func (r rowContext) misc_In(arguments []interface{}) bool {
return false return false
} }
func (r rowContext) misc_Iif(arguments []interface{}) interface{} {
if len(arguments) != 3 {
return nil
}
condition := r.resolveSelectItem(arguments[0].(parsers.SelectItem))
if condition != nil && condition == true {
return r.resolveSelectItem(arguments[1].(parsers.SelectItem))
}
return r.resolveSelectItem(arguments[2].(parsers.SelectItem))
}

View File

@ -210,4 +210,35 @@ func Test_Execute(t *testing.T) {
}, },
) )
}) })
t.Run("Should execute function IIF()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
testutils.SelectItem_Path("c", "id"),
{
Alias: "coolness",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallIif,
Arguments: []interface{}{
testutils.SelectItem_Path("c", "isCool"),
testutils.SelectItem_Constant_String("real cool"),
testutils.SelectItem_Constant_String("not cool"),
},
},
},
},
Table: parsers.Table{SelectItem: testutils.SelectItem_Path("c")},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "12345", "coolness": "not cool"},
map[string]interface{}{"id": "67890", "coolness": "real cool"},
map[string]interface{}{"id": "456", "coolness": "real cool"},
map[string]interface{}{"id": "123", "coolness": "real cool"},
},
)
})
} }

View File

@ -205,4 +205,27 @@ func Test_Execute_Select(t *testing.T) {
}, },
) )
}) })
t.Run("Should execute SELECT empty object", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Alias: "obj",
Type: parsers.SelectItemTypeObject,
SelectItems: []parsers.SelectItem{},
},
},
Table: parsers.Table{SelectItem: testutils.SelectItem_Path("c")},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"obj": map[string]interface{}{}},
map[string]interface{}{"obj": map[string]interface{}{}},
map[string]interface{}{"obj": map[string]interface{}{}},
map[string]interface{}{"obj": map[string]interface{}{}},
},
)
})
} }