fix: handle dashboards without version entries in unified storage migration (#108139)
* fix: handle dashboards without version entries in migration Signed-off-by: Bruno Abrantes <bruno@brunoabrantes.com> * fix: adds AllowFallback flag to enable COALESCE logic explicitly during migrations Signed-off-by: Bruno Abrantes <bruno@brunoabrantes.com> * fix: update dependencies Signed-off-by: Bruno Abrantes <bruno@brunoabrantes.com> * fix: maintain original behavior when not using AllowFallback option Signed-off-by: Bruno Abrantes <bruno@brunoabrantes.com> --------- Signed-off-by: Bruno Abrantes <bruno@brunoabrantes.com>
This commit is contained in:
@@ -230,7 +230,7 @@ require (
|
||||
require (
|
||||
github.com/grafana/grafana/apps/advisor v0.0.0-20250627191313-2f1a6ae1712b // @grafana/plugins-platform-backend
|
||||
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250627191313-2f1a6ae1712b // @grafana/alerting-backend
|
||||
github.com/grafana/grafana/apps/dashboard v0.0.0-20250716154214-974103c6fa20 // @grafana/grafana-app-platform-squad @grafana/dashboards-squad
|
||||
github.com/grafana/grafana/apps/dashboard v0.0.0-20250716132114-6fd75ebc5441 // @grafana/grafana-app-platform-squad @grafana/dashboards-squad
|
||||
github.com/grafana/grafana/apps/folder v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-search-and-storage
|
||||
github.com/grafana/grafana/apps/iam v0.0.0-20250627191313-2f1a6ae1712b // @grafana/identity-access-team
|
||||
github.com/grafana/grafana/apps/investigations v0.0.0-20250627191313-2f1a6ae1712b // @fcjack @matryer
|
||||
|
||||
@@ -1613,8 +1613,8 @@ github.com/grafana/grafana/apps/advisor v0.0.0-20250627191313-2f1a6ae1712b h1:8o
|
||||
github.com/grafana/grafana/apps/advisor v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:q+h3HbmqU/PposW6lq8cMle1v8vuyX1LCMrGzbabHxc=
|
||||
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250627191313-2f1a6ae1712b h1:jr+C3epmjhd5Yyob4P1Z/dPaW4LRTkU5UJLXsI4eaeM=
|
||||
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:WpI7TCck4P2wKTO2WJLBRcfOWvUGvTdxYu3QqS3z7jM=
|
||||
github.com/grafana/grafana/apps/dashboard v0.0.0-20250716154214-974103c6fa20 h1:2aeZOXOdJID5BbrUtBWG+yGUU4wDXkqwsq9drSalrDE=
|
||||
github.com/grafana/grafana/apps/dashboard v0.0.0-20250716154214-974103c6fa20/go.mod h1:1XWiRSVuDQiayapHhQiDc4S4e9GzEZgg/3GeNCuDgn4=
|
||||
github.com/grafana/grafana/apps/dashboard v0.0.0-20250716132114-6fd75ebc5441 h1:+TSbaxCXBZrKkdROWBzdWna8uStE1f9LYd7GiqjVfz8=
|
||||
github.com/grafana/grafana/apps/dashboard v0.0.0-20250716132114-6fd75ebc5441/go.mod h1:1XWiRSVuDQiayapHhQiDc4S4e9GzEZgg/3GeNCuDgn4=
|
||||
github.com/grafana/grafana/apps/folder v0.0.0-20250627191313-2f1a6ae1712b h1:31MwoIKKT9Ay0ZjbT4lkfcPijiWogUWzXs2EjrCgodI=
|
||||
github.com/grafana/grafana/apps/folder v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:dLtYBp1pza5HYalezNvzlP8JDeKrZ5BKTonDgEOE0NY=
|
||||
github.com/grafana/grafana/apps/iam v0.0.0-20250627191313-2f1a6ae1712b h1:NV8v9xdM/pzjjy+1cLqUseia3bYcvQGh88vZdMW/jA0=
|
||||
|
||||
@@ -211,10 +211,11 @@ func (a *dashboardSqlAccess) countValues(ctx context.Context, opts MigrateOption
|
||||
|
||||
func (a *dashboardSqlAccess) migrateDashboards(ctx context.Context, orgId int64, opts MigrateOptions, stream resourcepb.BulkStore_BulkProcessClient) (*BlobStoreInfo, error) {
|
||||
query := &DashboardQuery{
|
||||
OrgID: orgId,
|
||||
Limit: 100000000,
|
||||
GetHistory: opts.WithHistory, // include history
|
||||
Order: "ASC", // oldest first
|
||||
OrgID: orgId,
|
||||
Limit: 100000000,
|
||||
GetHistory: opts.WithHistory, // include history
|
||||
AllowFallback: true, // allow fallback to dashboard table during migration
|
||||
Order: "ASC", // oldest first
|
||||
}
|
||||
|
||||
blobs := &BlobStoreInfo{}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate/mocks"
|
||||
)
|
||||
|
||||
func TestDashboardMigrationQuery(t *testing.T) {
|
||||
// Test that migration queries use AllowFallback flag correctly
|
||||
nodb := &legacysql.LegacyDatabaseHelper{
|
||||
Table: func(n string) string {
|
||||
return "grafana." + n
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Migration query should enable AllowFallback flag", func(t *testing.T) {
|
||||
// Create a migration query as would be used in actual migration
|
||||
migrationQuery := &DashboardQuery{
|
||||
OrgID: 1,
|
||||
GetHistory: true, // Migration includes history
|
||||
AllowFallback: true, // This is the key flag for migration
|
||||
Order: "ASC", // Migration uses ascending order
|
||||
}
|
||||
|
||||
// Verify UseHistoryTable returns true (requirement for COALESCE logic)
|
||||
require.True(t, migrationQuery.UseHistoryTable(), "Migration query should use history table")
|
||||
|
||||
// Verify the flag is set correctly
|
||||
require.True(t, migrationQuery.AllowFallback, "Migration query should allow fallback")
|
||||
require.True(t, migrationQuery.GetHistory, "Migration query should get history")
|
||||
require.Equal(t, "ASC", migrationQuery.Order, "Migration should use ascending order")
|
||||
})
|
||||
|
||||
t.Run("Regular history query should not use AllowFallback", func(t *testing.T) {
|
||||
// Regular history query without migration
|
||||
historyQuery := &DashboardQuery{
|
||||
OrgID: 1,
|
||||
GetHistory: true,
|
||||
Order: "DESC",
|
||||
}
|
||||
|
||||
require.True(t, historyQuery.UseHistoryTable(), "History query should use history table")
|
||||
require.False(t, historyQuery.AllowFallback, "Regular history query should not allow fallback")
|
||||
require.True(t, historyQuery.GetHistory, "History query should get history")
|
||||
})
|
||||
|
||||
t.Run("Migration query template produces COALESCE SQL", func(t *testing.T) {
|
||||
// Test that the SQL template produces COALESCE logic for migration queries
|
||||
migrationQuery := &DashboardQuery{
|
||||
OrgID: 1,
|
||||
GetHistory: true,
|
||||
AllowFallback: true,
|
||||
Order: "ASC",
|
||||
}
|
||||
|
||||
req := newQueryReq(nodb, migrationQuery)
|
||||
req.SQLTemplate = mocks.NewTestingSQLTemplate()
|
||||
|
||||
// Execute the template to get the generated SQL
|
||||
rawQuery, err := sqltemplate.Execute(sqlQueryDashboards, &req)
|
||||
require.NoError(t, err)
|
||||
|
||||
sql := rawQuery
|
||||
|
||||
// Verify that COALESCE functions are present in the generated SQL
|
||||
// These should be used when GetHistory=true AND AllowFallback=true
|
||||
require.Contains(t, sql, "COALESCE(dashboard_version.created, dashboard.updated)",
|
||||
"Migration SQL should contain COALESCE for updated timestamp")
|
||||
require.Contains(t, sql, "COALESCE(dashboard_version.version, dashboard.version)",
|
||||
"Migration SQL should contain COALESCE for version")
|
||||
require.Contains(t, sql, "COALESCE(dashboard_version.data, dashboard.data)",
|
||||
"Migration SQL should contain COALESCE for data")
|
||||
require.Contains(t, sql, "COALESCE(dashboard_version.api_version, dashboard.api_version)",
|
||||
"Migration SQL should contain COALESCE for api_version")
|
||||
require.Contains(t, sql, "COALESCE(dashboard_version.message, '')",
|
||||
"Migration SQL should contain COALESCE for message with empty string fallback")
|
||||
|
||||
// Verify ORDER BY uses COALESCE as well
|
||||
require.Contains(t, sql, "COALESCE(dashboard_version.created, dashboard.updated) ASC",
|
||||
"Migration SQL should ORDER BY COALESCED created timestamp")
|
||||
require.Contains(t, sql, "COALESCE(dashboard_version.version, dashboard.version) ASC",
|
||||
"Migration SQL should ORDER BY COALESCED version")
|
||||
|
||||
// Verify it doesn't have the strict history table filter that would exclude NULL version entries
|
||||
require.NotContains(t, sql, "dashboard_version.id IS NOT NULL",
|
||||
"Migration SQL should not exclude dashboards without version entries")
|
||||
})
|
||||
|
||||
t.Run("Regular history query produces strict SQL", func(t *testing.T) {
|
||||
// Test that regular history queries still use strict dashboard_version fields
|
||||
historyQuery := &DashboardQuery{
|
||||
OrgID: 1,
|
||||
GetHistory: true,
|
||||
Order: "DESC",
|
||||
}
|
||||
|
||||
req := newQueryReq(nodb, historyQuery)
|
||||
req.SQLTemplate = mocks.NewTestingSQLTemplate()
|
||||
|
||||
rawQuery, err := sqltemplate.Execute(sqlQueryDashboards, &req)
|
||||
require.NoError(t, err)
|
||||
|
||||
sql := rawQuery
|
||||
|
||||
// Verify that direct dashboard_version fields are used (no COALESCE)
|
||||
require.Contains(t, sql, "dashboard_version.created as updated",
|
||||
"Regular history SQL should use direct dashboard_version.created")
|
||||
require.Contains(t, sql, "dashboard_version.version",
|
||||
"Regular history SQL should use direct dashboard_version.version")
|
||||
require.Contains(t, sql, "dashboard_version.data",
|
||||
"Regular history SQL should use direct dashboard_version.data")
|
||||
|
||||
// NOTE: We intentionally do NOT add dashboard_version.id IS NOT NULL filter
|
||||
// to allow for cases where dashboard_version entries might be missing
|
||||
|
||||
// Should not contain COALESCE functions
|
||||
require.NotContains(t, sql, "COALESCE(dashboard_version.created, dashboard.updated)",
|
||||
"Regular history SQL should not contain COALESCE for updated")
|
||||
})
|
||||
}
|
||||
|
||||
func TestMigrateDashboardsConfiguration(t *testing.T) {
|
||||
// Test the actual migration function configuration
|
||||
|
||||
t.Run("Migration options should configure query correctly", func(t *testing.T) {
|
||||
// Test the migration configuration as used in real migration
|
||||
opts := MigrateOptions{
|
||||
WithHistory: true, // Migration includes history
|
||||
}
|
||||
|
||||
// This simulates what happens in migrateDashboards function
|
||||
expectedQuery := &DashboardQuery{
|
||||
OrgID: 1,
|
||||
Limit: 100000000,
|
||||
GetHistory: opts.WithHistory, // Should be true
|
||||
AllowFallback: true, // Should be true for migration
|
||||
Order: "ASC", // Should be ASC for migration
|
||||
}
|
||||
|
||||
// Verify the configuration matches what migration sets up
|
||||
require.True(t, expectedQuery.GetHistory, "Migration should enable GetHistory")
|
||||
require.True(t, expectedQuery.AllowFallback, "Migration should enable AllowFallback")
|
||||
require.Equal(t, "ASC", expectedQuery.Order, "Migration should use ascending order")
|
||||
require.Equal(t, 100000000, expectedQuery.Limit, "Migration should use large limit")
|
||||
|
||||
// Verify UseHistoryTable logic
|
||||
require.True(t, expectedQuery.UseHistoryTable(), "Migration query should use history table")
|
||||
})
|
||||
}
|
||||
@@ -85,6 +85,15 @@ func TestDashboardQueries(t *testing.T) {
|
||||
Order: "ASC",
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "migration_with_fallback",
|
||||
Data: getQuery(&DashboardQuery{
|
||||
OrgID: 1,
|
||||
GetHistory: true,
|
||||
AllowFallback: true,
|
||||
Order: "ASC",
|
||||
}),
|
||||
},
|
||||
},
|
||||
sqlQueryPanels: {
|
||||
{
|
||||
|
||||
@@ -13,14 +13,24 @@ SELECT
|
||||
created_user.uid as created_by,
|
||||
dashboard.created_by as created_by_id,
|
||||
{{ if .Query.UseHistoryTable }}
|
||||
dashboard_version.created,
|
||||
{{ if and .Query.GetHistory (not .Query.AllowFallback) }}
|
||||
dashboard_version.created as updated,
|
||||
updated_user.uid as updated_by,
|
||||
updated_user.id as created_by_id,
|
||||
dashboard_version.created_by as updated_by_id,
|
||||
dashboard_version.version,
|
||||
dashboard_version.message,
|
||||
dashboard_version.data,
|
||||
dashboard_version.api_version
|
||||
{{ else }}
|
||||
COALESCE(dashboard_version.created, dashboard.updated) as updated,
|
||||
updated_user.uid as updated_by,
|
||||
COALESCE(dashboard_version.created_by, dashboard.updated_by) as updated_by_id,
|
||||
COALESCE(dashboard_version.version, dashboard.version) as version,
|
||||
COALESCE(dashboard_version.message, '') as message,
|
||||
COALESCE(dashboard_version.data, dashboard.data) as data,
|
||||
COALESCE(dashboard_version.api_version, dashboard.api_version) as api_version
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
dashboard.updated,
|
||||
updated_user.uid as updated_by,
|
||||
dashboard.updated_by as updated_by_id,
|
||||
@@ -35,7 +45,7 @@ LEFT OUTER JOIN {{ .Ident .VersionTable }} as dashboard_version ON dashboard.id
|
||||
{{ end }}
|
||||
LEFT OUTER JOIN {{ .Ident .ProvisioningTable }} as provisioning ON dashboard.id = provisioning.dashboard_id
|
||||
LEFT OUTER JOIN {{ .Ident .UserTable }} as created_user ON dashboard.created_by = created_user.id
|
||||
LEFT OUTER JOIN {{ .Ident .UserTable }} as updated_user ON {{ if .Query.UseHistoryTable }}dashboard_version.created_by = updated_user.id{{ else }}dashboard.updated_by = updated_user.id{{ end }}
|
||||
LEFT OUTER JOIN {{ .Ident .UserTable }} as updated_user ON {{ if .Query.UseHistoryTable }}{{ if and .Query.GetHistory (not .Query.AllowFallback) }}dashboard_version.created_by = updated_user.id{{ else }}COALESCE(dashboard_version.created_by, dashboard.updated_by) = updated_user.id{{ end }}{{ else }}dashboard.updated_by = updated_user.id{{ end }}
|
||||
WHERE dashboard.is_folder = {{ .Arg .Query.GetFolders }}
|
||||
AND dashboard.org_id = {{ .Arg .Query.OrgID }}
|
||||
{{ if .Query.UseHistoryTable }}
|
||||
@@ -45,11 +55,20 @@ WHERE dashboard.is_folder = {{ .Arg .Query.GetFolders }}
|
||||
{{ if .Query.Version }}
|
||||
AND dashboard_version.version = {{ .Arg .Query.Version }}
|
||||
{{ else if .Query.LastID }}
|
||||
{{ if and .Query.GetHistory (not .Query.AllowFallback) }}
|
||||
AND dashboard_version.version < {{ .Arg .Query.LastID }}
|
||||
{{ else }}
|
||||
AND COALESCE(dashboard_version.version, dashboard.version) < {{ .Arg .Query.LastID }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
ORDER BY
|
||||
{{ if and .Query.GetHistory (not .Query.AllowFallback) }}
|
||||
dashboard_version.created {{ .Query.Order }},
|
||||
dashboard_version.version {{ .Query.Order }},
|
||||
{{ else }}
|
||||
COALESCE(dashboard_version.created, dashboard.updated) {{ .Query.Order }},
|
||||
COALESCE(dashboard_version.version, dashboard.version) {{ .Query.Order }},
|
||||
{{ end }}
|
||||
dashboard.uid ASC
|
||||
{{ else }}
|
||||
{{ if .Query.UID }}
|
||||
|
||||
@@ -263,6 +263,7 @@ func (a *dashboardSqlAccess) scanRow(rows *sql.Rows, history bool) (*dashboardRo
|
||||
&updated, &updatedBy, &updatedByID,
|
||||
&version, &message, &data, &apiVersion,
|
||||
)
|
||||
|
||||
switch apiVersion.String {
|
||||
case "":
|
||||
apiVersion.String = dashboardV0.VERSION // default value
|
||||
|
||||
@@ -128,6 +128,65 @@ func TestScanRow(t *testing.T) {
|
||||
require.Equal(t, "slo", manager.Identity) // the ID of the plugin
|
||||
require.Equal(t, "", meta.GetAnnotations()[utils.AnnoKeySourceChecksum]) // hash is not used on plugins
|
||||
})
|
||||
|
||||
t.Run("Migration scenario should use COALESCE logic with AllowFallback=true", func(t *testing.T) {
|
||||
// This specifically tests the migration use case where GetHistory=true but AllowFallback=true
|
||||
// allows the query to use COALESCE logic to fall back to dashboard table data when
|
||||
// dashboard_version entries are missing.
|
||||
|
||||
migrationTimestamp := timestamp.Add(2 * time.Hour) // Migration scenario timestamp
|
||||
migrationVersion := int64(5)
|
||||
migrationUpdatedUser := "migration_updater"
|
||||
migrationMessage := "" // Empty message for migration (COALESCE behavior)
|
||||
migrationData := []byte(`{"migration": "data", "title": "Migrated Dashboard"}`)
|
||||
migrationAPIVersion := "v0alpha1"
|
||||
|
||||
// In migration scenario, COALESCE functions return dashboard table values
|
||||
// when dashboard_version values are NULL, ensuring all dashboards are migrated
|
||||
rows := sqlmock.NewRows(columns).AddRow(
|
||||
1, id, title, folderUID, nil, "", // basic dashboard fields
|
||||
"", "", "", 0, // origin fields
|
||||
timestamp, createdUser, 0, // created fields
|
||||
// These represent COALESCED values from dashboard table (not version table)
|
||||
migrationTimestamp, migrationUpdatedUser, 0, migrationVersion, migrationMessage, migrationData, migrationAPIVersion,
|
||||
)
|
||||
|
||||
mock.ExpectQuery("SELECT *").WillReturnRows(rows)
|
||||
resultRows, err := mockDB.Query("SELECT *")
|
||||
require.NoError(t, err)
|
||||
defer resultRows.Close() // nolint:errcheck
|
||||
resultRows.Next()
|
||||
|
||||
// Test with history=true (migration scenario) - should work with COALESCED values
|
||||
row, err := store.scanRow(resultRows, true)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, row)
|
||||
|
||||
// Verify migration scenario works correctly with fallback data
|
||||
require.Equal(t, title, row.Dash.Name)
|
||||
require.Equal(t, migrationVersion, row.RV) // Should use COALESCEd dashboard table version
|
||||
require.Equal(t, common.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"migration": "data",
|
||||
"title": "Migrated Dashboard",
|
||||
},
|
||||
}, row.Dash.Spec) // Should use COALESCEd dashboard table data
|
||||
require.Equal(t, "default", row.Dash.Namespace)
|
||||
|
||||
// Token should use the version in history mode (migration scenario)
|
||||
require.Equal(t, &continueToken{orgId: int64(1), id: migrationVersion}, row.token)
|
||||
|
||||
meta, err := utils.MetaAccessor(row.Dash)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, id, meta.GetDeprecatedInternalID()) // nolint:staticcheck
|
||||
require.Equal(t, migrationVersion, meta.GetGeneration()) // Should use COALESCEd version
|
||||
require.Equal(t, k8sTimestamp, meta.GetCreationTimestamp()) // Created timestamp unchanged
|
||||
require.Equal(t, "user:"+createdUser, meta.GetCreatedBy()) // Original creator preserved
|
||||
require.Equal(t, "user:"+migrationUpdatedUser, meta.GetUpdatedBy()) // COALESCEd updater
|
||||
require.Equal(t, migrationMessage, meta.GetMessage()) // Empty message from COALESCE
|
||||
require.Equal(t, folderUID, meta.GetFolder())
|
||||
require.Equal(t, "dashboard.grafana.app/"+migrationAPIVersion, row.Dash.APIVersion)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildSaveDashboardCommand(t *testing.T) {
|
||||
|
||||
Vendored
+2
-2
@@ -12,9 +12,9 @@ SELECT
|
||||
dashboard.created,
|
||||
created_user.uid as created_by,
|
||||
dashboard.created_by as created_by_id,
|
||||
dashboard_version.created,
|
||||
dashboard_version.created as updated,
|
||||
updated_user.uid as updated_by,
|
||||
updated_user.id as created_by_id,
|
||||
dashboard_version.created_by as updated_by_id,
|
||||
dashboard_version.version,
|
||||
dashboard_version.message,
|
||||
dashboard_version.data,
|
||||
|
||||
Vendored
+9
-9
@@ -12,23 +12,23 @@ SELECT
|
||||
dashboard.created,
|
||||
created_user.uid as created_by,
|
||||
dashboard.created_by as created_by_id,
|
||||
dashboard_version.created,
|
||||
COALESCE(dashboard_version.created, dashboard.updated) as updated,
|
||||
updated_user.uid as updated_by,
|
||||
updated_user.id as created_by_id,
|
||||
dashboard_version.version,
|
||||
dashboard_version.message,
|
||||
dashboard_version.data,
|
||||
dashboard_version.api_version
|
||||
COALESCE(dashboard_version.created_by, dashboard.updated_by) as updated_by_id,
|
||||
COALESCE(dashboard_version.version, dashboard.version) as version,
|
||||
COALESCE(dashboard_version.message, '') as message,
|
||||
COALESCE(dashboard_version.data, dashboard.data) as data,
|
||||
COALESCE(dashboard_version.api_version, dashboard.api_version) as api_version
|
||||
FROM `grafana`.`dashboard` as dashboard
|
||||
LEFT OUTER JOIN `grafana`.`dashboard_version` as dashboard_version ON dashboard.id = dashboard_version.dashboard_id
|
||||
LEFT OUTER JOIN `grafana`.`dashboard_provisioning` as provisioning ON dashboard.id = provisioning.dashboard_id
|
||||
LEFT OUTER JOIN `grafana`.`user` as created_user ON dashboard.created_by = created_user.id
|
||||
LEFT OUTER JOIN `grafana`.`user` as updated_user ON dashboard_version.created_by = updated_user.id
|
||||
LEFT OUTER JOIN `grafana`.`user` as updated_user ON COALESCE(dashboard_version.created_by, dashboard.updated_by) = updated_user.id
|
||||
WHERE dashboard.is_folder = FALSE
|
||||
AND dashboard.org_id = 2
|
||||
AND dashboard.uid = 'UUU'
|
||||
AND dashboard_version.version = 3
|
||||
ORDER BY
|
||||
dashboard_version.created DESC,
|
||||
dashboard_version.version DESC,
|
||||
COALESCE(dashboard_version.created, dashboard.updated) DESC,
|
||||
COALESCE(dashboard_version.version, dashboard.version) DESC,
|
||||
dashboard.uid ASC
|
||||
|
||||
Vendored
Executable
+32
@@ -0,0 +1,32 @@
|
||||
SELECT
|
||||
dashboard.org_id,
|
||||
dashboard.id,
|
||||
dashboard.uid,
|
||||
dashboard.folder_uid,
|
||||
dashboard.deleted,
|
||||
plugin_id,
|
||||
provisioning.name as repo_name,
|
||||
provisioning.external_id as repo_path,
|
||||
provisioning.check_sum as repo_hash,
|
||||
provisioning.updated as repo_ts,
|
||||
dashboard.created,
|
||||
created_user.uid as created_by,
|
||||
dashboard.created_by as created_by_id,
|
||||
COALESCE(dashboard_version.created, dashboard.updated) as updated,
|
||||
updated_user.uid as updated_by,
|
||||
COALESCE(dashboard_version.created_by, dashboard.updated_by) as updated_by_id,
|
||||
COALESCE(dashboard_version.version, dashboard.version) as version,
|
||||
COALESCE(dashboard_version.message, '') as message,
|
||||
COALESCE(dashboard_version.data, dashboard.data) as data,
|
||||
COALESCE(dashboard_version.api_version, dashboard.api_version) as api_version
|
||||
FROM `grafana`.`dashboard` as dashboard
|
||||
LEFT OUTER JOIN `grafana`.`dashboard_version` as dashboard_version ON dashboard.id = dashboard_version.dashboard_id
|
||||
LEFT OUTER JOIN `grafana`.`dashboard_provisioning` as provisioning ON dashboard.id = provisioning.dashboard_id
|
||||
LEFT OUTER JOIN `grafana`.`user` as created_user ON dashboard.created_by = created_user.id
|
||||
LEFT OUTER JOIN `grafana`.`user` as updated_user ON COALESCE(dashboard_version.created_by, dashboard.updated_by) = updated_user.id
|
||||
WHERE dashboard.is_folder = FALSE
|
||||
AND dashboard.org_id = 1
|
||||
ORDER BY
|
||||
COALESCE(dashboard_version.created, dashboard.updated) ASC,
|
||||
COALESCE(dashboard_version.version, dashboard.version) ASC,
|
||||
dashboard.uid ASC
|
||||
Vendored
+2
-2
@@ -12,9 +12,9 @@ SELECT
|
||||
dashboard.created,
|
||||
created_user.uid as created_by,
|
||||
dashboard.created_by as created_by_id,
|
||||
dashboard_version.created,
|
||||
dashboard_version.created as updated,
|
||||
updated_user.uid as updated_by,
|
||||
updated_user.id as created_by_id,
|
||||
dashboard_version.created_by as updated_by_id,
|
||||
dashboard_version.version,
|
||||
dashboard_version.message,
|
||||
dashboard_version.data,
|
||||
|
||||
Vendored
+9
-9
@@ -12,23 +12,23 @@ SELECT
|
||||
dashboard.created,
|
||||
created_user.uid as created_by,
|
||||
dashboard.created_by as created_by_id,
|
||||
dashboard_version.created,
|
||||
COALESCE(dashboard_version.created, dashboard.updated) as updated,
|
||||
updated_user.uid as updated_by,
|
||||
updated_user.id as created_by_id,
|
||||
dashboard_version.version,
|
||||
dashboard_version.message,
|
||||
dashboard_version.data,
|
||||
dashboard_version.api_version
|
||||
COALESCE(dashboard_version.created_by, dashboard.updated_by) as updated_by_id,
|
||||
COALESCE(dashboard_version.version, dashboard.version) as version,
|
||||
COALESCE(dashboard_version.message, '') as message,
|
||||
COALESCE(dashboard_version.data, dashboard.data) as data,
|
||||
COALESCE(dashboard_version.api_version, dashboard.api_version) as api_version
|
||||
FROM "grafana"."dashboard" as dashboard
|
||||
LEFT OUTER JOIN "grafana"."dashboard_version" as dashboard_version ON dashboard.id = dashboard_version.dashboard_id
|
||||
LEFT OUTER JOIN "grafana"."dashboard_provisioning" as provisioning ON dashboard.id = provisioning.dashboard_id
|
||||
LEFT OUTER JOIN "grafana"."user" as created_user ON dashboard.created_by = created_user.id
|
||||
LEFT OUTER JOIN "grafana"."user" as updated_user ON dashboard_version.created_by = updated_user.id
|
||||
LEFT OUTER JOIN "grafana"."user" as updated_user ON COALESCE(dashboard_version.created_by, dashboard.updated_by) = updated_user.id
|
||||
WHERE dashboard.is_folder = FALSE
|
||||
AND dashboard.org_id = 2
|
||||
AND dashboard.uid = 'UUU'
|
||||
AND dashboard_version.version = 3
|
||||
ORDER BY
|
||||
dashboard_version.created DESC,
|
||||
dashboard_version.version DESC,
|
||||
COALESCE(dashboard_version.created, dashboard.updated) DESC,
|
||||
COALESCE(dashboard_version.version, dashboard.version) DESC,
|
||||
dashboard.uid ASC
|
||||
|
||||
Vendored
Executable
+32
@@ -0,0 +1,32 @@
|
||||
SELECT
|
||||
dashboard.org_id,
|
||||
dashboard.id,
|
||||
dashboard.uid,
|
||||
dashboard.folder_uid,
|
||||
dashboard.deleted,
|
||||
plugin_id,
|
||||
provisioning.name as repo_name,
|
||||
provisioning.external_id as repo_path,
|
||||
provisioning.check_sum as repo_hash,
|
||||
provisioning.updated as repo_ts,
|
||||
dashboard.created,
|
||||
created_user.uid as created_by,
|
||||
dashboard.created_by as created_by_id,
|
||||
COALESCE(dashboard_version.created, dashboard.updated) as updated,
|
||||
updated_user.uid as updated_by,
|
||||
COALESCE(dashboard_version.created_by, dashboard.updated_by) as updated_by_id,
|
||||
COALESCE(dashboard_version.version, dashboard.version) as version,
|
||||
COALESCE(dashboard_version.message, '') as message,
|
||||
COALESCE(dashboard_version.data, dashboard.data) as data,
|
||||
COALESCE(dashboard_version.api_version, dashboard.api_version) as api_version
|
||||
FROM "grafana"."dashboard" as dashboard
|
||||
LEFT OUTER JOIN "grafana"."dashboard_version" as dashboard_version ON dashboard.id = dashboard_version.dashboard_id
|
||||
LEFT OUTER JOIN "grafana"."dashboard_provisioning" as provisioning ON dashboard.id = provisioning.dashboard_id
|
||||
LEFT OUTER JOIN "grafana"."user" as created_user ON dashboard.created_by = created_user.id
|
||||
LEFT OUTER JOIN "grafana"."user" as updated_user ON COALESCE(dashboard_version.created_by, dashboard.updated_by) = updated_user.id
|
||||
WHERE dashboard.is_folder = FALSE
|
||||
AND dashboard.org_id = 1
|
||||
ORDER BY
|
||||
COALESCE(dashboard_version.created, dashboard.updated) ASC,
|
||||
COALESCE(dashboard_version.version, dashboard.version) ASC,
|
||||
dashboard.uid ASC
|
||||
Vendored
+2
-2
@@ -12,9 +12,9 @@ SELECT
|
||||
dashboard.created,
|
||||
created_user.uid as created_by,
|
||||
dashboard.created_by as created_by_id,
|
||||
dashboard_version.created,
|
||||
dashboard_version.created as updated,
|
||||
updated_user.uid as updated_by,
|
||||
updated_user.id as created_by_id,
|
||||
dashboard_version.created_by as updated_by_id,
|
||||
dashboard_version.version,
|
||||
dashboard_version.message,
|
||||
dashboard_version.data,
|
||||
|
||||
Vendored
+9
-9
@@ -12,23 +12,23 @@ SELECT
|
||||
dashboard.created,
|
||||
created_user.uid as created_by,
|
||||
dashboard.created_by as created_by_id,
|
||||
dashboard_version.created,
|
||||
COALESCE(dashboard_version.created, dashboard.updated) as updated,
|
||||
updated_user.uid as updated_by,
|
||||
updated_user.id as created_by_id,
|
||||
dashboard_version.version,
|
||||
dashboard_version.message,
|
||||
dashboard_version.data,
|
||||
dashboard_version.api_version
|
||||
COALESCE(dashboard_version.created_by, dashboard.updated_by) as updated_by_id,
|
||||
COALESCE(dashboard_version.version, dashboard.version) as version,
|
||||
COALESCE(dashboard_version.message, '') as message,
|
||||
COALESCE(dashboard_version.data, dashboard.data) as data,
|
||||
COALESCE(dashboard_version.api_version, dashboard.api_version) as api_version
|
||||
FROM "grafana"."dashboard" as dashboard
|
||||
LEFT OUTER JOIN "grafana"."dashboard_version" as dashboard_version ON dashboard.id = dashboard_version.dashboard_id
|
||||
LEFT OUTER JOIN "grafana"."dashboard_provisioning" as provisioning ON dashboard.id = provisioning.dashboard_id
|
||||
LEFT OUTER JOIN "grafana"."user" as created_user ON dashboard.created_by = created_user.id
|
||||
LEFT OUTER JOIN "grafana"."user" as updated_user ON dashboard_version.created_by = updated_user.id
|
||||
LEFT OUTER JOIN "grafana"."user" as updated_user ON COALESCE(dashboard_version.created_by, dashboard.updated_by) = updated_user.id
|
||||
WHERE dashboard.is_folder = FALSE
|
||||
AND dashboard.org_id = 2
|
||||
AND dashboard.uid = 'UUU'
|
||||
AND dashboard_version.version = 3
|
||||
ORDER BY
|
||||
dashboard_version.created DESC,
|
||||
dashboard_version.version DESC,
|
||||
COALESCE(dashboard_version.created, dashboard.updated) DESC,
|
||||
COALESCE(dashboard_version.version, dashboard.version) DESC,
|
||||
dashboard.uid ASC
|
||||
|
||||
Vendored
Executable
+32
@@ -0,0 +1,32 @@
|
||||
SELECT
|
||||
dashboard.org_id,
|
||||
dashboard.id,
|
||||
dashboard.uid,
|
||||
dashboard.folder_uid,
|
||||
dashboard.deleted,
|
||||
plugin_id,
|
||||
provisioning.name as repo_name,
|
||||
provisioning.external_id as repo_path,
|
||||
provisioning.check_sum as repo_hash,
|
||||
provisioning.updated as repo_ts,
|
||||
dashboard.created,
|
||||
created_user.uid as created_by,
|
||||
dashboard.created_by as created_by_id,
|
||||
COALESCE(dashboard_version.created, dashboard.updated) as updated,
|
||||
updated_user.uid as updated_by,
|
||||
COALESCE(dashboard_version.created_by, dashboard.updated_by) as updated_by_id,
|
||||
COALESCE(dashboard_version.version, dashboard.version) as version,
|
||||
COALESCE(dashboard_version.message, '') as message,
|
||||
COALESCE(dashboard_version.data, dashboard.data) as data,
|
||||
COALESCE(dashboard_version.api_version, dashboard.api_version) as api_version
|
||||
FROM "grafana"."dashboard" as dashboard
|
||||
LEFT OUTER JOIN "grafana"."dashboard_version" as dashboard_version ON dashboard.id = dashboard_version.dashboard_id
|
||||
LEFT OUTER JOIN "grafana"."dashboard_provisioning" as provisioning ON dashboard.id = provisioning.dashboard_id
|
||||
LEFT OUTER JOIN "grafana"."user" as created_user ON dashboard.created_by = created_user.id
|
||||
LEFT OUTER JOIN "grafana"."user" as updated_user ON COALESCE(dashboard_version.created_by, dashboard.updated_by) = updated_user.id
|
||||
WHERE dashboard.is_folder = FALSE
|
||||
AND dashboard.org_id = 1
|
||||
ORDER BY
|
||||
COALESCE(dashboard_version.created, dashboard.updated) ASC,
|
||||
COALESCE(dashboard_version.version, dashboard.version) ASC,
|
||||
dashboard.uid ASC
|
||||
@@ -27,6 +27,10 @@ type DashboardQuery struct {
|
||||
GetHistory bool
|
||||
Version int64
|
||||
|
||||
// Allow fallback to dashboard table when version data is missing
|
||||
// Used during migration to handle dashboards without version entries
|
||||
AllowFallback bool
|
||||
|
||||
// Only folders
|
||||
GetFolders bool
|
||||
|
||||
|
||||
Reference in New Issue
Block a user