summary history files

internal/store/config.go
package store

import (
	"context"
	"database/sql"
)

// ConfigStorer manages metric config.
type ConfigStorer interface {
	Create(context.Context, int64) error
	SelectOne(context.Context, int64, string) (string, error)
	Upsert(context.Context, *Config) error
	SelectLimit(context.Context, int64) ([]Config, error)
}

// ConfigStore manages metric config.
type ConfigStore struct {
	DB *sql.DB
}

// Create creates a new config item.
func (s ConfigStore) Create(ctx context.Context, metricID int64) error {
	tx, err := s.DB.BeginTx(ctx, nil)
	if err != nil {
		return err
	}
	defer tx.Rollback()

	stmt, err := tx.PrepareContext(ctx, "INSERT INTO config (metric_id, opt, val) VALUES (?, ?, ?)")
	if err != nil {
		return err
	}

	// FIXME: Default data_type to float for now but support WithDataType() in the future.
	_, err = stmt.ExecContext(ctx, metricID, "data_type", "float")
	if err != nil {
		return err
	}

	return tx.Commit()
}

// SelectOne selects a single config value from a metric ID and config option.
func (s ConfigStore) SelectOne(ctx context.Context, metricID int64, configOpt string) (string, error) {
	tx, err := s.DB.BeginTx(ctx, nil)
	if err != nil {
		return "", err
	}
	defer tx.Rollback()

	var ret string
	err = tx.QueryRowContext(ctx, "SELECT val FROM config WHERE metric_id = ? AND opt = ?", metricID, configOpt).Scan(&ret)
	if err != nil && err != sql.ErrNoRows {
		return "", err
	}
	if err != nil && err == sql.ErrNoRows {
		return "", ErrNotFound
	}

	return ret, tx.Commit()
}

// Upsert inserts a new config or updates an existing config if it exists.
func (s ConfigStore) Upsert(ctx context.Context, o *Config) error {
	tx, err := s.DB.BeginTx(ctx, nil)
	if err != nil {
		return err
	}
	defer tx.Rollback()

	stmt, err := tx.PrepareContext(ctx, "INSERT INTO config (metric_id, opt, val) VALUES (?, ?, ?) ON CONFLICT(metric_id, opt) DO UPDATE SET val = ?")
	if err != nil {
		return err
	}

	_, err = stmt.ExecContext(ctx, o.MetricID, o.Opt, o.Val, o.Val)
	if err != nil {
		return err
	}

	return tx.Commit()
}

// SelectLimit returns all config rows up to limit. A limit of 0 is no limit.
func (s ConfigStore) SelectLimit(ctx context.Context, limit int64) ([]Config, error) {

	tx, err := s.DB.BeginTx(ctx, nil)
	if err != nil {
		return nil, err
	}
	defer tx.Rollback()

	var ret []Config
	var rows *sql.Rows

	if limit == 0 {
		rows, err = tx.QueryContext(ctx, "SELECT metric_id, opt, val FROM config")
		if err != nil {
			return nil, err
		}
	} else {
		rows, err = tx.QueryContext(ctx, "SELECT metric_id, opt, val FROM config LIMIT ?", limit)
		if err != nil {
			return nil, err
		}
	}
	defer rows.Close()

	for rows.Next() {
		var o Config
		if err = rows.Scan(&o.MetricID, &o.Opt, &o.Val); err != nil {
			return nil, err
		}
		ret = append(ret, o)
	}

	if err := rows.Err(); err != nil {
		return nil, err
	}

	return ret, tx.Commit()
}