2019-02-14 13:33:42 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package pgutil
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
2019-03-12 13:29:13 +00:00
|
|
|
"strings"
|
2019-02-14 13:33:42 +00:00
|
|
|
|
2019-04-04 15:42:01 +01:00
|
|
|
"github.com/lib/pq"
|
2019-02-14 13:33:42 +00:00
|
|
|
"github.com/zeebo/errs"
|
2019-06-04 22:30:21 +01:00
|
|
|
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
2019-02-14 13:33:42 +00:00
|
|
|
|
2019-05-21 15:30:06 +01:00
|
|
|
"storj.io/storj/internal/dbutil"
|
2019-02-14 13:33:42 +00:00
|
|
|
"storj.io/storj/internal/dbutil/dbschema"
|
2019-04-23 12:13:57 +01:00
|
|
|
"storj.io/storj/internal/errs2"
|
2019-02-14 13:33:42 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// DB is postgres database with schema
|
|
|
|
type DB struct {
|
|
|
|
*sql.DB
|
|
|
|
Schema string
|
|
|
|
}
|
|
|
|
|
2019-06-04 22:30:21 +01:00
|
|
|
var (
|
|
|
|
mon = monkit.Package()
|
|
|
|
)
|
|
|
|
|
2019-02-14 13:33:42 +00:00
|
|
|
// Open opens a postgres database with a schema
|
|
|
|
func Open(connstr string, schemaPrefix string) (*DB, error) {
|
2019-04-02 17:52:25 +01:00
|
|
|
schemaName := schemaPrefix + "-" + CreateRandomTestingSchemaName(8)
|
2019-02-14 13:33:42 +00:00
|
|
|
|
|
|
|
db, err := sql.Open("postgres", ConnstrWithSchema(connstr, schemaName))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-06-04 22:30:21 +01:00
|
|
|
dbutil.Configure(db, mon)
|
2019-05-21 15:30:06 +01:00
|
|
|
|
2019-02-14 13:33:42 +00:00
|
|
|
err = CreateSchema(db, schemaName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Combine(err, db.Close())
|
|
|
|
}
|
|
|
|
|
|
|
|
return &DB{db, schemaName}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close closes the database and deletes the schema.
|
|
|
|
func (db *DB) Close() error {
|
|
|
|
return errs.Combine(
|
|
|
|
DropSchema(db.DB, db.Schema),
|
|
|
|
db.DB.Close(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadSchemaFromSQL inserts script into connstr and loads schema.
|
|
|
|
func LoadSchemaFromSQL(connstr, script string) (_ *dbschema.Schema, err error) {
|
|
|
|
db, err := Open(connstr, "load-schema")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer func() { err = errs.Combine(err, db.Close()) }()
|
|
|
|
|
|
|
|
_, err = db.Exec(script)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return QuerySchema(db)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadSnapshotFromSQL inserts script into connstr and loads schema.
|
|
|
|
func LoadSnapshotFromSQL(connstr, script string) (_ *dbschema.Snapshot, err error) {
|
|
|
|
db, err := Open(connstr, "load-schema")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer func() { err = errs.Combine(err, db.Close()) }()
|
|
|
|
|
|
|
|
_, err = db.Exec(script)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
snapshot, err := QuerySnapshot(db)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
snapshot.Script = script
|
|
|
|
return snapshot, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// QuerySnapshot loads snapshot from database
|
|
|
|
func QuerySnapshot(db dbschema.Queryer) (*dbschema.Snapshot, error) {
|
|
|
|
schema, err := QuerySchema(db)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := QueryData(db, schema)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &dbschema.Snapshot{
|
|
|
|
Version: -1,
|
|
|
|
Schema: schema,
|
|
|
|
Data: data,
|
|
|
|
}, err
|
|
|
|
}
|
2019-03-12 13:29:13 +00:00
|
|
|
|
|
|
|
//CheckApplicationName ensures that the Connection String contains an application name
|
|
|
|
func CheckApplicationName(s string) (r string) {
|
|
|
|
if !strings.Contains(s, "application_name") {
|
|
|
|
if !strings.Contains(s, "?") {
|
|
|
|
r = s + "?application_name=Satellite"
|
|
|
|
return
|
|
|
|
}
|
|
|
|
r = s + "&application_name=Satellite"
|
|
|
|
return
|
|
|
|
}
|
|
|
|
//return source as is if application_name is set
|
|
|
|
return s
|
|
|
|
}
|
2019-04-04 15:42:01 +01:00
|
|
|
|
|
|
|
// IsConstraintError checks if given error is about constraint violation
|
|
|
|
func IsConstraintError(err error) bool {
|
2019-04-23 12:13:57 +01:00
|
|
|
return errs2.IsFunc(err, func(err error) bool {
|
|
|
|
if e, ok := err.(*pq.Error); ok {
|
|
|
|
if e.Code.Class() == "23" {
|
|
|
|
return true
|
|
|
|
}
|
2019-04-04 15:42:01 +01:00
|
|
|
}
|
2019-04-23 12:13:57 +01:00
|
|
|
return false
|
|
|
|
})
|
2019-04-04 15:42:01 +01:00
|
|
|
}
|