fix(pkg/db): create or upgrade postgres schema based on semver
This commit is contained in:
11
pkg/db/db.go
11
pkg/db/db.go
@ -7,17 +7,6 @@ import (
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
type DBOType string
|
||||
|
||||
func (dboType DBOType) String() string {
|
||||
return string(dboType)
|
||||
}
|
||||
|
||||
const (
|
||||
DBOTypePostgres DBOType = "postgres"
|
||||
DBOTypeOracle = "oracle"
|
||||
)
|
||||
|
||||
func New(dboType DBOType, host string, port string, database string, user string, password string) (Database, error) {
|
||||
connStr := fmt.Sprintf("%v://%v:%v@%v:%v/%v?sslmode=disable", dboType.String(), user, password, host, port, database)
|
||||
newDBO, err := sql.Open(dboType.String(), connStr)
|
||||
|
12
pkg/db/dbotype.go
Normal file
12
pkg/db/dbotype.go
Normal file
@ -0,0 +1,12 @@
|
||||
package db
|
||||
|
||||
type DBOType string
|
||||
|
||||
func (dboType DBOType) String() string {
|
||||
return string(dboType)
|
||||
}
|
||||
|
||||
const (
|
||||
DBOTypePostgres DBOType = "postgres"
|
||||
DBOTypeOracle = "oracle"
|
||||
)
|
@ -3,6 +3,7 @@ package db
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/go-flucky/flucky/pkg/types"
|
||||
)
|
||||
|
||||
@ -11,6 +12,9 @@ type Database interface {
|
||||
// Close DB Connction
|
||||
Close() error
|
||||
|
||||
// Schema
|
||||
Schema(ctx context.Context, version *semver.Version) error
|
||||
|
||||
// Delete
|
||||
DeleteDevices(ctx context.Context, devices []*types.Device) error
|
||||
DeleteMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error
|
||||
|
@ -4,6 +4,10 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
|
||||
"github.com/go-flucky/flucky/pkg/types"
|
||||
_ "github.com/lib/pq"
|
||||
@ -17,6 +21,61 @@ func (p *Postgres) Close() error {
|
||||
return p.Close()
|
||||
}
|
||||
|
||||
// Schema create or upgrade database schema to the version of the flucky binary
|
||||
func (p *Postgres) Schema(ctx context.Context, version *semver.Version) error {
|
||||
|
||||
query := "SELECT value FROM info WHERE key='version';"
|
||||
|
||||
stmt, err := p.dbo.PrepareContext(ctx, query)
|
||||
if err != nil {
|
||||
|
||||
asset := "pkg/db/sql/psql/schema.sql"
|
||||
queryBytes, err := Asset(asset)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v: %v", errorGetAsset, err)
|
||||
}
|
||||
query = string(queryBytes)
|
||||
|
||||
_, err = p.dbo.ExecContext(ctx, query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v: %v", errorStatementExecute, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
schemaVersion := ""
|
||||
row := stmt.QueryRowContext(ctx)
|
||||
err = row.Scan(&schemaVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v: %v", errorScanRow, err)
|
||||
}
|
||||
|
||||
fromVersion, err := semver.NewVersion(schemaVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Can not create new sematic version from database: %v", err)
|
||||
}
|
||||
|
||||
if err := schema(fromVersion, version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The result will be 0 if from == to, -1 if from < to, or +1 if from > to.
|
||||
switch fromVersion.Compare(version) {
|
||||
case -1:
|
||||
return schema(fromVersion, version)
|
||||
case 0:
|
||||
// fromVersion and toVersion are equal
|
||||
return nil
|
||||
case 1:
|
||||
// fromVersion < toVersion
|
||||
return fmt.Errorf("Can not downgrade the database schema. Update the flucky binary")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (p *Postgres) DeleteDevices(ctx context.Context, devices []*types.Device) error {
|
||||
asset := "pkg/db/sql/psql/deleteDevice.sql"
|
||||
queryBytes, err := Asset(asset)
|
||||
@ -479,3 +538,42 @@ func (p *Postgres) UpdateMeasuredValues(ctx context.Context, measuredValues []*t
|
||||
func (p *Postgres) UpdateSensors(ctx context.Context, sensots []*types.Sensor) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func schema(fromVersion *semver.Version, toVersion *semver.Version) error {
|
||||
|
||||
sqlAssetFiles, err := parseAssetDir("pkg/db/sql/psql/schema")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Can not restore sql files: %v", err)
|
||||
}
|
||||
|
||||
for _, sqlAssetFile := range sqlAssetFiles {
|
||||
|
||||
version := strings.Replace()
|
||||
|
||||
sqlSchemaInformations := &schemaInformation{
|
||||
Version: filepath.Split()
|
||||
Asset: sqlAssetFile,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseAssetDir(dir string) ([]string, error) {
|
||||
assetFiles, err := AssetDir(dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Can not load asset directory %v: %v", dir, err)
|
||||
}
|
||||
|
||||
goldenAssetFiles := assetFiles
|
||||
|
||||
for i, assetFile := range assetFiles {
|
||||
if strings.HasPrefix(assetFile, "/") {
|
||||
// is directory- SKIP
|
||||
goldenAssetFiles = append(goldenAssetFiles[:i], goldenAssetFiles[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
return goldenAssetFiles, nil
|
||||
}
|
||||
|
@ -80,53 +80,57 @@ func TestPostgres(t *testing.T) {
|
||||
|
||||
tests := []*test{
|
||||
&test{
|
||||
Name: "insertDevices",
|
||||
Test: testInsertDevices,
|
||||
},
|
||||
&test{
|
||||
Name: "insertSensors",
|
||||
Test: testInsertSensors,
|
||||
},
|
||||
&test{
|
||||
Name: "insertHumidity",
|
||||
Test: testInsertHumidity,
|
||||
},
|
||||
&test{
|
||||
Name: "insertPressure",
|
||||
Test: testInsertPressure,
|
||||
},
|
||||
&test{
|
||||
Name: "insertTemperatures",
|
||||
Test: testInsertTemperatures,
|
||||
},
|
||||
&test{
|
||||
Name: "deleteHumidities",
|
||||
Test: testDeleteHumidity,
|
||||
},
|
||||
&test{
|
||||
Name: "deletePressures",
|
||||
Test: testDeletePressures,
|
||||
},
|
||||
&test{
|
||||
Name: "deleteTemperatures",
|
||||
Test: testDeleteTemperatures,
|
||||
},
|
||||
&test{
|
||||
Name: "insertMeasuredValues",
|
||||
Test: testInsertMeasuredValues,
|
||||
},
|
||||
&test{
|
||||
Name: "deleteMeasuredValues",
|
||||
Test: testDeleteMeasuredValues,
|
||||
},
|
||||
&test{
|
||||
Name: "deleteSensors",
|
||||
Test: testDeleteSensors,
|
||||
},
|
||||
&test{
|
||||
Name: "deleteDevices",
|
||||
Test: testDeleteDevices,
|
||||
Name: "schema",
|
||||
Test: testSchemaCreate,
|
||||
},
|
||||
// &test{
|
||||
// Name: "insertDevices",
|
||||
// Test: testInsertDevices,
|
||||
// },
|
||||
// &test{
|
||||
// Name: "insertSensors",
|
||||
// Test: testInsertSensors,
|
||||
// },
|
||||
// &test{
|
||||
// Name: "insertHumidity",
|
||||
// Test: testInsertHumidity,
|
||||
// },
|
||||
// &test{
|
||||
// Name: "insertPressure",
|
||||
// Test: testInsertPressure,
|
||||
// },
|
||||
// &test{
|
||||
// Name: "insertTemperatures",
|
||||
// Test: testInsertTemperatures,
|
||||
// },
|
||||
// &test{
|
||||
// Name: "deleteHumidities",
|
||||
// Test: testDeleteHumidity,
|
||||
// },
|
||||
// &test{
|
||||
// Name: "deletePressures",
|
||||
// Test: testDeletePressures,
|
||||
// },
|
||||
// &test{
|
||||
// Name: "deleteTemperatures",
|
||||
// Test: testDeleteTemperatures,
|
||||
// },
|
||||
// &test{
|
||||
// Name: "insertMeasuredValues",
|
||||
// Test: testInsertMeasuredValues,
|
||||
// },
|
||||
// &test{
|
||||
// Name: "deleteMeasuredValues",
|
||||
// Test: testDeleteMeasuredValues,
|
||||
// },
|
||||
// &test{
|
||||
// Name: "deleteSensors",
|
||||
// Test: testDeleteSensors,
|
||||
// },
|
||||
// &test{
|
||||
// Name: "deleteDevices",
|
||||
// Test: testDeleteDevices,
|
||||
// },
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@ -134,6 +138,13 @@ func TestPostgres(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testSchemaCreate(t *testing.T) {
|
||||
require := require.New(t)
|
||||
ctx := context.Background()
|
||||
err := database.Schema(ctx, "v0.1.0")
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
func testInsertDevices(t *testing.T) {
|
||||
require := require.New(t)
|
||||
ctx := context.Background()
|
||||
|
10
pkg/db/schema.go
Normal file
10
pkg/db/schema.go
Normal file
@ -0,0 +1,10 @@
|
||||
package db
|
||||
|
||||
var (
|
||||
postgreSQLSchemata []*schemaInformation
|
||||
)
|
||||
|
||||
type schemaInformation struct {
|
||||
Version string
|
||||
Asset string
|
||||
}
|
0
pkg/db/sql/psql/schema/latest.sql
Normal file
0
pkg/db/sql/psql/schema/latest.sql
Normal file
@ -3,6 +3,7 @@ DROP TABLE IF EXISTS sensors CASCADE;
|
||||
DROP TABLE IF EXISTS humidities CASCADE;
|
||||
DROP TABLE IF EXISTS pressures CASCADE;
|
||||
DROP TABLE IF EXISTS temperatures CASCADE;
|
||||
DROP TABLE IF EXISTS info CASCADE;
|
||||
|
||||
|
||||
-- +----------------------------------------+
|
||||
@ -61,6 +62,11 @@ CREATE TABLE IF NOT EXISTS temperatures (
|
||||
update_date TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS info (
|
||||
key VARCHAR(32) CONSTRAINT pk_info PRIMARY KEY,
|
||||
value VARCHAR(32) NOT NULL
|
||||
);
|
||||
|
||||
-- +----------------------------------------+
|
||||
-- | FOREIGN-KEYS |
|
||||
-- +----------------------------------------+
|
Reference in New Issue
Block a user