grafana/pkg/services/sqlstore/searchstore/builder.go
Ezequiel Victorero 42d75ac737
Dashboards: Add feature restore dashboards backend (#83131)
Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>
2024-05-16 14:36:26 -03:00

196 lines
4.6 KiB
Go

package searchstore
import (
"bytes"
"fmt"
"strings"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/search/model"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
)
// Builder defaults to returning a SQL query to get a list of all dashboards
// in default order, but can be modified by applying filters.
type Builder struct {
// List of FilterWhere/FilterGroupBy/FilterOrderBy/FilterLeftJoin
// to modify the query.
Filters []any
Dialect migrator.Dialect
Features featuremgmt.FeatureToggles
params []any
sql bytes.Buffer
}
// ToSQL builds the SQL query and returns it as a string, together with the SQL parameters.
func (b *Builder) ToSQL(limit, page int64) (string, []any) {
b.params = make([]any, 0)
b.sql = bytes.Buffer{}
b.buildSelect()
b.sql.WriteString("( ")
orderQuery := b.applyFilters()
b.sql.WriteString(b.Dialect.LimitOffset(limit, (page-1)*limit) + `) AS ids
INNER JOIN dashboard ON ids.id = dashboard.id`)
b.sql.WriteString("\n")
if b.Features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
// covered by UQE_folder_org_id_uid
b.sql.WriteString(
`LEFT OUTER JOIN folder ON folder.uid = dashboard.folder_uid AND folder.org_id = dashboard.org_id`)
} else {
b.sql.WriteString(`
LEFT OUTER JOIN dashboard AS folder ON folder.id = dashboard.folder_id`)
}
b.sql.WriteString(`
LEFT OUTER JOIN dashboard_tag ON dashboard.id = dashboard_tag.dashboard_id`)
b.sql.WriteString("\n")
b.sql.WriteString(orderQuery)
return b.sql.String(), b.params
}
func (b *Builder) buildSelect() {
var recQuery string
var recQueryParams []any
b.sql.WriteString(
`SELECT
dashboard.id,
dashboard.uid,
dashboard.title,
dashboard.slug,
dashboard_tag.term,
dashboard.is_folder,
dashboard.folder_id,
dashboard.deleted,
folder.uid AS folder_uid,
`)
if b.Features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
b.sql.WriteString(`
folder.title AS folder_slug,`)
} else {
b.sql.WriteString(`
folder.slug AS folder_slug,`)
}
b.sql.WriteString(`
folder.title AS folder_title `)
for _, f := range b.Filters {
if f, ok := f.(model.FilterSelect); ok {
b.sql.WriteString(fmt.Sprintf(", %s", f.Select()))
}
if f, ok := f.(model.FilterWith); ok {
recQuery, recQueryParams = f.With()
}
}
b.sql.WriteString(` FROM `)
if recQuery == "" {
return
}
// prepend recursive queries
var bf bytes.Buffer
bf.WriteString(recQuery)
bf.WriteString(b.sql.String())
b.sql = bf
b.params = append(recQueryParams, b.params...)
}
func (b *Builder) applyFilters() (ordering string) {
joins := []string{}
orderJoins := []string{}
wheres := []string{}
whereParams := []any{}
groups := []string{}
groupParams := []any{}
orders := []string{}
for _, f := range b.Filters {
if f, ok := f.(model.FilterLeftJoin); ok {
s := f.LeftJoin()
if s != "" {
joins = append(joins, fmt.Sprintf(" LEFT OUTER JOIN %s ", s))
}
}
if f, ok := f.(model.FilterWhere); ok {
sql, params := f.Where()
if sql != "" {
wheres = append(wheres, sql)
whereParams = append(whereParams, params...)
}
}
if f, ok := f.(model.FilterGroupBy); ok {
sql, params := f.GroupBy()
if sql != "" {
groups = append(groups, sql)
groupParams = append(groupParams, params...)
}
}
if f, ok := f.(model.FilterOrderBy); ok {
if f, ok := f.(model.FilterLeftJoin); ok {
orderJoins = append(orderJoins, fmt.Sprintf(" LEFT OUTER JOIN %s ", f.LeftJoin()))
}
orders = append(orders, f.OrderBy())
}
}
b.sql.WriteString("SELECT dashboard.id FROM dashboard")
b.sql.WriteString(strings.Join(joins, ""))
if len(wheres) > 0 {
b.sql.WriteString(fmt.Sprintf(" WHERE %s", strings.Join(wheres, " AND ")))
b.params = append(b.params, whereParams...)
}
if len(orders) < 1 {
orders = append(orders, TitleSorter{}.OrderBy())
}
if len(groups) > 0 {
cols := make([]string, 0, len(orders)+len(groups))
for _, o := range orders {
o := strings.TrimSuffix(o, " DESC")
o = strings.TrimSuffix(o, " ASC")
exists := false
for _, g := range groups {
if g == o {
exists = true
break
}
}
if !exists {
cols = append(cols, o)
}
}
cols = append(cols, groups...)
b.sql.WriteString(fmt.Sprintf(" GROUP BY %s", strings.Join(cols, ", ")))
b.params = append(b.params, groupParams...)
}
orderByCols := []string{}
for _, o := range orders {
orderByCols = append(orderByCols, b.Dialect.OrderBy(o))
}
orderBy := fmt.Sprintf(" ORDER BY %s", strings.Join(orderByCols, ", "))
b.sql.WriteString(orderBy)
order := strings.Join(orderJoins, "")
order += orderBy
return order
}