The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/sqlstore/searchstore/builder.go

196 lines
4.6 KiB

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.org_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
}