mirror of
https://github.com/pikami/cosmium.git
synced 2025-12-19 17:00:37 +00:00
Add support for subqueries
This commit is contained in:
@@ -122,4 +122,25 @@ func Test_Parse(t *testing.T) {
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should parse IN selector", func(t *testing.T) {
|
||||
testQueryParse(
|
||||
t,
|
||||
`SELECT c.id FROM c IN c.tags`,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{
|
||||
Path: []string{"c", "id"},
|
||||
Type: parsers.SelectItemTypeField,
|
||||
},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "c",
|
||||
SelectItem: parsers.SelectItem{
|
||||
Path: []string{"c", "tags"},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,16 +4,19 @@ package nosql
|
||||
import "github.com/pikami/cosmium/parsers"
|
||||
|
||||
func makeSelectStmt(
|
||||
columns, table, joinItems,
|
||||
columns, fromClause, joinItems,
|
||||
whereClause interface{}, distinctClause interface{},
|
||||
count interface{}, groupByClause interface{}, orderList interface{},
|
||||
offsetClause interface{},
|
||||
) (parsers.SelectStmt, error) {
|
||||
selectStmt := parsers.SelectStmt{
|
||||
SelectItems: columns.([]parsers.SelectItem),
|
||||
Table: table.(parsers.Table),
|
||||
}
|
||||
|
||||
if fromTable, ok := fromClause.(parsers.Table); ok {
|
||||
selectStmt.Table = fromTable
|
||||
}
|
||||
|
||||
if joinItemsArray, ok := joinItems.([]interface{}); ok && len(joinItemsArray) > 0 {
|
||||
selectStmt.JoinItems = make([]parsers.JoinItem, len(joinItemsArray))
|
||||
for i, joinItem := range joinItemsArray {
|
||||
@@ -56,10 +59,18 @@ func makeSelectStmt(
|
||||
}
|
||||
|
||||
func makeJoin(table interface{}, column interface{}) (parsers.JoinItem, error) {
|
||||
return parsers.JoinItem{
|
||||
Table: table.(parsers.Table),
|
||||
SelectItem: column.(parsers.SelectItem),
|
||||
}, nil
|
||||
joinItem := parsers.JoinItem{}
|
||||
|
||||
if selectItem, isSelectItem := column.(parsers.SelectItem); isSelectItem {
|
||||
joinItem.SelectItem = selectItem
|
||||
joinItem.Table.Value = selectItem.Alias
|
||||
}
|
||||
|
||||
if tableTyped, isTable := table.(parsers.Table); isTable {
|
||||
joinItem.Table = tableTyped
|
||||
}
|
||||
|
||||
return joinItem, nil
|
||||
}
|
||||
|
||||
func makeSelectItem(name interface{}, path interface{}, selectItemType parsers.SelectItemType) (parsers.SelectItem, error) {
|
||||
@@ -177,13 +188,13 @@ SelectStmt <- Select ws
|
||||
distinctClause:DistinctClause? ws
|
||||
topClause:TopClause? ws
|
||||
columns:Selection ws
|
||||
From ws table:TableName ws
|
||||
joinClauses:JoinClause* ws
|
||||
fromClause:FromClause? ws
|
||||
joinClauses:(ws join:JoinClause { return join, nil })* ws
|
||||
whereClause:(ws Where ws condition:Condition { return condition, nil })?
|
||||
groupByClause:(ws GroupBy ws columns:ColumnList { return columns, nil })?
|
||||
orderByClause:OrderByClause?
|
||||
offsetClause:OffsetClause? {
|
||||
return makeSelectStmt(columns, table, joinClauses, whereClause,
|
||||
orderByClause:(ws order:OrderByClause { return order, nil })?
|
||||
offsetClause:(ws offset:OffsetClause { return offset, nil })? {
|
||||
return makeSelectStmt(columns, fromClause, joinClauses, whereClause,
|
||||
distinctClause, topClause, groupByClause, orderByClause, offsetClause)
|
||||
}
|
||||
|
||||
@@ -193,8 +204,49 @@ TopClause <- Top ws count:Integer {
|
||||
return count, nil
|
||||
}
|
||||
|
||||
FromClause <- From ws table:TableName selectItem:(ws "IN"i ws column:SelectItem { return column, nil })? {
|
||||
tableTyped := table.(parsers.Table)
|
||||
|
||||
if selectItem != nil {
|
||||
tableTyped.SelectItem = selectItem.(parsers.SelectItem)
|
||||
}
|
||||
|
||||
return tableTyped, nil
|
||||
} / From ws subQuery:SubQuerySelectItem {
|
||||
subQueryTyped := subQuery.(parsers.SelectItem)
|
||||
table := parsers.Table{
|
||||
Value: subQueryTyped.Alias,
|
||||
SelectItem: subQueryTyped,
|
||||
}
|
||||
return table, nil
|
||||
}
|
||||
|
||||
SubQuery <- exists:(exists:Exists ws { return exists, nil })? "(" ws selectStmt:SelectStmt ws ")" {
|
||||
if selectStatement, isGoodValue := selectStmt.(parsers.SelectStmt); isGoodValue {
|
||||
selectStatement.Exists = exists != nil
|
||||
return selectStatement, nil
|
||||
}
|
||||
|
||||
return selectStmt, nil
|
||||
}
|
||||
|
||||
SubQuerySelectItem <- subQuery:SubQuery asClause:(ws alias:AsClause { return alias, nil })? {
|
||||
selectItem := parsers.SelectItem{
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: subQuery,
|
||||
}
|
||||
|
||||
if tableName, isString := asClause.(string); isString {
|
||||
selectItem.Alias = tableName
|
||||
}
|
||||
|
||||
return selectItem, nil
|
||||
}
|
||||
|
||||
JoinClause <- Join ws table:TableName ws "IN"i ws column:SelectItem {
|
||||
return makeJoin(table, column)
|
||||
} / Join ws subQuery:SubQuerySelectItem {
|
||||
return makeJoin(nil, subQuery)
|
||||
}
|
||||
|
||||
OffsetClause <- "OFFSET"i ws offset:IntegerLiteral ws "LIMIT"i ws limit:IntegerLiteral {
|
||||
@@ -241,7 +293,7 @@ SelectProperty <- name:Identifier path:(DotFieldAccess / ArrayFieldAccess)* {
|
||||
return makeSelectItem(name, path, parsers.SelectItemTypeField)
|
||||
}
|
||||
|
||||
SelectItem <- selectItem:(Literal / FunctionCall / SelectArray / SelectObject / SelectProperty) asClause:AsClause? {
|
||||
SelectItem <- selectItem:(SubQuerySelectItem / Literal / FunctionCall / SelectArray / SelectObject / SelectProperty) asClause:AsClause? {
|
||||
var itemResult parsers.SelectItem
|
||||
switch typedValue := selectItem.(type) {
|
||||
case parsers.SelectItem:
|
||||
@@ -322,11 +374,13 @@ From <- "FROM"i
|
||||
|
||||
Join <- "JOIN"i
|
||||
|
||||
Exists <- "EXISTS"i
|
||||
|
||||
Where <- "WHERE"i
|
||||
|
||||
And <- "AND"i
|
||||
|
||||
Or <- "OR"i
|
||||
Or <- "OR"i wss
|
||||
|
||||
GroupBy <- "GROUP"i ws "BY"i
|
||||
|
||||
@@ -677,4 +731,6 @@ non_escape_character <- !(escape_character) char:.
|
||||
|
||||
ws <- [ \t\n\r]*
|
||||
|
||||
wss <- [ \t\n\r]+
|
||||
|
||||
EOF <- !.
|
||||
|
||||
125
parsers/nosql/subquery_test.go
Normal file
125
parsers/nosql/subquery_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package nosql_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pikami/cosmium/parsers"
|
||||
)
|
||||
|
||||
func Test_Parse_SubQuery(t *testing.T) {
|
||||
|
||||
t.Run("Should parse FROM subquery", func(t *testing.T) {
|
||||
testQueryParse(
|
||||
t,
|
||||
`SELECT c.id FROM (SELECT VALUE cc["info"] FROM cc) AS c`,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"c", "id"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "c",
|
||||
SelectItem: parsers.SelectItem{
|
||||
Alias: "c",
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: parsers.SelectStmt{
|
||||
Table: parsers.Table{Value: "cc"},
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"cc", "info"}, IsTopLevel: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should parse JOIN subquery", func(t *testing.T) {
|
||||
testQueryParse(
|
||||
t,
|
||||
`SELECT c.id, cc.name FROM c JOIN (SELECT tag.name FROM tag IN c.tags) AS cc`,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"c", "id"}},
|
||||
{Path: []string{"cc", "name"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "c",
|
||||
},
|
||||
JoinItems: []parsers.JoinItem{
|
||||
{
|
||||
Table: parsers.Table{
|
||||
Value: "cc",
|
||||
},
|
||||
SelectItem: parsers.SelectItem{
|
||||
Alias: "cc",
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"tag", "name"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "tag",
|
||||
SelectItem: parsers.SelectItem{
|
||||
Path: []string{"c", "tags"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should parse JOIN EXISTS subquery", func(t *testing.T) {
|
||||
testQueryParse(
|
||||
t,
|
||||
`SELECT c.id
|
||||
FROM c
|
||||
JOIN (
|
||||
SELECT VALUE EXISTS(SELECT tag.name FROM tag IN c.tags)
|
||||
) AS hasTags
|
||||
WHERE hasTags`,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"c", "id"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "c",
|
||||
},
|
||||
JoinItems: []parsers.JoinItem{
|
||||
{
|
||||
Table: parsers.Table{Value: "hasTags"},
|
||||
SelectItem: parsers.SelectItem{
|
||||
Alias: "hasTags",
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{
|
||||
IsTopLevel: true,
|
||||
Type: parsers.SelectItemTypeSubQuery,
|
||||
Value: parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"tag", "name"}},
|
||||
},
|
||||
Table: parsers.Table{
|
||||
Value: "tag",
|
||||
SelectItem: parsers.SelectItem{
|
||||
Path: []string{"c", "tags"},
|
||||
},
|
||||
},
|
||||
Exists: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Filters: parsers.SelectItem{
|
||||
Path: []string{"hasTags"},
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user