flucky/pkg/repository/sqlite.go

1180 lines
27 KiB
Go

package repository
import (
"context"
"database/sql"
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
"sync"
"git.cryptic.systems/volker.raschek/flucky/pkg/repository/postgres"
"git.cryptic.systems/volker.raschek/flucky/pkg/repository/sqlite3"
"git.cryptic.systems/volker.raschek/flucky/pkg/types"
"git.cryptic.systems/volker.raschek/go-logger"
"github.com/golang-migrate/migrate/v4"
"github.com/johejo/golang-migrate-extra/source/iofs"
)
type SQLiteOpts struct {
DatabaseURL *url.URL
Logger logger.Logger
}
func (o *SQLiteOpts) Validate() error {
for k, v := range map[string]interface{}{
"DatabaseURL": o.DatabaseURL,
"Logger": o.Logger,
} {
if v == nil {
return fmt.Errorf("%v does not be empty or nil", k)
}
}
if o.DatabaseURL.Scheme != "sqlite3" {
return fmt.Errorf("Unsupported database scheme %v for database backend sqlite3", o.DatabaseURL.Scheme)
}
return nil
}
func NewSQLite(opts SQLiteOpts) (Repository, error) {
if err := opts.Validate(); err != nil {
return nil, err
}
// Create directory if not exist
if _, err := os.Stat(filepath.Dir(opts.DatabaseURL.Path)); os.IsNotExist(err) {
/* #nosec */
err := os.MkdirAll(filepath.Dir(opts.DatabaseURL.Path), 0755)
if err != nil {
return nil, err
}
}
// enable foreign keys
values := opts.DatabaseURL.Query()
values.Set("_foreign_keys", "on")
customRawURL := fmt.Sprintf("file://%v?%v", opts.DatabaseURL.Path, values.Encode())
sqlDBO, err := sql.Open(opts.DatabaseURL.Scheme, customRawURL)
if err != nil {
return nil, err
}
return &SQLite{
databaseURL: opts.DatabaseURL,
dbo: sqlDBO,
flogger: opts.Logger,
mutex: new(sync.Mutex),
}, nil
}
// SQLite implementation
type SQLite struct {
databaseURL *url.URL
dbo *sql.DB
flogger logger.Logger
mutex *sync.Mutex
}
// AddDevices into the database
func (d *SQLite) AddDevices(ctx context.Context, devices ...*types.Device) error {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
err = d.insertDevices(tx, devices...)
if err != nil {
return err
}
return tx.Commit()
}
func (d *SQLite) insertDevices(tx *sql.Tx, devices ...*types.Device) error {
stmt, err := tx.Prepare(sqlite3.InsertDeviceSQL)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer func() { _ = stmt.Close() }()
for _, device := range devices {
_, err = stmt.Exec(
&device.ID,
&device.Name,
&device.Location,
&device.CreationDate,
&device.UpdateDate,
)
if err != nil {
return fmt.Errorf("Failed to execute statement: %v", err)
}
}
return nil
}
// AddOrUpdateDevices into the database
func (d *SQLite) AddOrUpdateDevices(ctx context.Context, devices ...*types.Device) error {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
err = d.insertOrUpdateDevices(tx, devices...)
if err != nil {
return err
}
return tx.Commit()
}
func (d *SQLite) insertOrUpdateDevices(tx *sql.Tx, devices ...*types.Device) error {
stmt, err := tx.Prepare(sqlite3.InsertOrUpdateDeviceSQL)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer func() { _ = stmt.Close() }()
for _, device := range devices {
_, err = stmt.Exec(
&device.ID,
&device.Name,
&device.Location,
&device.CreationDate,
&device.UpdateDate,
)
if err != nil {
return fmt.Errorf("Failed to execute statement: %v", err)
}
}
return nil
}
// AddMeasuredValues into the database
func (d *SQLite) AddMeasuredValues(ctx context.Context, measuredValues ...*types.MeasuredValue) error {
d.mutex.Lock()
defer d.mutex.Unlock()
splittedMeasuredValues := make(map[types.MeasuredValueType][]*types.MeasuredValue, 0)
for _, measuredValue := range measuredValues {
if _, ok := splittedMeasuredValues[measuredValue.ValueType]; !ok {
splittedMeasuredValues[measuredValue.ValueType] = make([]*types.MeasuredValue, 0)
}
splittedMeasuredValues[measuredValue.ValueType] = append(splittedMeasuredValues[measuredValue.ValueType], measuredValue)
}
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
for measuredValueType, measuredValues := range splittedMeasuredValues {
var query string
switch measuredValueType {
case types.Humidity:
query = sqlite3.InsertHumiditySQL
case types.Pressure:
query = sqlite3.InsertPressureSQL
case types.Temperature:
query = sqlite3.InsertTemperatureSQL
default:
return fmt.Errorf("Measured value type %v not supported", measuredValueType)
}
err := d.insertMeasuredValues(tx, query, measuredValues...)
if err != nil {
return err
}
}
return tx.Commit()
}
func (d *SQLite) insertMeasuredValues(tx *sql.Tx, query string, measuredValues ...*types.MeasuredValue) error {
stmt, err := tx.Prepare(query)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer func() { _ = stmt.Close() }()
for _, measuredValue := range measuredValues {
_, err := stmt.Exec(
&measuredValue.ID,
&measuredValue.Value,
&measuredValue.Date,
&measuredValue.SensorID,
&measuredValue.CreationDate,
&measuredValue.UpdateDate,
)
if err != nil {
return fmt.Errorf("Failed to execute statement: %v", err)
}
}
return nil
}
// AddOrUpdateMeasuredValues into the database
func (d *SQLite) AddOrUpdateMeasuredValues(ctx context.Context, measuredValues ...*types.MeasuredValue) error {
d.mutex.Lock()
defer d.mutex.Unlock()
splittedMeasuredValues := make(map[types.MeasuredValueType][]*types.MeasuredValue, 0)
for _, measuredValue := range measuredValues {
if _, ok := splittedMeasuredValues[measuredValue.ValueType]; !ok {
splittedMeasuredValues[measuredValue.ValueType] = make([]*types.MeasuredValue, 0)
}
splittedMeasuredValues[measuredValue.ValueType] = append(splittedMeasuredValues[measuredValue.ValueType], measuredValue)
}
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
for measuredValueType, measuredValues := range splittedMeasuredValues {
var query string
switch measuredValueType {
case types.Humidity:
query = sqlite3.InsertOrUpdateHumiditySQL
case types.Pressure:
query = sqlite3.InsertOrUpdatePressureSQL
case types.Temperature:
query = sqlite3.InsertOrUpdateTemperatureSQL
default:
return fmt.Errorf("Measured value type %v not supported", measuredValueType)
}
err := d.insertOrUpdateMeasuredValues(tx, query, measuredValues...)
if err != nil {
return err
}
}
return tx.Commit()
}
func (d *SQLite) insertOrUpdateMeasuredValues(tx *sql.Tx, query string, measuredValues ...*types.MeasuredValue) error {
stmt, err := tx.Prepare(query)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer func() { _ = stmt.Close() }()
for _, measuredValue := range measuredValues {
_, err := stmt.Exec(
&measuredValue.ID,
&measuredValue.Value,
&measuredValue.Date,
&measuredValue.SensorID,
&measuredValue.CreationDate,
&measuredValue.UpdateDate,
)
if err != nil {
return fmt.Errorf("Failed to execute statement: %v", err)
}
}
return nil
}
// AddSensors into the database
func (d *SQLite) AddSensors(ctx context.Context, sensors ...*types.Sensor) error {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
stmt, err := tx.Prepare(sqlite3.InsertSensorSQL)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer func() { _ = stmt.Close() }()
for _, sensor := range sensors {
_, err = stmt.Exec(
&sensor.ID,
&sensor.Name,
&sensor.Location,
&sensor.WireID,
&sensor.I2CBus,
&sensor.I2CAddress,
&sensor.GPIONumber,
&sensor.Model,
&sensor.Enabled,
&sensor.TickDuration,
&sensor.DeviceID,
&sensor.CreationDate,
&sensor.UpdateDate,
)
if err != nil {
return fmt.Errorf("Failed to execute statement: %v", err)
}
}
return tx.Commit()
}
// AddOrUpdateSensors into the database
func (d *SQLite) AddOrUpdateSensors(ctx context.Context, sensors ...*types.Sensor) error {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
err = d.insertOrUpdateSensors(tx, sensors...)
if err != nil {
return err
}
return tx.Commit()
}
func (d *SQLite) insertOrUpdateSensors(tx *sql.Tx, sensors ...*types.Sensor) error {
stmt, err := tx.Prepare(sqlite3.InsertOrUpdateSensorSQL)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer func() { _ = stmt.Close() }()
for _, sensor := range sensors {
_, err = stmt.Exec(
&sensor.ID,
&sensor.Name,
&sensor.Location,
&sensor.WireID,
&sensor.I2CBus,
&sensor.I2CAddress,
&sensor.GPIONumber,
&sensor.Model,
&sensor.Enabled,
&sensor.TickDuration,
&sensor.DeviceID,
&sensor.CreationDate,
&sensor.UpdateDate,
)
if err != nil {
return fmt.Errorf("Failed to execute statement: %v", err)
}
}
return nil
}
// Close closes the database and prevents new queries from starting. Close then
// waits for all queries that have started processing on the server to finish.
func (d *SQLite) Close() error {
d.mutex.Lock()
defer d.mutex.Unlock()
return d.dbo.Close()
}
// Migrate creates all required tables if not exist
func (d *SQLite) Migrate(ctx context.Context) error {
d.mutex.Lock()
defer d.mutex.Unlock()
sourceDriver, err := iofs.New(sqlite3.DDLAssets, sqlite3.DDLAssetPath)
if err != nil {
return err
}
m, err := migrate.NewWithSourceInstance("iofs", sourceDriver, d.databaseURL.String())
if err != nil {
return err
}
err = m.Up()
switch {
case errors.Is(err, migrate.ErrNoChange):
return nil
default:
return err
}
}
// GetDevice from database
func (d *SQLite) GetDeviceByID(ctx context.Context, id string) (*types.Device, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
devices, err := d.selectDevices(tx, sqlite3.SelectDeviceByIDSQL, id)
if err != nil {
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, err
}
if len(devices) == 0 {
return nil, nil
}
return devices[0], nil
}
// GetDevice from database
func (d *SQLite) GetDeviceByName(ctx context.Context, name string) (*types.Device, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
devices, err := d.selectDevices(tx, sqlite3.SelectDeviceByNameSQL, name)
if err != nil {
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, err
}
if len(devices) == 0 {
return nil, nil
}
return devices[0], nil
}
// GetDevices from the database
func (d *SQLite) GetDevices(ctx context.Context) ([]*types.Device, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
devices, err := d.selectDevices(tx, sqlite3.SelectDevicesSQL)
if err != nil {
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, fmt.Errorf("Failed to commit transaction: %v", err)
}
return devices, nil
}
func (d *SQLite) selectDevices(tx *sql.Tx, query string, args ...interface{}) ([]*types.Device, error) {
stmt, err := tx.Prepare(query)
if err != nil {
return nil, fmt.Errorf("Failed to prepare statement: %v", err)
}
defer func() { _ = stmt.Close() }()
rows, err := stmt.Query(args...)
if err != nil {
return nil, fmt.Errorf("Failed to query statement: %v", err)
}
defer func() { _ = rows.Close() }()
devices := make([]*types.Device, 0)
for rows.Next() {
device := new(types.Device)
err = rows.Scan(
&device.ID,
&device.Name,
&device.Location,
&device.CreationDate,
&device.UpdateDate,
)
if err != nil {
return nil, fmt.Errorf("Failed to scan row: %v", err)
}
devices = append(devices, device)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("failed to scan rows: %v", err)
}
return devices, nil
}
// GetHumidity returns humidity from the database
func (d *SQLite) GetHumidityByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, err
}
defer func() { _ = tx.Rollback() }()
measuredValues, err := d.selectMeasuredValue(tx, sqlite3.SelectHumidityByIDSQL, id)
if err != nil {
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, err
}
if measuredValues == nil {
return nil, nil
}
for _, measuredValue := range measuredValues {
measuredValue.ValueType = types.Humidity
}
return measuredValues[0], nil
}
// GetHumidities returns humidities from the database
func (d *SQLite) GetHumidities(ctx context.Context) ([]*types.MeasuredValue, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, err
}
defer func() { _ = tx.Rollback() }()
measuredValues, err := d.selectMeasuredValue(tx, sqlite3.SelectHumiditiesSQL)
if err != nil {
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, err
}
for _, measuredValue := range measuredValues {
measuredValue.ValueType = types.Humidity
}
return measuredValues, nil
}
func (d *SQLite) selectMeasuredValue(tx *sql.Tx, query string, args ...interface{}) ([]*types.MeasuredValue, error) {
stmt, err := tx.Prepare(query)
if err != nil {
return nil, err
}
defer func() { _ = stmt.Close() }()
rows, err := stmt.Query(args...)
if err != nil {
return nil, err
}
defer func() { _ = rows.Close() }()
measuredValues := make([]*types.MeasuredValue, 0)
for rows.Next() {
measuredValue := new(types.MeasuredValue)
err := rows.Scan(
&measuredValue.ID,
&measuredValue.Value,
&measuredValue.Date,
&measuredValue.SensorID,
&measuredValue.CreationDate,
&measuredValue.UpdateDate,
)
if err != nil {
return nil, err
}
measuredValues = append(measuredValues, measuredValue)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("failed to scan rows: %v", err)
}
return measuredValues, nil
}
// GetPressure returns pressure from the database
func (d *SQLite) GetPressureByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, err
}
defer func() { _ = tx.Rollback() }()
measuredValues, err := d.selectMeasuredValue(tx, sqlite3.SelectPressureByIDSQL, id)
if err != nil {
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, err
}
if measuredValues == nil {
return nil, nil
}
for _, measuredValue := range measuredValues {
measuredValue.ValueType = types.Pressure
}
return measuredValues[0], nil
}
// GetPressures returns pressure from the database
func (d *SQLite) GetPressures(ctx context.Context) ([]*types.MeasuredValue, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, err
}
defer func() { _ = tx.Rollback() }()
measuredValues, err := d.selectMeasuredValue(tx, sqlite3.SelectPressuresSQL)
if err != nil {
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, err
}
for _, measuredValue := range measuredValues {
measuredValue.ValueType = types.Pressure
}
return measuredValues, nil
}
// GetSensor from database
func (d *SQLite) GetSensorByID(ctx context.Context, id string) (*types.Sensor, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
sensors, err := d.selectSensors(tx, sqlite3.SelectSensorByIDSQL, id)
if err != nil {
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, fmt.Errorf("Failed to commit transaction: %v", err)
}
if len(sensors) == 0 {
return nil, nil
}
return sensors[0], nil
}
// GetSensors from the database
func (d *SQLite) GetSensors(ctx context.Context) ([]*types.Sensor, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
sensors, err := d.selectSensors(tx, sqlite3.SelectSensorsSQL)
if err != nil {
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, fmt.Errorf("Failed to commit transaction: %v", err)
}
return sensors, nil
}
// GetSensorsByModels from the database
func (d *SQLite) GetSensorsByDeviceIDs(ctx context.Context, deviceIDs ...string) ([]*types.Sensor, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
cachedSensors := make([]*types.Sensor, 0)
for i := range deviceIDs {
sensors, err := d.selectSensors(tx, sqlite3.SelectSensorsByDeviceIDSQL, deviceIDs[i])
if err != nil {
return nil, err
}
cachedSensors = append(cachedSensors, sensors...)
}
err = tx.Commit()
if err != nil {
return nil, fmt.Errorf("Failed to commit transaction: %v", err)
}
return cachedSensors, nil
}
// GetSensorsByModels from the database
func (d *SQLite) GetSensorsByModels(ctx context.Context, sensorModels ...string) ([]*types.Sensor, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
cachedSensors := make([]*types.Sensor, 0)
for i := range sensorModels {
sensors, err := d.selectSensors(tx, sqlite3.SelectSensorsByModelSQL, sensorModels[i])
if err != nil {
return nil, err
}
cachedSensors = append(cachedSensors, sensors...)
}
err = tx.Commit()
if err != nil {
return nil, fmt.Errorf("Failed to commit transaction: %v", err)
}
return cachedSensors, nil
}
// GetSensorsByModels from the database
func (d *SQLite) GetSensorsByNames(ctx context.Context, sensorNames ...string) ([]*types.Sensor, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
cachedSensors := make([]*types.Sensor, 0)
for i := range sensorNames {
sensors, err := d.selectSensors(tx, sqlite3.SelectSensorsByNameSQL, sensorNames[i])
if err != nil {
return nil, err
}
cachedSensors = append(cachedSensors, sensors...)
}
err = tx.Commit()
if err != nil {
return nil, fmt.Errorf("Failed to commit transaction: %v", err)
}
return cachedSensors, nil
}
func (d *SQLite) selectSensors(tx *sql.Tx, query string, args ...interface{}) ([]*types.Sensor, error) {
stmt, err := tx.Prepare(query)
if err != nil {
return nil, fmt.Errorf("Failed to prepare statement: %v", err)
}
defer func() { _ = stmt.Close() }()
rows, err := stmt.Query(args...)
if err != nil {
return nil, fmt.Errorf("Failed to query statement: %v", err)
}
defer func() { _ = rows.Close() }()
sensors := make([]*types.Sensor, 0)
for rows.Next() {
sensor := new(types.Sensor)
err = rows.Scan(
&sensor.ID,
&sensor.Name,
&sensor.Location,
&sensor.WireID,
&sensor.I2CBus,
&sensor.I2CAddress,
&sensor.GPIONumber,
&sensor.Model,
&sensor.Enabled,
&sensor.TickDuration,
&sensor.DeviceID,
&sensor.CreationDate,
&sensor.UpdateDate,
)
if err != nil {
return nil, fmt.Errorf("Failed to scan row: %v", err)
}
sensors = append(sensors, sensor)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("failed to scan rows: %v", err)
}
return sensors, nil
}
// GetTemperature returns temperatures from the database
func (d *SQLite) GetTemperatureByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, err
}
defer func() { _ = tx.Rollback() }()
measuredValues, err := d.selectMeasuredValue(tx, sqlite3.SelectTemperatureByIDSQL, id)
if err != nil {
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, err
}
if measuredValues == nil {
return nil, nil
}
for _, measuredValue := range measuredValues {
measuredValue.ValueType = types.Temperature
}
return measuredValues[0], nil
}
// GetTemperatures returns temperatures from the database
func (d *SQLite) GetTemperatures(ctx context.Context) ([]*types.MeasuredValue, error) {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
return nil, err
}
defer func() { _ = tx.Rollback() }()
measuredValues, err := d.selectMeasuredValue(tx, sqlite3.SelectTemperaturesSQL)
if err != nil {
return nil, err
}
err = tx.Commit()
if err != nil {
return nil, err
}
for _, measuredValue := range measuredValues {
measuredValue.ValueType = types.Temperature
}
return measuredValues, nil
}
// Import imports devices, sensors and all measured values from a source
// repository. Existing entries will be updated.
func (d *SQLite) Import(ctx context.Context, src Repository) error {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return err
}
defer func() { _ = tx.Rollback() }()
devices, err := src.GetDevices(ctx)
if err != nil {
return err
}
err = d.insertOrUpdateDevices(tx, devices...)
if err != nil {
return err
}
sensors, err := src.GetSensors(ctx)
if err != nil {
return err
}
err = d.insertOrUpdateSensors(tx, sensors...)
if err != nil {
return err
}
for _, value := range []struct {
f func(ctx context.Context) ([]*types.MeasuredValue, error)
query string
}{
{f: src.GetHumidities, query: postgres.InsertOrUpdateHumiditySQL},
{f: src.GetPressures, query: postgres.InsertOrUpdatePressureSQL},
{f: src.GetTemperatures, query: postgres.InsertOrUpdateTemperatureSQL},
} {
measuredValues, err := value.f(ctx)
if err != nil {
return err
}
err = d.insertOrUpdateMeasuredValues(tx, value.query, measuredValues...)
if err != nil {
return err
}
}
return tx.Commit()
}
// RemoveDevices from the database
func (d *SQLite) RemoveDevicesByIDs(ctx context.Context, deviceIDs ...string) error {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
stmt, err := tx.Prepare(sqlite3.DeleteDeviceByIDSQL)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer func() { _ = stmt.Close() }()
for _, deviceID := range deviceIDs {
_, err = stmt.Exec(deviceID)
if err != nil {
return err
}
}
return tx.Commit()
}
// RemoveDevices from the database
func (d *SQLite) RemoveDevicesByNames(ctx context.Context, names ...string) error {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
stmt, err := tx.Prepare(sqlite3.DeleteDeviceByNameSQL)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer func() { _ = stmt.Close() }()
for _, deviceID := range names {
_, err = stmt.Exec(deviceID)
if err != nil {
return err
}
}
return tx.Commit()
}
// RemoveSensors from the database
func (d *SQLite) RemoveSensorsByIDs(ctx context.Context, sensorIDs ...string) error {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
stmt, err := tx.Prepare(sqlite3.DeleteSensorByIDSQL)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer func() { _ = stmt.Close() }()
for _, sensorID := range sensorIDs {
_, err = stmt.Exec(sensorID)
if err != nil {
return err
}
}
return tx.Commit()
}
// RemoveSensors from the database
func (d *SQLite) RemoveSensorsByNames(ctx context.Context, names ...string) error {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer func() { _ = tx.Rollback() }()
stmt, err := tx.Prepare(sqlite3.DeleteSensorByNameSQL)
if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer func() { _ = stmt.Close() }()
for _, sensorID := range names {
_, err = stmt.Exec(sensorID)
if err != nil {
return err
}
}
return tx.Commit()
}
// UpdateDevices updates a device in the database
func (d *SQLite) UpdateDevices(ctx context.Context, devices ...*types.Device) error {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return err
}
defer func() { _ = tx.Rollback() }()
stmt, err := tx.Prepare(sqlite3.UpdateDeviceSQL)
if err != nil {
return err
}
defer func() { _ = stmt.Close() }()
for _, device := range devices {
_, err := stmt.Exec(
&device.Name,
&device.Location,
&device.CreationDate,
&device.UpdateDate,
&device.ID,
)
if err != nil {
return err
}
}
return tx.Commit()
}
// UpdateSensors updates a sensor in the database
func (d *SQLite) UpdateSensors(ctx context.Context, sensors ...*types.Sensor) error {
d.mutex.Lock()
defer d.mutex.Unlock()
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return err
}
defer func() { _ = tx.Rollback() }()
stmt, err := tx.Prepare(sqlite3.UpdateSensorSQL)
if err != nil {
return err
}
defer func() { _ = stmt.Close() }()
for _, sensor := range sensors {
_, err := stmt.Exec(
&sensor.Name,
&sensor.Location,
&sensor.WireID,
&sensor.I2CBus,
&sensor.I2CAddress,
&sensor.GPIONumber,
&sensor.Model,
&sensor.Enabled,
&sensor.TickDuration,
&sensor.DeviceID,
&sensor.CreationDate,
&sensor.UpdateDate,
&sensor.ID,
)
if err != nil {
return err
}
}
return tx.Commit()
}