Add ability to change alias of all projections in the ProjectionList.
Add ability to exclude list of columns from ProjectionList.
This commit is contained in:
parent
392ba63bc5
commit
5cbf4aac86
8 changed files with 377 additions and 31 deletions
|
|
@ -13,7 +13,12 @@ func newAlias(expression Expression, aliasName string) Projection {
|
|||
}
|
||||
|
||||
func (a *alias) fromImpl(subQuery SelectTable) Projection {
|
||||
column := NewColumnImpl(a.alias, "", nil)
|
||||
// if alias is in the form "table.column", we break it into two parts so that ProjectionList.As(newAlias) can
|
||||
// overwrite tableName with a new alias. This method is called only for exporting aliased custom columns.
|
||||
// Generated columns have default aliasing.
|
||||
tableName, columnName := extractTableAndColumnName(a.alias)
|
||||
|
||||
column := NewColumnImpl(columnName, tableName, nil)
|
||||
column.subQuery = subQuery
|
||||
|
||||
return &column
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package jet
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Expression is common interface for all expressions.
|
||||
// Can be Bool, Int, Float, String, Date, Time, Timez, Timestamp or Timestampz expressions.
|
||||
type Expression interface {
|
||||
|
|
@ -33,7 +35,8 @@ type ExpressionInterfaceImpl struct {
|
|||
}
|
||||
|
||||
func (e *ExpressionInterfaceImpl) fromImpl(subQuery SelectTable) Projection {
|
||||
return e.Parent
|
||||
panic(fmt.Sprintf("jet: can't export unaliased expression subQuery: %s, expression: %s",
|
||||
subQuery.Alias(), serializeToDefaultDebugString(e.Parent)))
|
||||
}
|
||||
|
||||
// IS_NULL tests expression whether it is a NULL value.
|
||||
|
|
|
|||
|
|
@ -16,36 +16,68 @@ func SerializeForProjection(projection Projection, statementType StatementType,
|
|||
// ProjectionList is a redefined type, so that ProjectionList can be used as a Projection.
|
||||
type ProjectionList []Projection
|
||||
|
||||
func (cl ProjectionList) fromImpl(subQuery SelectTable) Projection {
|
||||
func (pl ProjectionList) fromImpl(subQuery SelectTable) Projection {
|
||||
newProjectionList := ProjectionList{}
|
||||
|
||||
for _, projection := range cl {
|
||||
for _, projection := range pl {
|
||||
newProjectionList = append(newProjectionList, projection.fromImpl(subQuery))
|
||||
}
|
||||
|
||||
return newProjectionList
|
||||
}
|
||||
|
||||
func (cl ProjectionList) serializeForProjection(statement StatementType, out *SQLBuilder) {
|
||||
SerializeProjectionList(statement, cl, out)
|
||||
func (pl ProjectionList) serializeForProjection(statement StatementType, out *SQLBuilder) {
|
||||
SerializeProjectionList(statement, pl, out)
|
||||
}
|
||||
|
||||
// As is used to set aliases of the projection list. alias should be in the form 'name' or 'name.*'.
|
||||
// For instance: If projection list has a column 'Artist.Name', and alias is 'Musician.*', returned projection list will
|
||||
// have column wrapped in alias 'Musician.Name'.
|
||||
func (cl ProjectionList) As(alias string) ProjectionList {
|
||||
alias = strings.TrimRight(alias, ".*")
|
||||
// As will create new projection list where each column is wrapped with a new table alias.
|
||||
// tableAlias should be in the form 'name' or 'name.*'.
|
||||
// For instance: If projection list has a column 'Artist.Name', and tableAlias is 'Musician.*', returned projection list will
|
||||
// have a column wrapped in alias 'Musician.Name'.
|
||||
func (pl ProjectionList) As(tableAlias string) ProjectionList {
|
||||
tableAlias = strings.TrimRight(tableAlias, ".*")
|
||||
|
||||
newProjectionList := ProjectionList{}
|
||||
|
||||
for _, projection := range cl {
|
||||
for _, projection := range pl {
|
||||
switch p := projection.(type) {
|
||||
case ProjectionList:
|
||||
newProjectionList = append(newProjectionList, p.As(alias))
|
||||
newProjectionList = append(newProjectionList, p.As(tableAlias))
|
||||
case ColumnExpression:
|
||||
newProjectionList = append(newProjectionList, newAlias(p, alias+"."+p.Name()))
|
||||
newProjectionList = append(newProjectionList, newAlias(p, tableAlias+"."+p.Name()))
|
||||
case *alias:
|
||||
newAlias := *p
|
||||
_, columnName := extractTableAndColumnName(newAlias.alias)
|
||||
newAlias.alias = tableAlias + "." + columnName
|
||||
newProjectionList = append(newProjectionList, &newAlias)
|
||||
}
|
||||
}
|
||||
|
||||
return newProjectionList
|
||||
}
|
||||
|
||||
// Except will create new projection list in which columns contained in excluded column names are removed
|
||||
func (pl ProjectionList) Except(toExclude ...Column) ProjectionList {
|
||||
excludedColumnList := UnwidColumnList(toExclude)
|
||||
excludedColumnNames := map[string]bool{}
|
||||
|
||||
for _, excludedColumn := range excludedColumnList {
|
||||
excludedColumnNames[excludedColumn.Name()] = true
|
||||
}
|
||||
|
||||
var ret ProjectionList
|
||||
|
||||
for _, projection := range pl {
|
||||
switch p := projection.(type) {
|
||||
case ProjectionList:
|
||||
ret = append(ret, p.Except(toExclude...))
|
||||
case ColumnExpression:
|
||||
if excludedColumnNames[p.Name()] {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, p)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
|
|
|||
46
internal/jet/projection_test.go
Normal file
46
internal/jet/projection_test.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package jet
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestProjectionAs(t *testing.T) {
|
||||
projectionList := ProjectionList{
|
||||
table1Col3,
|
||||
SUM(table1ColInt).AS("sum"),
|
||||
SUM(table1ColInt).AS("table.sum"),
|
||||
ProjectionList{
|
||||
table1ColBool,
|
||||
AVG(table1ColInt).AS("avg"),
|
||||
AVG(table1ColInt).AS("t.avg"),
|
||||
},
|
||||
}
|
||||
|
||||
aliasedProjectionList := projectionList.As("new_alias.*")
|
||||
|
||||
assertProjectionSerialize(t, aliasedProjectionList,
|
||||
`table1.col3 AS "new_alias.col3",
|
||||
SUM(table1.col_int) AS "new_alias.sum",
|
||||
SUM(table1.col_int) AS "new_alias.sum",
|
||||
table1.col_bool AS "new_alias.col_bool",
|
||||
AVG(table1.col_int) AS "new_alias.avg",
|
||||
AVG(table1.col_int) AS "new_alias.avg"`)
|
||||
|
||||
subQueryProjections := projectionList.fromImpl(NewSelectTable(nil, "subQuery"))
|
||||
|
||||
assertProjectionSerialize(t, subQueryProjections,
|
||||
`"subQuery"."table1.col3" AS "table1.col3",
|
||||
"subQuery".sum AS "sum",
|
||||
"subQuery"."table.sum" AS "table.sum",
|
||||
"subQuery"."table1.col_bool" AS "table1.col_bool",
|
||||
"subQuery".avg AS "avg",
|
||||
"subQuery"."t.avg" AS "t.avg"`)
|
||||
|
||||
aliasedSubQueryProjectionList := subQueryProjections.(ProjectionList).As("subAlias")
|
||||
|
||||
assertProjectionSerialize(t, aliasedSubQueryProjectionList,
|
||||
`"subQuery"."table1.col3" AS "subAlias.col3",
|
||||
"subQuery".sum AS "subAlias.sum",
|
||||
"subQuery"."table.sum" AS "subAlias.sum",
|
||||
"subQuery"."table1.col_bool" AS "subAlias.col_bool",
|
||||
"subQuery".avg AS "subAlias.avg",
|
||||
"subQuery"."t.avg" AS "subAlias.avg"`)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package jet
|
|||
import (
|
||||
"github.com/go-jet/jet/v2/internal/utils"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SerializeClauseList func
|
||||
|
|
@ -244,3 +245,22 @@ func OptionalOrDefaultExpression(defaultExpression Expression, expression ...Exp
|
|||
|
||||
return defaultExpression
|
||||
}
|
||||
|
||||
func extractTableAndColumnName(alias string) (tableName string, columnName string) {
|
||||
parts := strings.Split(alias, ".")
|
||||
|
||||
if len(parts) >= 2 {
|
||||
tableName = parts[0]
|
||||
columnName = parts[1]
|
||||
} else {
|
||||
columnName = parts[0]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func serializeToDefaultDebugString(expr Serializer) string {
|
||||
out := SQLBuilder{Dialect: defaultDialect, Debug: true}
|
||||
expr.serialize(SelectStatementType, &out)
|
||||
return out.Buff.String()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ func PrintJson(v interface{}) {
|
|||
fmt.Println(string(jsonText))
|
||||
}
|
||||
|
||||
// ToJSON converts v into json string
|
||||
func ToJSON(v interface{}) string {
|
||||
jsonText, _ := json.MarshalIndent(v, "", "\t")
|
||||
return string(jsonText)
|
||||
}
|
||||
|
||||
// AssertJSON check if data json output is the same as expectedJSON
|
||||
func AssertJSON(t *testing.T, data interface{}, expectedJSON string) {
|
||||
jsonData, err := json.MarshalIndent(data, "", "\t")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue