feat: import from sqlite or postgres

This commit is contained in:
Markus Pesch 2021-04-09 16:29:09 +02:00
parent 8c2090a316
commit 749f2697c7
Signed by: volker.raschek
GPG Key ID: 852BCC170D81A982
6 changed files with 772 additions and 608 deletions

View File

@ -1,6 +1,13 @@
package imp package imp
import ( import (
"context"
"fmt"
"net/url"
"git.cryptic.systems/volker.raschek/flucky/pkg/config"
"git.cryptic.systems/volker.raschek/flucky/pkg/repository"
"git.cryptic.systems/volker.raschek/go-logger"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -15,61 +22,74 @@ func InitCmd(cmd *cobra.Command) error {
importCmd := &cobra.Command{ importCmd := &cobra.Command{
Use: "import", Use: "import",
Args: cobra.RangeArgs(1, 2),
Short: "Import data from passed URL", Short: "Import data from passed URL",
Example: `import sqlite3:///var/cache/flucky/sqlite3.db
import sqlite3:///var/cache/flucky/sqlite3.db postgres://user:password@host:port/database?sslmode=disable`,
RunE: importSources, RunE: importSources,
} }
importCmd.Flags().BoolVar(&importSensors, "sensors", true, "Import sensors")
importCmd.Flags().BoolVar(&importHumidities, "humidities", true, "Import humidities")
importCmd.Flags().BoolVar(&importPressures, "pressures", true, "Import pressures")
importCmd.Flags().BoolVar(&importTemperatures, "temperatures", true, "Import temperatures")
cmd.AddCommand(importCmd) cmd.AddCommand(importCmd)
return nil return nil
} }
func importSources(cmd *cobra.Command, args []string) error { func importSources(cmd *cobra.Command, args []string) error {
// configFile, err := cmd.Flags().GetString("config")
// if err != nil {
// return fmt.Errorf("No config file defined")
// }
// cnf, err := config.Read(configFile) configFile, err := cmd.Flags().GetString("config")
// if err != nil { if err != nil {
// return err return fmt.Errorf("No config file defined")
// } }
// destURL, err := url.Parse(cnf.DSN) cnf, err := config.Read(configFile)
// if err != nil { if err != nil {
// return err return err
// } }
// logLevelString, err := cmd.Flags().GetString("loglevel") logLevelString, err := cmd.Flags().GetString("loglevel")
// if err != nil { if err != nil {
// return err return err
// } }
// logLevel, err := logger.ParseLogLevel(logLevelString) logLevel, err := logger.ParseLogLevel(logLevelString)
// if err != nil { if err != nil {
// return err return err
// } }
// flogger := logger.NewLogger(logLevel) flogger := logger.NewLogger(logLevel)
// sourceURL, err := url.Parse(args[0]) var (
// if err != nil { srcURL *url.URL
// return fmt.Errorf("Failed to parse source url: %w", err) destURL *url.URL
// } )
// err = repository.Import(sourceURL, destURL, flogger, repository.OptImport{ srcURL, err = url.Parse(args[0])
// Sensors: importSensors, if err != nil {
// Humidities: importHumidities, return err
// Pressures: importPressures, }
// Temperatures: importTemperatures,
// }) switch len(args) {
// if err != nil { case 1:
// return fmt.Errorf("Failed to import: %w", err) destURL, err = url.Parse(cnf.DSN)
// } if err != nil {
return err
return nil }
case 2:
destURL, err = url.Parse(args[1])
if err != nil {
return err
}
}
srcRepository, err := repository.New(srcURL, flogger)
if err != nil {
return err
}
destRepository, err := repository.New(destURL, flogger)
if err != nil {
return err
}
return destRepository.Import(context.Background(), srcRepository)
} }

View File

@ -1,121 +1,26 @@
package postgres package repository
import ( import (
"context" "context"
"database/sql" "database/sql"
"embed"
"errors" "errors"
"fmt" "fmt"
"net/url" "net/url"
"time" "time"
"git.cryptic.systems/volker.raschek/flucky/pkg/repository/postgres"
"git.cryptic.systems/volker.raschek/flucky/pkg/types" "git.cryptic.systems/volker.raschek/flucky/pkg/types"
"git.cryptic.systems/volker.raschek/go-logger" "git.cryptic.systems/volker.raschek/go-logger"
"github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4"
"github.com/johejo/golang-migrate-extra/source/iofs" "github.com/johejo/golang-migrate-extra/source/iofs"
) )
var ( type PostgresOpts struct {
//go:embed ddl/*.sql
ddlAssets embed.FS
//go:embed dml/deleteDeviceByID.sql
deleteDeviceByIDSQL string
//go:embed dml/deleteDeviceByName.sql
deleteDeviceByNameSQL string
//go:embed dml/deleteSensorByID.sql
deleteSensorByIDSQL string
//go:embed dml/deleteSensorByName.sql
deleteSensorByNameSQL string
//go:embed dml/insertDevice.sql
insertDeviceSQL string
//go:embed dml/insertHumidity.sql
insertHumiditySQL string
//go:embed dml/insertOrUpdateDevice.sql
insertOrUpdateDeviceSQL string
//go:embed dml/insertOrUpdateHumidity.sql
insertOrUpdateHumiditySQL string
//go:embed dml/insertOrUpdatePressure.sql
insertOrUpdatePressureSQL string
//go:embed dml/insertOrUpdateSensor.sql
insertOrUpdateSensorSQL string
//go:embed dml/insertOrUpdateTemperature.sql
insertOrUpdateTemperatureSQL string
//go:embed dml/insertPressure.sql
insertPressureSQL string
//go:embed dml/insertSensor.sql
insertSensorSQL string
//go:embed dml/insertTemperature.sql
insertTemperatureSQL string
//go:embed dml/selectDeviceByID.sql
selectDeviceByIDSQL string
//go:embed dml/selectDeviceByName.sql
selectDeviceByNameSQL string
//go:embed dml/selectDevices.sql
selectDevicesSQL string
//go:embed dml/selectHumidities.sql
selectHumiditiesSQL string
//go:embed dml/selectHumidityByID.sql
selectHumidityByIDSQL string
//go:embed dml/selectPressureByID.sql
selectPressureByIDSQL string
//go:embed dml/selectPressures.sql
selectPressuresSQL string
//go:embed dml/selectSensorByID.sql
selectSensorByIDSQL string
//go:embed dml/selectSensors.sql
selectSensorsSQL string
//go:embed dml/selectSensorsByDeviceID.sql
selectSensorsByDeviceIDSQL string
//go:embed dml/selectSensorsByModel.sql
selectSensorsByModelSQL string
//go:embed dml/selectSensorsByName.sql
selectSensorsByNameSQL string
//go:embed dml/selectTemperatureByID.sql
selectTemperatureByIDSQL string
//go:embed dml/selectTemperatures.sql
selectTemperaturesSQL string
//go:embed dml/updateDevice.sql
updateDeviceSQL string
//go:embed dml/updateSensor.sql
updateSensorSQL string
)
type Opts struct {
DatabaseURL *url.URL DatabaseURL *url.URL
Logger logger.Logger Logger logger.Logger
} }
func (o *Opts) Validate() error { func (o *PostgresOpts) Validate() error {
for k, v := range map[string]interface{}{ for k, v := range map[string]interface{}{
"DatabaseURL": o.DatabaseURL, "DatabaseURL": o.DatabaseURL,
"Logger": o.Logger, "Logger": o.Logger,
@ -132,7 +37,7 @@ func (o *Opts) Validate() error {
return nil return nil
} }
func New(opts Opts) (*Postgres, error) { func NewPostgres(opts PostgresOpts) (Repository, error) {
if err := opts.Validate(); err != nil { if err := opts.Validate(); err != nil {
return nil, err return nil, err
} }
@ -157,14 +62,14 @@ type Postgres struct {
} }
// AddDevices into the database // AddDevices into the database
func (postgres *Postgres) AddDevices(ctx context.Context, devices ...*types.Device) error { func (d *Postgres) AddDevices(ctx context.Context, devices ...*types.Device) error {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
err = postgres.insertDevices(tx, devices...) err = d.insertDevices(tx, devices...)
if err != nil { if err != nil {
return err return err
} }
@ -172,8 +77,8 @@ func (postgres *Postgres) AddDevices(ctx context.Context, devices ...*types.Devi
return tx.Commit() return tx.Commit()
} }
func (postgres *Postgres) insertDevices(tx *sql.Tx, devices ...*types.Device) error { func (d *Postgres) insertDevices(tx *sql.Tx, devices ...*types.Device) error {
stmt, err := tx.Prepare(insertDeviceSQL) stmt, err := tx.Prepare(postgres.InsertDeviceSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -200,14 +105,23 @@ func (postgres *Postgres) insertDevices(tx *sql.Tx, devices ...*types.Device) er
return nil return nil
} }
func (postgres *Postgres) AddOrUpdateDevices(ctx context.Context, devices ...*types.Device) error { func (d *Postgres) AddOrUpdateDevices(ctx context.Context, devices ...*types.Device) error {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(insertOrUpdateDeviceSQL) err = d.insertOrUpdateDevices(tx, devices...)
if err != nil {
return err
}
return tx.Commit()
}
func (d *Postgres) insertOrUpdateDevices(tx *sql.Tx, devices ...*types.Device) error {
stmt, err := tx.Prepare(postgres.InsertOrUpdateDeviceSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %w", err) return fmt.Errorf("Failed to prepare statement: %w", err)
} }
@ -231,11 +145,11 @@ func (postgres *Postgres) AddOrUpdateDevices(ctx context.Context, devices ...*ty
} }
} }
return tx.Commit() return nil
} }
// AddMeasuredValues into the database // AddMeasuredValues into the database
func (postgres *Postgres) AddMeasuredValues(ctx context.Context, measuredValues ...*types.MeasuredValue) error { func (d *Postgres) AddMeasuredValues(ctx context.Context, measuredValues ...*types.MeasuredValue) error {
splittedMeasuredValues := make(map[types.MeasuredValueType][]*types.MeasuredValue, 0) splittedMeasuredValues := make(map[types.MeasuredValueType][]*types.MeasuredValue, 0)
for _, measuredValue := range measuredValues { for _, measuredValue := range measuredValues {
@ -245,14 +159,36 @@ func (postgres *Postgres) AddMeasuredValues(ctx context.Context, measuredValues
splittedMeasuredValues[measuredValue.ValueType] = append(splittedMeasuredValues[measuredValue.ValueType], measuredValue) splittedMeasuredValues[measuredValue.ValueType] = append(splittedMeasuredValues[measuredValue.ValueType], measuredValue)
} }
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
// General insert function for measuredValueType, measuredValues := range splittedMeasuredValues {
insert := func(tx *sql.Tx, query string, measuredValues []*types.MeasuredValue) error { var queryFile string
switch measuredValueType {
case types.Humidity:
queryFile = postgres.InsertHumiditySQL
case types.Pressure:
queryFile = postgres.InsertPressureSQL
case types.Temperature:
queryFile = postgres.InsertTemperatureSQL
default:
return fmt.Errorf("Measured value type %v not supported", measuredValueType)
}
err := d.insertMeasuredValues(tx, queryFile, measuredValues...)
if err != nil {
return err
}
}
return tx.Commit()
}
func (d *Postgres) insertMeasuredValues(tx *sql.Tx, query string, measuredValues ...*types.MeasuredValue) error {
stmt, err := tx.Prepare(query) stmt, err := tx.Prepare(query)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
@ -282,31 +218,8 @@ func (postgres *Postgres) AddMeasuredValues(ctx context.Context, measuredValues
return nil return nil
} }
for measuredValueType, measuredValues := range splittedMeasuredValues {
var queryFile string
switch measuredValueType {
case types.Humidity:
queryFile = insertHumiditySQL
case types.Pressure:
queryFile = insertPressureSQL
case types.Temperature:
queryFile = insertTemperatureSQL
default:
return fmt.Errorf("Measured value type %v not supported", measuredValueType)
}
err := insert(tx, queryFile, measuredValues)
if err != nil {
return err
}
}
return tx.Commit()
}
// AddOrUpdateMeasuredValues into the database // AddOrUpdateMeasuredValues into the database
func (postgres *Postgres) AddOrUpdateMeasuredValues(ctx context.Context, measuredValues ...*types.MeasuredValue) error { func (d *Postgres) AddOrUpdateMeasuredValues(ctx context.Context, measuredValues ...*types.MeasuredValue) error {
splittedMeasuredValues := make(map[types.MeasuredValueType][]*types.MeasuredValue, 0) splittedMeasuredValues := make(map[types.MeasuredValueType][]*types.MeasuredValue, 0)
for _, measuredValue := range measuredValues { for _, measuredValue := range measuredValues {
@ -316,14 +229,36 @@ func (postgres *Postgres) AddOrUpdateMeasuredValues(ctx context.Context, measure
splittedMeasuredValues[measuredValue.ValueType] = append(splittedMeasuredValues[measuredValue.ValueType], measuredValue) splittedMeasuredValues[measuredValue.ValueType] = append(splittedMeasuredValues[measuredValue.ValueType], measuredValue)
} }
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
// General insert function for measuredValueType, measuredValues := range splittedMeasuredValues {
insert := func(tx *sql.Tx, query string, measuredValues []*types.MeasuredValue) error { var queryFile string
switch measuredValueType {
case types.Humidity:
queryFile = postgres.InsertOrUpdateHumiditySQL
case types.Pressure:
queryFile = postgres.InsertOrUpdatePressureSQL
case types.Temperature:
queryFile = postgres.InsertOrUpdateTemperatureSQL
default:
return fmt.Errorf("Measured value type %v not supported", measuredValueType)
}
err := d.insertOrUpdateMeasuredValues(tx, queryFile, measuredValues...)
if err != nil {
return err
}
}
return tx.Commit()
}
func (d *Postgres) insertOrUpdateMeasuredValues(tx *sql.Tx, query string, measuredValues ...*types.MeasuredValue) error {
stmt, err := tx.Prepare(query) stmt, err := tx.Prepare(query)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
@ -353,38 +288,15 @@ func (postgres *Postgres) AddOrUpdateMeasuredValues(ctx context.Context, measure
return nil return nil
} }
for measuredValueType, measuredValues := range splittedMeasuredValues {
var queryFile string
switch measuredValueType {
case types.Humidity:
queryFile = insertOrUpdateHumiditySQL
case types.Pressure:
queryFile = insertOrUpdatePressureSQL
case types.Temperature:
queryFile = insertOrUpdateTemperatureSQL
default:
return fmt.Errorf("Measured value type %v not supported", measuredValueType)
}
err := insert(tx, queryFile, measuredValues)
if err != nil {
return err
}
}
return tx.Commit()
}
// AddSensors into the database // AddSensors into the database
func (postgres *Postgres) AddSensors(ctx context.Context, sensors ...*types.Sensor) error { func (d *Postgres) AddSensors(ctx context.Context, sensors ...*types.Sensor) error {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(insertSensorSQL) stmt, err := tx.Prepare(postgres.InsertSensorSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -420,14 +332,23 @@ func (postgres *Postgres) AddSensors(ctx context.Context, sensors ...*types.Sens
} }
// AddOrUpdateSensors into the database // AddOrUpdateSensors into the database
func (postgres *Postgres) AddOrUpdateSensors(ctx context.Context, sensors ...*types.Sensor) error { func (d *Postgres) AddOrUpdateSensors(ctx context.Context, sensors ...*types.Sensor) error {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(insertOrUpdateSensorSQL) err = d.insertOrUpdateSensors(tx, sensors...)
if err != nil {
return err
}
return tx.Commit()
}
func (d *Postgres) insertOrUpdateSensors(tx *sql.Tx, sensors ...*types.Sensor) error {
stmt, err := tx.Prepare(postgres.InsertOrUpdateSensorSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -459,7 +380,7 @@ func (postgres *Postgres) AddOrUpdateSensors(ctx context.Context, sensors ...*ty
} }
} }
return tx.Commit() return nil
} }
// Close closes the database and prevents new queries from starting. Close then // Close closes the database and prevents new queries from starting. Close then
@ -469,13 +390,13 @@ func (postgres *Postgres) Close() error {
} }
// Migrate creates all required tables if not exist // Migrate creates all required tables if not exist
func (postgres *Postgres) Migrate(ctx context.Context) error { func (d *Postgres) Migrate(ctx context.Context) error {
sourceDriver, err := iofs.New(ddlAssets, "ddl") sourceDriver, err := iofs.New(postgres.DDLAssets, postgres.DDLAssetPath)
if err != nil { if err != nil {
return err return err
} }
m, err := migrate.NewWithSourceInstance("iofs", sourceDriver, postgres.databaseURL.String()) m, err := migrate.NewWithSourceInstance("iofs", sourceDriver, d.databaseURL.String())
if err != nil { if err != nil {
return err return err
} }
@ -490,14 +411,14 @@ func (postgres *Postgres) Migrate(ctx context.Context) error {
} }
// GetDevice from database // GetDevice from database
func (postgres *Postgres) GetDeviceByID(ctx context.Context, id string) (*types.Device, error) { func (d *Postgres) GetDeviceByID(ctx context.Context, id string) (*types.Device, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
devices, err := postgres.selectDevices(tx, selectDeviceByIDSQL, id) devices, err := d.selectDevices(tx, postgres.SelectDeviceByIDSQL, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -515,14 +436,14 @@ func (postgres *Postgres) GetDeviceByID(ctx context.Context, id string) (*types.
} }
// GetDevice from database // GetDevice from database
func (postgres *Postgres) GetDeviceByName(ctx context.Context, name string) (*types.Device, error) { func (d *Postgres) GetDeviceByName(ctx context.Context, name string) (*types.Device, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
devices, err := postgres.selectDevices(tx, selectDeviceByNameSQL, name) devices, err := d.selectDevices(tx, postgres.SelectDeviceByNameSQL, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -540,14 +461,14 @@ func (postgres *Postgres) GetDeviceByName(ctx context.Context, name string) (*ty
} }
// GetDevices from the database // GetDevices from the database
func (postgres *Postgres) GetDevices(ctx context.Context) ([]*types.Device, error) { func (d *Postgres) GetDevices(ctx context.Context) ([]*types.Device, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
devices, err := postgres.selectDevices(tx, selectDevicesSQL) devices, err := d.selectDevices(tx, postgres.SelectDevicesSQL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -560,7 +481,7 @@ func (postgres *Postgres) GetDevices(ctx context.Context) ([]*types.Device, erro
return devices, nil return devices, nil
} }
func (postgres *Postgres) selectDevices(tx *sql.Tx, query string, args ...interface{}) ([]*types.Device, error) { func (d *Postgres) selectDevices(tx *sql.Tx, query string, args ...interface{}) ([]*types.Device, error) {
stmt, err := tx.Prepare(query) stmt, err := tx.Prepare(query)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to prepare statement: %v", err) return nil, fmt.Errorf("Failed to prepare statement: %v", err)
@ -592,14 +513,14 @@ func (postgres *Postgres) selectDevices(tx *sql.Tx, query string, args ...interf
} }
// GetHumidity returns humidity from the database // GetHumidity returns humidity from the database
func (postgres *Postgres) GetHumidityByID(ctx context.Context, id string) (*types.MeasuredValue, error) { func (d *Postgres) GetHumidityByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tx.Rollback() defer tx.Rollback()
measuredValues, err := postgres.selectMeasuredValue(tx, selectHumidityByIDSQL, id) measuredValues, err := d.selectMeasuredValue(tx, postgres.SelectHumidityByIDSQL, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -621,14 +542,14 @@ func (postgres *Postgres) GetHumidityByID(ctx context.Context, id string) (*type
} }
// GetHumidities returns humidities from the database // GetHumidities returns humidities from the database
func (postgres *Postgres) GetHumidities(ctx context.Context) ([]*types.MeasuredValue, error) { func (d *Postgres) GetHumidities(ctx context.Context) ([]*types.MeasuredValue, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tx.Rollback() defer tx.Rollback()
measuredValues, err := postgres.selectMeasuredValue(tx, selectHumiditiesSQL) measuredValues, err := d.selectMeasuredValue(tx, postgres.SelectHumiditiesSQL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -645,7 +566,7 @@ func (postgres *Postgres) GetHumidities(ctx context.Context) ([]*types.MeasuredV
return measuredValues, nil return measuredValues, nil
} }
func (postgres *Postgres) selectMeasuredValue(tx *sql.Tx, query string, args ...interface{}) ([]*types.MeasuredValue, error) { func (d *Postgres) selectMeasuredValue(tx *sql.Tx, query string, args ...interface{}) ([]*types.MeasuredValue, error) {
stmt, err := tx.Prepare(query) stmt, err := tx.Prepare(query)
if err != nil { if err != nil {
return nil, err return nil, err
@ -680,14 +601,14 @@ func (postgres *Postgres) selectMeasuredValue(tx *sql.Tx, query string, args ...
} }
// GetPressure returns pressure from the database // GetPressure returns pressure from the database
func (postgres *Postgres) GetPressureByID(ctx context.Context, id string) (*types.MeasuredValue, error) { func (d *Postgres) GetPressureByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tx.Rollback() defer tx.Rollback()
measuredValues, err := postgres.selectMeasuredValue(tx, selectPressureByIDSQL, id) measuredValues, err := d.selectMeasuredValue(tx, postgres.SelectPressureByIDSQL, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -709,14 +630,14 @@ func (postgres *Postgres) GetPressureByID(ctx context.Context, id string) (*type
} }
// GetPressures returns pressure from the database // GetPressures returns pressure from the database
func (postgres *Postgres) GetPressures(ctx context.Context) ([]*types.MeasuredValue, error) { func (d *Postgres) GetPressures(ctx context.Context) ([]*types.MeasuredValue, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tx.Rollback() defer tx.Rollback()
measuredValues, err := postgres.selectMeasuredValue(tx, selectPressuresSQL) measuredValues, err := d.selectMeasuredValue(tx, postgres.SelectPressuresSQL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -734,14 +655,14 @@ func (postgres *Postgres) GetPressures(ctx context.Context) ([]*types.MeasuredVa
} }
// GetSensor from database // GetSensor from database
func (postgres *Postgres) GetSensorByID(ctx context.Context, id string) (*types.Sensor, error) { func (d *Postgres) GetSensorByID(ctx context.Context, id string) (*types.Sensor, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
sensors, err := postgres.selectSensors(tx, selectSensorByIDSQL, id) sensors, err := d.selectSensors(tx, postgres.SelectSensorByIDSQL, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -759,14 +680,14 @@ func (postgres *Postgres) GetSensorByID(ctx context.Context, id string) (*types.
} }
// GetSensors from the database // GetSensors from the database
func (postgres *Postgres) GetSensors(ctx context.Context) ([]*types.Sensor, error) { func (d *Postgres) GetSensors(ctx context.Context) ([]*types.Sensor, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
sensors, err := postgres.selectSensors(tx, selectSensorsSQL) sensors, err := d.selectSensors(tx, postgres.SelectSensorsSQL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -780,8 +701,8 @@ func (postgres *Postgres) GetSensors(ctx context.Context) ([]*types.Sensor, erro
} }
// GetSensorsByModels from the database // GetSensorsByModels from the database
func (postgres *Postgres) GetSensorsByDeviceIDs(ctx context.Context, deviceIDs ...string) ([]*types.Sensor, error) { func (d *Postgres) GetSensorsByDeviceIDs(ctx context.Context, deviceIDs ...string) ([]*types.Sensor, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
@ -789,7 +710,7 @@ func (postgres *Postgres) GetSensorsByDeviceIDs(ctx context.Context, deviceIDs .
cachedSensors := make([]*types.Sensor, 0) cachedSensors := make([]*types.Sensor, 0)
for i := range deviceIDs { for i := range deviceIDs {
sensors, err := postgres.selectSensors(tx, selectSensorsByDeviceIDSQL, deviceIDs[i]) sensors, err := d.selectSensors(tx, postgres.SelectSensorsByDeviceIDSQL, deviceIDs[i])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -806,15 +727,15 @@ func (postgres *Postgres) GetSensorsByDeviceIDs(ctx context.Context, deviceIDs .
} }
// GetSensorsByModel from the database // GetSensorsByModel from the database
func (postgres *Postgres) GetSensorsByModels(ctx context.Context, sensorModels ...string) ([]*types.Sensor, error) { func (d *Postgres) GetSensorsByModels(ctx context.Context, sensorModels ...string) ([]*types.Sensor, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
cachedSensors := make([]*types.Sensor, 0) cachedSensors := make([]*types.Sensor, 0)
for i := range sensorModels { for i := range sensorModels {
sensors, err := postgres.selectSensors(tx, selectSensorsByModelSQL, sensorModels[i]) sensors, err := d.selectSensors(tx, postgres.SelectSensorsByModelSQL, sensorModels[i])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -831,8 +752,8 @@ func (postgres *Postgres) GetSensorsByModels(ctx context.Context, sensorModels .
} }
// GetSensorsByModel from the database // GetSensorsByModel from the database
func (postgres *Postgres) GetSensorsByNames(ctx context.Context, sensorNames ...string) ([]*types.Sensor, error) { func (d *Postgres) GetSensorsByNames(ctx context.Context, sensorNames ...string) ([]*types.Sensor, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
@ -840,7 +761,7 @@ func (postgres *Postgres) GetSensorsByNames(ctx context.Context, sensorNames ...
cachedSensors := make([]*types.Sensor, 0) cachedSensors := make([]*types.Sensor, 0)
for i := range sensorNames { for i := range sensorNames {
sensors, err := postgres.selectSensors(tx, selectSensorsByNameSQL, sensorNames[i]) sensors, err := d.selectSensors(tx, postgres.SelectSensorsByNameSQL, sensorNames[i])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -856,7 +777,7 @@ func (postgres *Postgres) GetSensorsByNames(ctx context.Context, sensorNames ...
return cachedSensors, nil return cachedSensors, nil
} }
func (postgres *Postgres) selectSensors(tx *sql.Tx, query string, args ...interface{}) ([]*types.Sensor, error) { func (d *Postgres) selectSensors(tx *sql.Tx, query string, args ...interface{}) ([]*types.Sensor, error) {
stmt, err := tx.Prepare(query) stmt, err := tx.Prepare(query)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to prepare statement: %v", err) return nil, fmt.Errorf("Failed to prepare statement: %v", err)
@ -897,14 +818,14 @@ func (postgres *Postgres) selectSensors(tx *sql.Tx, query string, args ...interf
} }
// GetTemperature returns temperatures from the database // GetTemperature returns temperatures from the database
func (postgres *Postgres) GetTemperatureByID(ctx context.Context, id string) (*types.MeasuredValue, error) { func (d *Postgres) GetTemperatureByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tx.Rollback() defer tx.Rollback()
measuredValues, err := postgres.selectMeasuredValue(tx, selectTemperatureByIDSQL, id) measuredValues, err := d.selectMeasuredValue(tx, postgres.SelectTemperatureByIDSQL, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -926,14 +847,14 @@ func (postgres *Postgres) GetTemperatureByID(ctx context.Context, id string) (*t
} }
// GetTemperatures returns temperatures from the database // GetTemperatures returns temperatures from the database
func (postgres *Postgres) GetTemperatures(ctx context.Context) ([]*types.MeasuredValue, error) { func (d *Postgres) GetTemperatures(ctx context.Context) ([]*types.MeasuredValue, error) {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tx.Rollback() defer tx.Rollback()
measuredValues, err := postgres.selectMeasuredValue(tx, selectTemperaturesSQL) measuredValues, err := d.selectMeasuredValue(tx, postgres.SelectTemperaturesSQL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -950,15 +871,62 @@ func (postgres *Postgres) GetTemperatures(ctx context.Context) ([]*types.Measure
return measuredValues, nil return measuredValues, nil
} }
// Import imports devices, sensors and all measured values from a source
// repository. Existing entries will be updated.
func (d *Postgres) Import(ctx context.Context, src Repository) error {
tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return err
}
defer 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)
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 // RemoveDevices from the database
func (postgres *Postgres) RemoveDevicesByIDs(ctx context.Context, deviceIDs ...string) error { func (d *Postgres) RemoveDevicesByIDs(ctx context.Context, deviceIDs ...string) error {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(deleteDeviceByIDSQL) stmt, err := tx.Prepare(postgres.DeleteDeviceByIDSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -975,14 +943,14 @@ func (postgres *Postgres) RemoveDevicesByIDs(ctx context.Context, deviceIDs ...s
} }
// RemoveDevices from the database // RemoveDevices from the database
func (postgres *Postgres) RemoveDevicesByNames(ctx context.Context, names ...string) error { func (d *Postgres) RemoveDevicesByNames(ctx context.Context, names ...string) error {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(deleteDeviceByNameSQL) stmt, err := tx.Prepare(postgres.DeleteDeviceByNameSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -999,14 +967,14 @@ func (postgres *Postgres) RemoveDevicesByNames(ctx context.Context, names ...str
} }
// RemoveSensors from the database // RemoveSensors from the database
func (postgres *Postgres) RemoveSensorsByIDs(ctx context.Context, sensorIDs ...string) error { func (d *Postgres) RemoveSensorsByIDs(ctx context.Context, sensorIDs ...string) error {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(deleteSensorByIDSQL) stmt, err := tx.Prepare(postgres.DeleteSensorByIDSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -1023,14 +991,14 @@ func (postgres *Postgres) RemoveSensorsByIDs(ctx context.Context, sensorIDs ...s
} }
// RemoveSensors from the database // RemoveSensors from the database
func (postgres *Postgres) RemoveSensorsByNames(ctx context.Context, sensorIDs ...string) error { func (d *Postgres) RemoveSensorsByNames(ctx context.Context, sensorIDs ...string) error {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(deleteSensorByNameSQL) stmt, err := tx.Prepare(postgres.DeleteSensorByNameSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -1047,14 +1015,14 @@ func (postgres *Postgres) RemoveSensorsByNames(ctx context.Context, sensorIDs ..
} }
// UpdateDevices updates a device in the database // UpdateDevices updates a device in the database
func (postgres *Postgres) UpdateDevices(ctx context.Context, devices ...*types.Device) error { func (d *Postgres) UpdateDevices(ctx context.Context, devices ...*types.Device) error {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return err return err
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(updateDeviceSQL) stmt, err := tx.Prepare(postgres.UpdateDeviceSQL)
if err != nil { if err != nil {
return err return err
} }
@ -1080,14 +1048,14 @@ func (postgres *Postgres) UpdateDevices(ctx context.Context, devices ...*types.D
} }
// UpdateSensors updates a sensor in the database // UpdateSensors updates a sensor in the database
func (postgres *Postgres) UpdateSensors(ctx context.Context, sensors ...*types.Sensor) error { func (d *Postgres) UpdateSensors(ctx context.Context, sensors ...*types.Sensor) error {
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return err return err
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(updateSensorSQL) stmt, err := tx.Prepare(postgres.UpdateSensorSQL)
if err != nil { if err != nil {
return err return err
} }

View File

@ -0,0 +1,100 @@
package postgres
import "embed"
var (
DDLAssetPath = "ddl"
//go:embed ddl/*.sql
DDLAssets embed.FS
//go:embed dml/deleteDeviceByID.sql
DeleteDeviceByIDSQL string
//go:embed dml/deleteDeviceByName.sql
DeleteDeviceByNameSQL string
//go:embed dml/deleteSensorByID.sql
DeleteSensorByIDSQL string
//go:embed dml/deleteSensorByName.sql
DeleteSensorByNameSQL string
//go:embed dml/insertDevice.sql
InsertDeviceSQL string
//go:embed dml/insertHumidity.sql
InsertHumiditySQL string
//go:embed dml/insertOrUpdateDevice.sql
InsertOrUpdateDeviceSQL string
//go:embed dml/insertOrUpdateHumidity.sql
InsertOrUpdateHumiditySQL string
//go:embed dml/insertOrUpdatePressure.sql
InsertOrUpdatePressureSQL string
//go:embed dml/insertOrUpdateSensor.sql
InsertOrUpdateSensorSQL string
//go:embed dml/insertOrUpdateTemperature.sql
InsertOrUpdateTemperatureSQL string
//go:embed dml/insertPressure.sql
InsertPressureSQL string
//go:embed dml/insertSensor.sql
InsertSensorSQL string
//go:embed dml/insertTemperature.sql
InsertTemperatureSQL string
//go:embed dml/selectDeviceByID.sql
SelectDeviceByIDSQL string
//go:embed dml/selectDeviceByName.sql
SelectDeviceByNameSQL string
//go:embed dml/selectDevices.sql
SelectDevicesSQL string
//go:embed dml/selectHumidities.sql
SelectHumiditiesSQL string
//go:embed dml/selectHumidityByID.sql
SelectHumidityByIDSQL string
//go:embed dml/selectPressureByID.sql
SelectPressureByIDSQL string
//go:embed dml/selectPressures.sql
SelectPressuresSQL string
//go:embed dml/selectSensorByID.sql
SelectSensorByIDSQL string
//go:embed dml/selectSensors.sql
SelectSensorsSQL string
//go:embed dml/selectSensorsByDeviceID.sql
SelectSensorsByDeviceIDSQL string
//go:embed dml/selectSensorsByModel.sql
SelectSensorsByModelSQL string
//go:embed dml/selectSensorsByName.sql
SelectSensorsByNameSQL string
//go:embed dml/selectTemperatureByID.sql
SelectTemperatureByIDSQL string
//go:embed dml/selectTemperatures.sql
SelectTemperaturesSQL string
//go:embed dml/updateDevice.sql
UpdateDeviceSQL string
//go:embed dml/updateSensor.sql
UpdateSensorSQL string
)

View File

@ -5,8 +5,6 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"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/flucky/pkg/types"
"git.cryptic.systems/volker.raschek/go-logger" "git.cryptic.systems/volker.raschek/go-logger"
) )
@ -33,6 +31,7 @@ type Repository interface {
GetSensors(ctx context.Context) ([]*types.Sensor, error) GetSensors(ctx context.Context) ([]*types.Sensor, error)
GetTemperatureByID(ctx context.Context, id string) (*types.MeasuredValue, error) GetTemperatureByID(ctx context.Context, id string) (*types.MeasuredValue, error)
GetTemperatures(ctx context.Context) ([]*types.MeasuredValue, error) GetTemperatures(ctx context.Context) ([]*types.MeasuredValue, error)
Import(ctx context.Context, src Repository) error
Migrate(ctx context.Context) error Migrate(ctx context.Context) error
RemoveDevicesByIDs(ctx context.Context, deviceIDs ...string) error RemoveDevicesByIDs(ctx context.Context, deviceIDs ...string) error
RemoveDevicesByNames(ctx context.Context, names ...string) error RemoveDevicesByNames(ctx context.Context, names ...string) error
@ -46,7 +45,7 @@ type Repository interface {
func New(databaseURL *url.URL, flogger logger.Logger) (Repository, error) { func New(databaseURL *url.URL, flogger logger.Logger) (Repository, error) {
switch databaseURL.Scheme { switch databaseURL.Scheme {
case "postgres": case "postgres":
repo, err := postgres.New(postgres.Opts{ repo, err := NewPostgres(PostgresOpts{
DatabaseURL: databaseURL, DatabaseURL: databaseURL,
Logger: flogger, Logger: flogger,
}) })
@ -63,7 +62,7 @@ func New(databaseURL *url.URL, flogger logger.Logger) (Repository, error) {
return repo, nil return repo, nil
case "sqlite3": case "sqlite3":
repo, err := sqlite3.New(sqlite3.Opts{ repo, err := NewSQLite(SQLiteOpts{
DatabaseURL: databaseURL, DatabaseURL: databaseURL,
Logger: flogger, Logger: flogger,
}) })

View File

@ -1,9 +1,8 @@
package sqlite3 package repository
import ( import (
"context" "context"
"database/sql" "database/sql"
"embed"
"errors" "errors"
"fmt" "fmt"
"net/url" "net/url"
@ -11,113 +10,20 @@ import (
"path/filepath" "path/filepath"
"sync" "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/flucky/pkg/types"
"git.cryptic.systems/volker.raschek/go-logger" "git.cryptic.systems/volker.raschek/go-logger"
"github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4"
"github.com/johejo/golang-migrate-extra/source/iofs" "github.com/johejo/golang-migrate-extra/source/iofs"
) )
var ( type SQLiteOpts struct {
//go:embed ddl/*.sql
ddlAssets embed.FS
//go:embed dml/deleteDeviceByID.sql
deleteDeviceByIDSQL string
//go:embed dml/deleteDeviceByName.sql
deleteDeviceByNameSQL string
//go:embed dml/deleteSensorByID.sql
deleteSensorByIDSQL string
//go:embed dml/deleteSensorByName.sql
deleteSensorByNameSQL string
//go:embed dml/insertDevice.sql
insertDeviceSQL string
//go:embed dml/insertHumidity.sql
insertHumiditySQL string
//go:embed dml/insertOrUpdateDevice.sql
insertOrUpdateDeviceSQL string
//go:embed dml/insertOrUpdateHumidity.sql
insertOrUpdateHumiditySQL string
//go:embed dml/insertOrUpdatePressure.sql
insertOrUpdatePressureSQL string
//go:embed dml/insertOrUpdateSensor.sql
insertOrUpdateSensorSQL string
//go:embed dml/insertOrUpdateTemperature.sql
insertOrUpdateTemperatureSQL string
//go:embed dml/insertPressure.sql
insertPressureSQL string
//go:embed dml/insertSensor.sql
insertSensorSQL string
//go:embed dml/insertTemperature.sql
insertTemperatureSQL string
//go:embed dml/selectDeviceByID.sql
selectDeviceByIDSQL string
//go:embed dml/selectDeviceByName.sql
selectDeviceByNameSQL string
//go:embed dml/selectDevices.sql
selectDevicesSQL string
//go:embed dml/selectHumidities.sql
selectHumiditiesSQL string
//go:embed dml/selectHumidityByID.sql
selectHumidityByIDSQL string
//go:embed dml/selectPressureByID.sql
selectPressureByIDSQL string
//go:embed dml/selectPressures.sql
selectPressuresSQL string
//go:embed dml/selectSensorByID.sql
selectSensorByIDSQL string
//go:embed dml/selectSensors.sql
selectSensorsSQL string
//go:embed dml/selectSensorsByDeviceID.sql
selectSensorsByDeviceIDSQL string
//go:embed dml/selectSensorsByModel.sql
selectSensorsByModelSQL string
//go:embed dml/selectSensorsByName.sql
selectSensorsByNameSQL string
//go:embed dml/selectTemperatureByID.sql
selectTemperatureByIDSQL string
//go:embed dml/selectTemperatures.sql
selectTemperaturesSQL string
//go:embed dml/updateDevice.sql
updateDeviceSQL string
//go:embed dml/updateSensor.sql
updateSensorSQL string
)
type Opts struct {
DatabaseURL *url.URL DatabaseURL *url.URL
Logger logger.Logger Logger logger.Logger
} }
func (o *Opts) Validate() error { func (o *SQLiteOpts) Validate() error {
for k, v := range map[string]interface{}{ for k, v := range map[string]interface{}{
"DatabaseURL": o.DatabaseURL, "DatabaseURL": o.DatabaseURL,
"Logger": o.Logger, "Logger": o.Logger,
@ -134,7 +40,7 @@ func (o *Opts) Validate() error {
return nil return nil
} }
func New(opts Opts) (*SQLite, error) { func NewSQLite(opts SQLiteOpts) (Repository, error) {
if err := opts.Validate(); err != nil { if err := opts.Validate(); err != nil {
return nil, err return nil, err
} }
@ -175,17 +81,26 @@ type SQLite struct {
} }
// AddDevices into the database // AddDevices into the database
func (sqlite *SQLite) AddDevices(ctx context.Context, devices ...*types.Device) error { func (d *SQLite) AddDevices(ctx context.Context, devices ...*types.Device) error {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(insertDeviceSQL) 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 { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -198,21 +113,30 @@ func (sqlite *SQLite) AddDevices(ctx context.Context, devices ...*types.Device)
} }
} }
return tx.Commit() return nil
} }
// AddOrUpdateDevices into the database // AddOrUpdateDevices into the database
func (sqlite *SQLite) AddOrUpdateDevices(ctx context.Context, devices ...*types.Device) error { func (d *SQLite) AddOrUpdateDevices(ctx context.Context, devices ...*types.Device) error {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(insertOrUpdateDeviceSQL) 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 { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -225,13 +149,13 @@ func (sqlite *SQLite) AddOrUpdateDevices(ctx context.Context, devices ...*types.
} }
} }
return tx.Commit() return nil
} }
// AddMeasuredValues into the database // AddMeasuredValues into the database
func (sqlite *SQLite) AddMeasuredValues(ctx context.Context, measuredValues ...*types.MeasuredValue) error { func (d *SQLite) AddMeasuredValues(ctx context.Context, measuredValues ...*types.MeasuredValue) error {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
splittedMeasuredValues := make(map[types.MeasuredValueType][]*types.MeasuredValue, 0) splittedMeasuredValues := make(map[types.MeasuredValueType][]*types.MeasuredValue, 0)
@ -242,53 +166,27 @@ func (sqlite *SQLite) AddMeasuredValues(ctx context.Context, measuredValues ...*
splittedMeasuredValues[measuredValue.ValueType] = append(splittedMeasuredValues[measuredValue.ValueType], measuredValue) splittedMeasuredValues[measuredValue.ValueType] = append(splittedMeasuredValues[measuredValue.ValueType], measuredValue)
} }
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
// General insert function
insert := func(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 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
}
for measuredValueType, measuredValues := range splittedMeasuredValues { for measuredValueType, measuredValues := range splittedMeasuredValues {
var query string var query string
switch measuredValueType { switch measuredValueType {
case types.Humidity: case types.Humidity:
query = insertHumiditySQL query = sqlite3.InsertHumiditySQL
case types.Pressure: case types.Pressure:
query = insertPressureSQL query = sqlite3.InsertPressureSQL
case types.Temperature: case types.Temperature:
query = insertTemperatureSQL query = sqlite3.InsertTemperatureSQL
default: default:
return fmt.Errorf("Measured value type %v not supported", measuredValueType) return fmt.Errorf("Measured value type %v not supported", measuredValueType)
} }
err := insert(tx, query, measuredValues) err := d.insertMeasuredValues(tx, query, measuredValues...)
if err != nil { if err != nil {
return err return err
} }
@ -297,29 +195,7 @@ func (sqlite *SQLite) AddMeasuredValues(ctx context.Context, measuredValues ...*
return tx.Commit() return tx.Commit()
} }
// AddOrUpdateMeasuredValues into the database func (d *SQLite) insertMeasuredValues(tx *sql.Tx, query string, measuredValues ...*types.MeasuredValue) error {
func (sqlite *SQLite) AddOrUpdateMeasuredValues(ctx context.Context, measuredValues ...*types.MeasuredValue) error {
sqlite.mutex.Lock()
defer sqlite.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 := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err)
}
defer tx.Rollback()
// General insert function
insert := func(tx *sql.Tx, query string, measuredValues []*types.MeasuredValue) error {
stmt, err := tx.Prepare(query) stmt, err := tx.Prepare(query)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
@ -344,21 +220,41 @@ func (sqlite *SQLite) AddOrUpdateMeasuredValues(ctx context.Context, measuredVal
return nil 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 tx.Rollback()
for measuredValueType, measuredValues := range splittedMeasuredValues { for measuredValueType, measuredValues := range splittedMeasuredValues {
var queryFile string var query string
switch measuredValueType { switch measuredValueType {
case types.Humidity: case types.Humidity:
queryFile = insertOrUpdateHumiditySQL query = sqlite3.InsertOrUpdateHumiditySQL
case types.Pressure: case types.Pressure:
queryFile = insertOrUpdatePressureSQL query = sqlite3.InsertOrUpdatePressureSQL
case types.Temperature: case types.Temperature:
queryFile = insertOrUpdateTemperatureSQL query = sqlite3.InsertOrUpdateTemperatureSQL
default: default:
return fmt.Errorf("Measured value type %v not supported", measuredValueType) return fmt.Errorf("Measured value type %v not supported", measuredValueType)
} }
err := insert(tx, queryFile, measuredValues) err := d.insertOrUpdateMeasuredValues(tx, query, measuredValues...)
if err != nil { if err != nil {
return err return err
} }
@ -367,18 +263,43 @@ func (sqlite *SQLite) AddOrUpdateMeasuredValues(ctx context.Context, measuredVal
return tx.Commit() return tx.Commit()
} }
// AddSensors into the database func (d *SQLite) insertOrUpdateMeasuredValues(tx *sql.Tx, query string, measuredValues ...*types.MeasuredValue) error {
func (sqlite *SQLite) AddSensors(ctx context.Context, sensors ...*types.Sensor) error { stmt, err := tx.Prepare(query)
sqlite.mutex.Lock() if err != nil {
defer sqlite.mutex.Unlock() return fmt.Errorf("Failed to prepare statement: %v", err)
}
defer stmt.Close()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) 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 { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(insertSensorSQL) stmt, err := tx.Prepare(sqlite3.InsertSensorSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -409,17 +330,26 @@ func (sqlite *SQLite) AddSensors(ctx context.Context, sensors ...*types.Sensor)
} }
// AddOrUpdateSensors into the database // AddOrUpdateSensors into the database
func (sqlite *SQLite) AddOrUpdateSensors(ctx context.Context, sensors ...*types.Sensor) error { func (d *SQLite) AddOrUpdateSensors(ctx context.Context, sensors ...*types.Sensor) error {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(insertOrUpdateSensorSQL) 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 { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -446,28 +376,28 @@ func (sqlite *SQLite) AddOrUpdateSensors(ctx context.Context, sensors ...*types.
} }
} }
return tx.Commit() return nil
} }
// Close closes the database and prevents new queries from starting. Close then // 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. // waits for all queries that have started processing on the server to finish.
func (sqlite *SQLite) Close() error { func (d *SQLite) Close() error {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
return sqlite.dbo.Close() return d.dbo.Close()
} }
// Migrate creates all required tables if not exist // Migrate creates all required tables if not exist
func (sqlite *SQLite) Migrate(ctx context.Context) error { func (d *SQLite) Migrate(ctx context.Context) error {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
sourceDriver, err := iofs.New(ddlAssets, "ddl") sourceDriver, err := iofs.New(sqlite3.DDLAssets, sqlite3.DDLAssetPath)
if err != nil { if err != nil {
return err return err
} }
m, err := migrate.NewWithSourceInstance("iofs", sourceDriver, sqlite.databaseURL.String()) m, err := migrate.NewWithSourceInstance("iofs", sourceDriver, d.databaseURL.String())
if err != nil { if err != nil {
return err return err
} }
@ -482,17 +412,17 @@ func (sqlite *SQLite) Migrate(ctx context.Context) error {
} }
// GetDevice from database // GetDevice from database
func (sqlite *SQLite) GetDeviceByID(ctx context.Context, id string) (*types.Device, error) { func (d *SQLite) GetDeviceByID(ctx context.Context, id string) (*types.Device, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
devices, err := sqlite.selectDevices(tx, selectDeviceByIDSQL, id) devices, err := d.selectDevices(tx, sqlite3.SelectDeviceByIDSQL, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -510,17 +440,17 @@ func (sqlite *SQLite) GetDeviceByID(ctx context.Context, id string) (*types.Devi
} }
// GetDevice from database // GetDevice from database
func (sqlite *SQLite) GetDeviceByName(ctx context.Context, name string) (*types.Device, error) { func (d *SQLite) GetDeviceByName(ctx context.Context, name string) (*types.Device, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
devices, err := sqlite.selectDevices(tx, selectDeviceByNameSQL, name) devices, err := d.selectDevices(tx, sqlite3.SelectDeviceByNameSQL, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -538,17 +468,17 @@ func (sqlite *SQLite) GetDeviceByName(ctx context.Context, name string) (*types.
} }
// GetDevices from the database // GetDevices from the database
func (sqlite *SQLite) GetDevices(ctx context.Context) ([]*types.Device, error) { func (d *SQLite) GetDevices(ctx context.Context) ([]*types.Device, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
devices, err := sqlite.selectDevices(tx, selectDevicesSQL) devices, err := d.selectDevices(tx, sqlite3.SelectDevicesSQL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -561,7 +491,7 @@ func (sqlite *SQLite) GetDevices(ctx context.Context) ([]*types.Device, error) {
return devices, nil return devices, nil
} }
func (sqlite *SQLite) selectDevices(tx *sql.Tx, query string, args ...interface{}) ([]*types.Device, error) { func (d *SQLite) selectDevices(tx *sql.Tx, query string, args ...interface{}) ([]*types.Device, error) {
stmt, err := tx.Prepare(query) stmt, err := tx.Prepare(query)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to prepare statement: %v", err) return nil, fmt.Errorf("Failed to prepare statement: %v", err)
@ -594,17 +524,17 @@ func (sqlite *SQLite) selectDevices(tx *sql.Tx, query string, args ...interface{
} }
// GetHumidity returns humidity from the database // GetHumidity returns humidity from the database
func (sqlite *SQLite) GetHumidityByID(ctx context.Context, id string) (*types.MeasuredValue, error) { func (d *SQLite) GetHumidityByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tx.Rollback() defer tx.Rollback()
measuredValues, err := sqlite.selectMeasuredValue(tx, selectHumidityByIDSQL, id) measuredValues, err := d.selectMeasuredValue(tx, sqlite3.SelectHumidityByIDSQL, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -626,17 +556,17 @@ func (sqlite *SQLite) GetHumidityByID(ctx context.Context, id string) (*types.Me
} }
// GetHumidities returns humidities from the database // GetHumidities returns humidities from the database
func (sqlite *SQLite) GetHumidities(ctx context.Context) ([]*types.MeasuredValue, error) { func (d *SQLite) GetHumidities(ctx context.Context) ([]*types.MeasuredValue, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tx.Rollback() defer tx.Rollback()
measuredValues, err := sqlite.selectMeasuredValue(tx, selectHumiditiesSQL) measuredValues, err := d.selectMeasuredValue(tx, sqlite3.SelectHumiditiesSQL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -653,7 +583,7 @@ func (sqlite *SQLite) GetHumidities(ctx context.Context) ([]*types.MeasuredValue
return measuredValues, nil return measuredValues, nil
} }
func (sqlite *SQLite) selectMeasuredValue(tx *sql.Tx, query string, args ...interface{}) ([]*types.MeasuredValue, error) { func (d *SQLite) selectMeasuredValue(tx *sql.Tx, query string, args ...interface{}) ([]*types.MeasuredValue, error) {
stmt, err := tx.Prepare(query) stmt, err := tx.Prepare(query)
if err != nil { if err != nil {
return nil, err return nil, err
@ -689,17 +619,17 @@ func (sqlite *SQLite) selectMeasuredValue(tx *sql.Tx, query string, args ...inte
} }
// GetPressure returns pressure from the database // GetPressure returns pressure from the database
func (sqlite *SQLite) GetPressureByID(ctx context.Context, id string) (*types.MeasuredValue, error) { func (d *SQLite) GetPressureByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tx.Rollback() defer tx.Rollback()
measuredValues, err := sqlite.selectMeasuredValue(tx, selectPressureByIDSQL, id) measuredValues, err := d.selectMeasuredValue(tx, sqlite3.SelectPressureByIDSQL, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -721,17 +651,17 @@ func (sqlite *SQLite) GetPressureByID(ctx context.Context, id string) (*types.Me
} }
// GetPressures returns pressure from the database // GetPressures returns pressure from the database
func (sqlite *SQLite) GetPressures(ctx context.Context) ([]*types.MeasuredValue, error) { func (d *SQLite) GetPressures(ctx context.Context) ([]*types.MeasuredValue, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tx.Rollback() defer tx.Rollback()
measuredValues, err := sqlite.selectMeasuredValue(tx, selectPressuresSQL) measuredValues, err := d.selectMeasuredValue(tx, sqlite3.SelectPressuresSQL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -749,17 +679,17 @@ func (sqlite *SQLite) GetPressures(ctx context.Context) ([]*types.MeasuredValue,
} }
// GetSensor from database // GetSensor from database
func (sqlite *SQLite) GetSensorByID(ctx context.Context, id string) (*types.Sensor, error) { func (d *SQLite) GetSensorByID(ctx context.Context, id string) (*types.Sensor, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
sensors, err := sqlite.selectSensors(tx, selectSensorByIDSQL, id) sensors, err := d.selectSensors(tx, sqlite3.SelectSensorByIDSQL, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -777,17 +707,17 @@ func (sqlite *SQLite) GetSensorByID(ctx context.Context, id string) (*types.Sens
} }
// GetSensors from the database // GetSensors from the database
func (sqlite *SQLite) GetSensors(ctx context.Context) ([]*types.Sensor, error) { func (d *SQLite) GetSensors(ctx context.Context) ([]*types.Sensor, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
sensors, err := sqlite.selectSensors(tx, selectSensorsSQL) sensors, err := d.selectSensors(tx, sqlite3.SelectSensorsSQL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -801,11 +731,11 @@ func (sqlite *SQLite) GetSensors(ctx context.Context) ([]*types.Sensor, error) {
} }
// GetSensorsByModels from the database // GetSensorsByModels from the database
func (sqlite *SQLite) GetSensorsByDeviceIDs(ctx context.Context, deviceIDs ...string) ([]*types.Sensor, error) { func (d *SQLite) GetSensorsByDeviceIDs(ctx context.Context, deviceIDs ...string) ([]*types.Sensor, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
@ -813,7 +743,7 @@ func (sqlite *SQLite) GetSensorsByDeviceIDs(ctx context.Context, deviceIDs ...st
cachedSensors := make([]*types.Sensor, 0) cachedSensors := make([]*types.Sensor, 0)
for i := range deviceIDs { for i := range deviceIDs {
sensors, err := sqlite.selectSensors(tx, selectSensorsByDeviceIDSQL, deviceIDs[i]) sensors, err := d.selectSensors(tx, sqlite3.SelectSensorsByDeviceIDSQL, deviceIDs[i])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -830,11 +760,11 @@ func (sqlite *SQLite) GetSensorsByDeviceIDs(ctx context.Context, deviceIDs ...st
} }
// GetSensorsByModels from the database // GetSensorsByModels from the database
func (sqlite *SQLite) GetSensorsByModels(ctx context.Context, sensorModels ...string) ([]*types.Sensor, error) { func (d *SQLite) GetSensorsByModels(ctx context.Context, sensorModels ...string) ([]*types.Sensor, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
@ -842,7 +772,7 @@ func (sqlite *SQLite) GetSensorsByModels(ctx context.Context, sensorModels ...st
cachedSensors := make([]*types.Sensor, 0) cachedSensors := make([]*types.Sensor, 0)
for i := range sensorModels { for i := range sensorModels {
sensors, err := sqlite.selectSensors(tx, selectSensorsByModelSQL, sensorModels[i]) sensors, err := d.selectSensors(tx, sqlite3.SelectSensorsByModelSQL, sensorModels[i])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -859,11 +789,11 @@ func (sqlite *SQLite) GetSensorsByModels(ctx context.Context, sensorModels ...st
} }
// GetSensorsByModels from the database // GetSensorsByModels from the database
func (sqlite *SQLite) GetSensorsByNames(ctx context.Context, sensorNames ...string) ([]*types.Sensor, error) { func (d *SQLite) GetSensorsByNames(ctx context.Context, sensorNames ...string) ([]*types.Sensor, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to begin new transaction: %v", err) return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
} }
@ -871,7 +801,7 @@ func (sqlite *SQLite) GetSensorsByNames(ctx context.Context, sensorNames ...stri
cachedSensors := make([]*types.Sensor, 0) cachedSensors := make([]*types.Sensor, 0)
for i := range sensorNames { for i := range sensorNames {
sensors, err := sqlite.selectSensors(tx, selectSensorsByNameSQL, sensorNames[i]) sensors, err := d.selectSensors(tx, sqlite3.SelectSensorsByNameSQL, sensorNames[i])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -887,7 +817,7 @@ func (sqlite *SQLite) GetSensorsByNames(ctx context.Context, sensorNames ...stri
return cachedSensors, nil return cachedSensors, nil
} }
func (sqlite *SQLite) selectSensors(tx *sql.Tx, query string, args ...interface{}) ([]*types.Sensor, error) { func (d *SQLite) selectSensors(tx *sql.Tx, query string, args ...interface{}) ([]*types.Sensor, error) {
stmt, err := tx.Prepare(query) stmt, err := tx.Prepare(query)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to prepare statement: %v", err) return nil, fmt.Errorf("Failed to prepare statement: %v", err)
@ -929,17 +859,17 @@ func (sqlite *SQLite) selectSensors(tx *sql.Tx, query string, args ...interface{
} }
// GetTemperature returns temperatures from the database // GetTemperature returns temperatures from the database
func (sqlite *SQLite) GetTemperatureByID(ctx context.Context, id string) (*types.MeasuredValue, error) { func (d *SQLite) GetTemperatureByID(ctx context.Context, id string) (*types.MeasuredValue, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tx.Rollback() defer tx.Rollback()
measuredValues, err := sqlite.selectMeasuredValue(tx, selectTemperatureByIDSQL, id) measuredValues, err := d.selectMeasuredValue(tx, sqlite3.SelectTemperatureByIDSQL, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -961,17 +891,17 @@ func (sqlite *SQLite) GetTemperatureByID(ctx context.Context, id string) (*types
} }
// GetTemperatures returns temperatures from the database // GetTemperatures returns temperatures from the database
func (sqlite *SQLite) GetTemperatures(ctx context.Context) ([]*types.MeasuredValue, error) { func (d *SQLite) GetTemperatures(ctx context.Context) ([]*types.MeasuredValue, error) {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer tx.Rollback() defer tx.Rollback()
measuredValues, err := sqlite.selectMeasuredValue(tx, selectTemperaturesSQL) measuredValues, err := d.selectMeasuredValue(tx, sqlite3.SelectTemperaturesSQL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -988,18 +918,65 @@ func (sqlite *SQLite) GetTemperatures(ctx context.Context) ([]*types.MeasuredVal
return measuredValues, nil return measuredValues, nil
} }
// RemoveDevices from the database // Import imports devices, sensors and all measured values from a source
func (sqlite *SQLite) RemoveDevicesByIDs(ctx context.Context, deviceIDs ...string) error { // repository. Existing entries will be updated.
sqlite.mutex.Lock() func (d *SQLite) Import(ctx context.Context, src Repository) error {
defer sqlite.mutex.Unlock() tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil {
return err
}
defer tx.Rollback()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) 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)
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 { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(deleteDeviceByIDSQL) stmt, err := tx.Prepare(sqlite3.DeleteDeviceByIDSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -1016,17 +993,17 @@ func (sqlite *SQLite) RemoveDevicesByIDs(ctx context.Context, deviceIDs ...strin
} }
// RemoveDevices from the database // RemoveDevices from the database
func (sqlite *SQLite) RemoveDevicesByNames(ctx context.Context, names ...string) error { func (d *SQLite) RemoveDevicesByNames(ctx context.Context, names ...string) error {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(deleteDeviceByNameSQL) stmt, err := tx.Prepare(sqlite3.DeleteDeviceByNameSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -1043,17 +1020,17 @@ func (sqlite *SQLite) RemoveDevicesByNames(ctx context.Context, names ...string)
} }
// RemoveSensors from the database // RemoveSensors from the database
func (sqlite *SQLite) RemoveSensorsByIDs(ctx context.Context, sensorIDs ...string) error { func (d *SQLite) RemoveSensorsByIDs(ctx context.Context, sensorIDs ...string) error {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(deleteSensorByIDSQL) stmt, err := tx.Prepare(sqlite3.DeleteSensorByIDSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -1070,17 +1047,17 @@ func (sqlite *SQLite) RemoveSensorsByIDs(ctx context.Context, sensorIDs ...strin
} }
// RemoveSensors from the database // RemoveSensors from the database
func (sqlite *SQLite) RemoveSensorsByNames(ctx context.Context, names ...string) error { func (d *SQLite) RemoveSensorsByNames(ctx context.Context, names ...string) error {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return fmt.Errorf("Failed to begin new transaction: %v", err) return fmt.Errorf("Failed to begin new transaction: %v", err)
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(deleteSensorByNameSQL) stmt, err := tx.Prepare(sqlite3.DeleteSensorByNameSQL)
if err != nil { if err != nil {
return fmt.Errorf("Failed to prepare statement: %v", err) return fmt.Errorf("Failed to prepare statement: %v", err)
} }
@ -1097,17 +1074,17 @@ func (sqlite *SQLite) RemoveSensorsByNames(ctx context.Context, names ...string)
} }
// UpdateDevices updates a device in the database // UpdateDevices updates a device in the database
func (sqlite *SQLite) UpdateDevices(ctx context.Context, devices ...*types.Device) error { func (d *SQLite) UpdateDevices(ctx context.Context, devices ...*types.Device) error {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return err return err
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(updateDeviceSQL) stmt, err := tx.Prepare(sqlite3.UpdateDeviceSQL)
if err != nil { if err != nil {
return err return err
} }
@ -1130,17 +1107,17 @@ func (sqlite *SQLite) UpdateDevices(ctx context.Context, devices ...*types.Devic
} }
// UpdateSensors updates a sensor in the database // UpdateSensors updates a sensor in the database
func (sqlite *SQLite) UpdateSensors(ctx context.Context, sensors ...*types.Sensor) error { func (d *SQLite) UpdateSensors(ctx context.Context, sensors ...*types.Sensor) error {
sqlite.mutex.Lock() d.mutex.Lock()
defer sqlite.mutex.Unlock() defer d.mutex.Unlock()
tx, err := sqlite.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false}) tx, err := d.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
if err != nil { if err != nil {
return err return err
} }
defer tx.Rollback() defer tx.Rollback()
stmt, err := tx.Prepare(updateSensorSQL) stmt, err := tx.Prepare(sqlite3.UpdateSensorSQL)
if err != nil { if err != nil {
return err return err
} }

View File

@ -0,0 +1,100 @@
package sqlite3
import "embed"
var (
DDLAssetPath = "ddl"
//go:embed ddl/*.sql
DDLAssets embed.FS
//go:embed dml/deleteDeviceByID.sql
DeleteDeviceByIDSQL string
//go:embed dml/deleteDeviceByName.sql
DeleteDeviceByNameSQL string
//go:embed dml/deleteSensorByID.sql
DeleteSensorByIDSQL string
//go:embed dml/deleteSensorByName.sql
DeleteSensorByNameSQL string
//go:embed dml/insertDevice.sql
InsertDeviceSQL string
//go:embed dml/insertHumidity.sql
InsertHumiditySQL string
//go:embed dml/insertOrUpdateDevice.sql
InsertOrUpdateDeviceSQL string
//go:embed dml/insertOrUpdateHumidity.sql
InsertOrUpdateHumiditySQL string
//go:embed dml/insertOrUpdatePressure.sql
InsertOrUpdatePressureSQL string
//go:embed dml/insertOrUpdateSensor.sql
InsertOrUpdateSensorSQL string
//go:embed dml/insertOrUpdateTemperature.sql
InsertOrUpdateTemperatureSQL string
//go:embed dml/insertPressure.sql
InsertPressureSQL string
//go:embed dml/insertSensor.sql
InsertSensorSQL string
//go:embed dml/insertTemperature.sql
InsertTemperatureSQL string
//go:embed dml/selectDeviceByID.sql
SelectDeviceByIDSQL string
//go:embed dml/selectDeviceByName.sql
SelectDeviceByNameSQL string
//go:embed dml/selectDevices.sql
SelectDevicesSQL string
//go:embed dml/selectHumidities.sql
SelectHumiditiesSQL string
//go:embed dml/selectHumidityByID.sql
SelectHumidityByIDSQL string
//go:embed dml/selectPressureByID.sql
SelectPressureByIDSQL string
//go:embed dml/selectPressures.sql
SelectPressuresSQL string
//go:embed dml/selectSensorByID.sql
SelectSensorByIDSQL string
//go:embed dml/selectSensors.sql
SelectSensorsSQL string
//go:embed dml/selectSensorsByDeviceID.sql
SelectSensorsByDeviceIDSQL string
//go:embed dml/selectSensorsByModel.sql
SelectSensorsByModelSQL string
//go:embed dml/selectSensorsByName.sql
SelectSensorsByNameSQL string
//go:embed dml/selectTemperatureByID.sql
SelectTemperatureByIDSQL string
//go:embed dml/selectTemperatures.sql
SelectTemperaturesSQL string
//go:embed dml/updateDevice.sql
UpdateDeviceSQL string
//go:embed dml/updateSensor.sql
UpdateSensorSQL string
)