grafana

Database

Grafana uses a database to persist settings between restarts. In fact, if you don’t specify one, Grafana creates a SQLite3 database file on your local disk. This guide explains how to store and retrieve data from the database.

Grafana supports the following databases:

Grafana uses the XORM framework for persisting objects to the database. For more information on how to use XORM, refer to the documentation.

Services don’t use XORM directly. Instead, services use the SQL store, a special type of service that provides an abstraction for the database layer. There are two ways of using the sqlstore: using sqlstore handlers, and using the SqlStore instance.

sqlstore handlers

Deprecated: We are deprecating sqlstore handlers in favor of using the SqlStore object directly in each service. Since most services still use the sqlstore handlers, we still want to explain how they work.

The sqlstore package allows you to register command handlers that either store, or retrieve objects from the database. sqlstore handlers are similar to services:

Register a sqlstore handler

Deprecated: Refer to the deprecation note for sqlstore handlers.

To register a handler:

func init() {
    bus.AddHandler("sql", DeleteDashboard)
}

func DeleteDashboard(cmd *models.DeleteDashboardCommand) error {
    return inTransaction(func(sess *DBSession) error {
        _, err := sess.Exec("DELETE FROM dashboards WHERE dashboard_id=?", cmd.DashboardID)
        return err
    })
}

Here, inTransaction is a helper function in the sqlstore package that provides a session, that lets you execute SQL statements.

SqlStore

As opposed to a sqlstore handler, the SqlStore is a service itself. The SqlStore has the same responsibility however: to store and retrieve objects, to and from the database.

To use the SqlStore, inject the SQLStore in your service struct:

type MyService struct {
    SQLStore *sqlstore.SqlStore `inject:""`
}

You can now make SQL queries in any of your command handlers or event listeners:

func (s *MyService) DeleteDashboard(cmd *models.DeleteDashboardCommand) error {
    if err := s.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
        _, err := sess.Exec("DELETE FROM dashboards WHERE dashboard_id=?", cmd.DashboardID)
        return err
    })
}

For transactions, use the WithTransactionalDbSession method instead.

Migrations

As Grafana evolves, it becomes necessary to create schema migrations for one or more database tables.

To see all the types of migrations you can add, refer to migrations.go.

Before you add a migration, make sure that you:

Add a migration using one of the following methods:

Important: If there are previous migrations for a service, use that method. By adding migrations using both methods, you risk running migrations in the wrong order.

Add migrations in migrations package

Most services have their migrations located in the migrations package.

To add a migration:

Example

Implement DatabaseMigrator

During initialization, SQL store queries the service registry, and runs migrations for every service that implements the DatabaseMigrator interface.

To add a migration:

func (s *MyService) AddMigration(mg *migrator.Migrator) {
    // ...

    mg.AddMigration("Add column age", NewAddColumnMigration(table, &Column{
        Name:     "age",
        Type:     migrator.DB_BigInt,
        Nullable: true,
    }))
}