multinode/multinodedb: add sqlite3 support
Change-Id: I023ffb75f836de2c33eb7dbee52072e1622448bd
This commit is contained in:
parent
dc2bec9f89
commit
2e1455bc55
@ -21,7 +21,7 @@ import (
|
||||
|
||||
// Config defines multinode configuration.
|
||||
type Config struct {
|
||||
Database string `help:"multinode database connection string" releaseDefault:"postgres://" devDefault:"postgres://"`
|
||||
Database string `help:"multinode database connection string" default:"sqlite3://file:$CONFDIR/master.db"`
|
||||
|
||||
multinode.Config
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ package multinodedb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/spacemonkeygo/monkit/v3"
|
||||
"github.com/zeebo/errs"
|
||||
@ -48,14 +49,17 @@ func Open(ctx context.Context, log *zap.Logger, databaseURL string) (multinode.D
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: do we need cockroach implementation?
|
||||
if implementation != dbutil.Postgres && implementation != dbutil.Cockroach {
|
||||
return nil, Error.New("unsupported driver %q", driver)
|
||||
}
|
||||
|
||||
source, err = pgutil.CheckApplicationName(source, "storagenode-multinode")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
switch implementation {
|
||||
case dbutil.SQLite3:
|
||||
source = sqlite3SetDefaultOptions(source)
|
||||
case dbutil.Postgres:
|
||||
source, err = pgutil.CheckApplicationName(source, "multinode")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, Error.New("unsupported driver %q", driver)
|
||||
}
|
||||
|
||||
dbxDB, err := dbx.Open(driver, source)
|
||||
@ -63,6 +67,7 @@ func Open(ctx context.Context, log *zap.Logger, databaseURL string) (multinode.D
|
||||
return nil, Error.New("failed opening database via DBX at %q: %v",
|
||||
source, err)
|
||||
}
|
||||
|
||||
log.Debug("Connected to:", zap.String("db source", source))
|
||||
|
||||
dbutil.Configure(ctx, dbxDB.DB, "multinodedb", mon)
|
||||
@ -98,3 +103,20 @@ func (db *multinodeDB) CreateSchema(ctx context.Context) error {
|
||||
_, err := db.ExecContext(ctx, db.DB.Schema())
|
||||
return err
|
||||
}
|
||||
|
||||
// sqlite3SetDefaultOptions sets default options for disk-based db with URI filename source string
|
||||
// if no options were set.
|
||||
func sqlite3SetDefaultOptions(source string) string {
|
||||
if !strings.HasPrefix(source, "file:") {
|
||||
return source
|
||||
}
|
||||
// do not set anything for in-memory db
|
||||
if strings.HasPrefix(source, "file::memory:") {
|
||||
return source
|
||||
}
|
||||
if strings.Contains(source, "?") {
|
||||
return source
|
||||
}
|
||||
|
||||
return source + "?_journal=WAL&_busy_timeout=10000"
|
||||
}
|
||||
|
@ -1,12 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
dbx schema -d pgx multinodedb.dbx .
|
||||
dbx golang -d pgx -p dbx -t templates multinodedb.dbx .
|
||||
set -euo pipefail
|
||||
|
||||
dbx schema -d pgx -d sqlite3 multinodedb.dbx .
|
||||
dbx golang -d pgx -d sqlite3 -p dbx -t templates multinodedb.dbx .
|
||||
( printf '%s\n' '//lint:file-ignore U1000,ST1012 generated file'; cat multinodedb.dbx.go ) > multinodedb.dbx.go.tmp && mv multinodedb.dbx.go.tmp multinodedb.dbx.go
|
||||
gofmt -r "*sql.Tx -> tagsql.Tx" -w multinodedb.dbx.go
|
||||
gofmt -r "*sql.Rows -> tagsql.Rows" -w multinodedb.dbx.go
|
||||
perl -0777 -pi \
|
||||
-e 's,\t_ "github.com/jackc/pgx/v4/stdlib"\n\),\t_ "github.com/jackc/pgx/v4/stdlib"\n\n\t"storj.io/storj/private/tagsql"\n\),' \
|
||||
-e 's,\t"fmt"\n,\t"fmt"\n\t"math/rand"\n,' \
|
||||
multinodedb.dbx.go
|
||||
perl -0777 -pi \
|
||||
-e 's,\t"math/rand"\n\),\n\t"storj.io/storj/private/tagsql"\n\),' \
|
||||
multinodedb.dbx.go
|
||||
perl -0777 -pi \
|
||||
-e 's/type DB struct \{\n\t\*sql\.DB/type DB struct \{\n\ttagsql.DB/' \
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -19,6 +20,7 @@ import (
|
||||
|
||||
"github.com/jackc/pgconn"
|
||||
_ "github.com/jackc/pgx/v4/stdlib"
|
||||
"github.com/mattn/go-sqlite3"
|
||||
|
||||
"storj.io/storj/private/tagsql"
|
||||
)
|
||||
@ -145,6 +147,8 @@ func Open(driver, source string) (db *DB, err error) {
|
||||
switch driver {
|
||||
case "pgx":
|
||||
sql_db, err = openpgx(source)
|
||||
case "sqlite3":
|
||||
sql_db, err = opensqlite3(source)
|
||||
default:
|
||||
return nil, unsupportedDriver(driver)
|
||||
}
|
||||
@ -169,6 +173,8 @@ func Open(driver, source string) (db *DB, err error) {
|
||||
switch driver {
|
||||
case "pgx":
|
||||
db.dbMethods = newpgx(db)
|
||||
case "sqlite3":
|
||||
db.dbMethods = newsqlite3(db)
|
||||
default:
|
||||
return nil, unsupportedDriver(driver)
|
||||
}
|
||||
@ -309,6 +315,84 @@ func pgxLogStmt(stmt string, args ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
type sqlite3Impl struct {
|
||||
db *DB
|
||||
dialect __sqlbundle_sqlite3
|
||||
driver driver
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) Rebind(s string) string {
|
||||
return obj.dialect.Rebind(s)
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) logStmt(stmt string, args ...interface{}) {
|
||||
sqlite3LogStmt(stmt, args...)
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) makeErr(err error) error {
|
||||
constraint, ok := obj.isConstraintError(err)
|
||||
if ok {
|
||||
return constraintViolation(err, constraint)
|
||||
}
|
||||
return makeErr(err)
|
||||
}
|
||||
|
||||
type sqlite3DB struct {
|
||||
db *DB
|
||||
*sqlite3Impl
|
||||
}
|
||||
|
||||
func newsqlite3(db *DB) *sqlite3DB {
|
||||
return &sqlite3DB{
|
||||
db: db,
|
||||
sqlite3Impl: &sqlite3Impl{
|
||||
db: db,
|
||||
driver: db.DB,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (obj *sqlite3DB) Schema() string {
|
||||
return `CREATE TABLE members (
|
||||
id BLOB NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
password_hash BLOB NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
CREATE TABLE nodes (
|
||||
id BLOB NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
public_address TEXT NOT NULL,
|
||||
api_secret BLOB NOT NULL,
|
||||
PRIMARY KEY ( id )
|
||||
);`
|
||||
}
|
||||
|
||||
func (obj *sqlite3DB) wrapTx(tx tagsql.Tx) txMethods {
|
||||
return &sqlite3Tx{
|
||||
dialectTx: dialectTx{tx: tx},
|
||||
sqlite3Impl: &sqlite3Impl{
|
||||
db: obj.db,
|
||||
driver: tx,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type sqlite3Tx struct {
|
||||
dialectTx
|
||||
*sqlite3Impl
|
||||
}
|
||||
|
||||
func sqlite3LogStmt(stmt string, args ...interface{}) {
|
||||
// TODO: render placeholders
|
||||
if Logger != nil {
|
||||
out := fmt.Sprintf("stmt: %s\nargs: %v\n", stmt, pretty(args))
|
||||
Logger(out)
|
||||
}
|
||||
}
|
||||
|
||||
type pretty []interface{}
|
||||
|
||||
func (p pretty) Format(f fmt.State, c rune) {
|
||||
@ -1369,6 +1453,478 @@ func (obj *pgxImpl) deleteAll(ctx context.Context) (count int64, err error) {
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) Create_Node(ctx context.Context,
|
||||
node_id Node_Id_Field,
|
||||
node_name Node_Name_Field,
|
||||
node_public_address Node_PublicAddress_Field,
|
||||
node_api_secret Node_ApiSecret_Field) (
|
||||
node *Node, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
__id_val := node_id.value()
|
||||
__name_val := node_name.value()
|
||||
__public_address_val := node_public_address.value()
|
||||
__api_secret_val := node_api_secret.value()
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("INSERT INTO nodes ( id, name, public_address, api_secret ) VALUES ( ?, ?, ?, ? )")
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, __id_val, __name_val, __public_address_val, __api_secret_val)
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
__res, err := obj.driver.ExecContext(ctx, __stmt, __values...)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
__pk, err := __res.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
return obj.getLastNode(ctx, __pk)
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) Create_Member(ctx context.Context,
|
||||
member_id Member_Id_Field,
|
||||
member_email Member_Email_Field,
|
||||
member_name Member_Name_Field,
|
||||
member_password_hash Member_PasswordHash_Field) (
|
||||
member *Member, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
__now := obj.db.Hooks.Now().UTC()
|
||||
__id_val := member_id.value()
|
||||
__email_val := member_email.value()
|
||||
__name_val := member_name.value()
|
||||
__password_hash_val := member_password_hash.value()
|
||||
__created_at_val := __now
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("INSERT INTO members ( id, email, name, password_hash, created_at ) VALUES ( ?, ?, ?, ?, ? )")
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, __id_val, __email_val, __name_val, __password_hash_val, __created_at_val)
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
__res, err := obj.driver.ExecContext(ctx, __stmt, __values...)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
__pk, err := __res.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
return obj.getLastMember(ctx, __pk)
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) Get_Node_By_Id(ctx context.Context,
|
||||
node_id Node_Id_Field) (
|
||||
node *Node, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("SELECT nodes.id, nodes.name, nodes.public_address, nodes.api_secret FROM nodes WHERE nodes.id = ?")
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, node_id.value())
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
node = &Node{}
|
||||
err = obj.driver.QueryRowContext(ctx, __stmt, __values...).Scan(&node.Id, &node.Name, &node.PublicAddress, &node.ApiSecret)
|
||||
if err != nil {
|
||||
return (*Node)(nil), obj.makeErr(err)
|
||||
}
|
||||
return node, nil
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) All_Node(ctx context.Context) (
|
||||
rows []*Node, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("SELECT nodes.id, nodes.name, nodes.public_address, nodes.api_secret FROM nodes")
|
||||
|
||||
var __values []interface{}
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
__rows, err := obj.driver.QueryContext(ctx, __stmt, __values...)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
defer __rows.Close()
|
||||
|
||||
for __rows.Next() {
|
||||
node := &Node{}
|
||||
err = __rows.Scan(&node.Id, &node.Name, &node.PublicAddress, &node.ApiSecret)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
rows = append(rows, node)
|
||||
}
|
||||
if err := __rows.Err(); err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
return rows, nil
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) Get_Member_By_Email(ctx context.Context,
|
||||
member_email Member_Email_Field) (
|
||||
member *Member, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("SELECT members.id, members.email, members.name, members.password_hash, members.created_at FROM members WHERE members.email = ? LIMIT 2")
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, member_email.value())
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
__rows, err := obj.driver.QueryContext(ctx, __stmt, __values...)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
defer __rows.Close()
|
||||
|
||||
if !__rows.Next() {
|
||||
if err := __rows.Err(); err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
return nil, makeErr(sql.ErrNoRows)
|
||||
}
|
||||
|
||||
member = &Member{}
|
||||
err = __rows.Scan(&member.Id, &member.Email, &member.Name, &member.PasswordHash, &member.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
|
||||
if __rows.Next() {
|
||||
return nil, tooManyRows("Member_By_Email")
|
||||
}
|
||||
|
||||
if err := __rows.Err(); err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
|
||||
return member, nil
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) Get_Member_By_Id(ctx context.Context,
|
||||
member_id Member_Id_Field) (
|
||||
member *Member, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("SELECT members.id, members.email, members.name, members.password_hash, members.created_at FROM members WHERE members.id = ?")
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, member_id.value())
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
member = &Member{}
|
||||
err = obj.driver.QueryRowContext(ctx, __stmt, __values...).Scan(&member.Id, &member.Email, &member.Name, &member.PasswordHash, &member.CreatedAt)
|
||||
if err != nil {
|
||||
return (*Member)(nil), obj.makeErr(err)
|
||||
}
|
||||
return member, nil
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) Update_Node_By_Id(ctx context.Context,
|
||||
node_id Node_Id_Field,
|
||||
update Node_Update_Fields) (
|
||||
node *Node, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
var __sets = &__sqlbundle_Hole{}
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("UPDATE nodes SET "), __sets, __sqlbundle_Literal(" WHERE nodes.id = ?")}}
|
||||
|
||||
__sets_sql := __sqlbundle_Literals{Join: ", "}
|
||||
var __values []interface{}
|
||||
var __args []interface{}
|
||||
|
||||
if update.Name._set {
|
||||
__values = append(__values, update.Name.value())
|
||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("name = ?"))
|
||||
}
|
||||
|
||||
if len(__sets_sql.SQLs) == 0 {
|
||||
return nil, emptyUpdate()
|
||||
}
|
||||
|
||||
__args = append(__args, node_id.value())
|
||||
|
||||
__values = append(__values, __args...)
|
||||
__sets.SQL = __sets_sql
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
node = &Node{}
|
||||
_, err = obj.driver.ExecContext(ctx, __stmt, __values...)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
|
||||
var __embed_stmt_get = __sqlbundle_Literal("SELECT nodes.id, nodes.name, nodes.public_address, nodes.api_secret FROM nodes WHERE nodes.id = ?")
|
||||
|
||||
var __stmt_get = __sqlbundle_Render(obj.dialect, __embed_stmt_get)
|
||||
obj.logStmt("(IMPLIED) "+__stmt_get, __args...)
|
||||
|
||||
err = obj.driver.QueryRowContext(ctx, __stmt_get, __args...).Scan(&node.Id, &node.Name, &node.PublicAddress, &node.ApiSecret)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) UpdateNoReturn_Node_By_Id(ctx context.Context,
|
||||
node_id Node_Id_Field,
|
||||
update Node_Update_Fields) (
|
||||
err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
var __sets = &__sqlbundle_Hole{}
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("UPDATE nodes SET "), __sets, __sqlbundle_Literal(" WHERE nodes.id = ?")}}
|
||||
|
||||
__sets_sql := __sqlbundle_Literals{Join: ", "}
|
||||
var __values []interface{}
|
||||
var __args []interface{}
|
||||
|
||||
if update.Name._set {
|
||||
__values = append(__values, update.Name.value())
|
||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("name = ?"))
|
||||
}
|
||||
|
||||
if len(__sets_sql.SQLs) == 0 {
|
||||
return emptyUpdate()
|
||||
}
|
||||
|
||||
__args = append(__args, node_id.value())
|
||||
|
||||
__values = append(__values, __args...)
|
||||
__sets.SQL = __sets_sql
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
_, err = obj.driver.ExecContext(ctx, __stmt, __values...)
|
||||
if err != nil {
|
||||
return obj.makeErr(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) Update_Member_By_Id(ctx context.Context,
|
||||
member_id Member_Id_Field,
|
||||
update Member_Update_Fields) (
|
||||
member *Member, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
var __sets = &__sqlbundle_Hole{}
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("UPDATE members SET "), __sets, __sqlbundle_Literal(" WHERE members.id = ?")}}
|
||||
|
||||
__sets_sql := __sqlbundle_Literals{Join: ", "}
|
||||
var __values []interface{}
|
||||
var __args []interface{}
|
||||
|
||||
if update.Email._set {
|
||||
__values = append(__values, update.Email.value())
|
||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("email = ?"))
|
||||
}
|
||||
|
||||
if update.Name._set {
|
||||
__values = append(__values, update.Name.value())
|
||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("name = ?"))
|
||||
}
|
||||
|
||||
if update.PasswordHash._set {
|
||||
__values = append(__values, update.PasswordHash.value())
|
||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("password_hash = ?"))
|
||||
}
|
||||
|
||||
if len(__sets_sql.SQLs) == 0 {
|
||||
return nil, emptyUpdate()
|
||||
}
|
||||
|
||||
__args = append(__args, member_id.value())
|
||||
|
||||
__values = append(__values, __args...)
|
||||
__sets.SQL = __sets_sql
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
member = &Member{}
|
||||
_, err = obj.driver.ExecContext(ctx, __stmt, __values...)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
|
||||
var __embed_stmt_get = __sqlbundle_Literal("SELECT members.id, members.email, members.name, members.password_hash, members.created_at FROM members WHERE members.id = ?")
|
||||
|
||||
var __stmt_get = __sqlbundle_Render(obj.dialect, __embed_stmt_get)
|
||||
obj.logStmt("(IMPLIED) "+__stmt_get, __args...)
|
||||
|
||||
err = obj.driver.QueryRowContext(ctx, __stmt_get, __args...).Scan(&member.Id, &member.Email, &member.Name, &member.PasswordHash, &member.CreatedAt)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
return member, nil
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) Delete_Node_By_Id(ctx context.Context,
|
||||
node_id Node_Id_Field) (
|
||||
deleted bool, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("DELETE FROM nodes WHERE nodes.id = ?")
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, node_id.value())
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
__res, err := obj.driver.ExecContext(ctx, __stmt, __values...)
|
||||
if err != nil {
|
||||
return false, obj.makeErr(err)
|
||||
}
|
||||
|
||||
__count, err := __res.RowsAffected()
|
||||
if err != nil {
|
||||
return false, obj.makeErr(err)
|
||||
}
|
||||
|
||||
return __count > 0, nil
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) Delete_Member_By_Id(ctx context.Context,
|
||||
member_id Member_Id_Field) (
|
||||
deleted bool, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("DELETE FROM members WHERE members.id = ?")
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, member_id.value())
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
__res, err := obj.driver.ExecContext(ctx, __stmt, __values...)
|
||||
if err != nil {
|
||||
return false, obj.makeErr(err)
|
||||
}
|
||||
|
||||
__count, err := __res.RowsAffected()
|
||||
if err != nil {
|
||||
return false, obj.makeErr(err)
|
||||
}
|
||||
|
||||
return __count > 0, nil
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) getLastNode(ctx context.Context,
|
||||
pk int64) (
|
||||
node *Node, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("SELECT nodes.id, nodes.name, nodes.public_address, nodes.api_secret FROM nodes WHERE _rowid_ = ?")
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, pk)
|
||||
|
||||
node = &Node{}
|
||||
err = obj.driver.QueryRowContext(ctx, __stmt, pk).Scan(&node.Id, &node.Name, &node.PublicAddress, &node.ApiSecret)
|
||||
if err != nil {
|
||||
return (*Node)(nil), obj.makeErr(err)
|
||||
}
|
||||
return node, nil
|
||||
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) getLastMember(ctx context.Context,
|
||||
pk int64) (
|
||||
member *Member, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("SELECT members.id, members.email, members.name, members.password_hash, members.created_at FROM members WHERE _rowid_ = ?")
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, pk)
|
||||
|
||||
member = &Member{}
|
||||
err = obj.driver.QueryRowContext(ctx, __stmt, pk).Scan(&member.Id, &member.Email, &member.Name, &member.PasswordHash, &member.CreatedAt)
|
||||
if err != nil {
|
||||
return (*Member)(nil), obj.makeErr(err)
|
||||
}
|
||||
return member, nil
|
||||
|
||||
}
|
||||
|
||||
func (impl sqlite3Impl) isConstraintError(err error) (
|
||||
constraint string, ok bool) {
|
||||
if e, ok := err.(sqlite3.Error); ok {
|
||||
if e.Code == sqlite3.ErrConstraint {
|
||||
msg := err.Error()
|
||||
colon := strings.LastIndex(msg, ":")
|
||||
if colon != -1 {
|
||||
return strings.TrimSpace(msg[colon:]), true
|
||||
}
|
||||
return "", true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (obj *sqlite3Impl) deleteAll(ctx context.Context) (count int64, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
var __res sql.Result
|
||||
var __count int64
|
||||
__res, err = obj.driver.ExecContext(ctx, "DELETE FROM nodes;")
|
||||
if err != nil {
|
||||
return 0, obj.makeErr(err)
|
||||
}
|
||||
|
||||
__count, err = __res.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, obj.makeErr(err)
|
||||
}
|
||||
count += __count
|
||||
__res, err = obj.driver.ExecContext(ctx, "DELETE FROM members;")
|
||||
if err != nil {
|
||||
return 0, obj.makeErr(err)
|
||||
}
|
||||
|
||||
__count, err = __res.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, obj.makeErr(err)
|
||||
}
|
||||
count += __count
|
||||
|
||||
return count, nil
|
||||
|
||||
}
|
||||
|
||||
type Rx struct {
|
||||
db *DB
|
||||
tx *Tx
|
||||
@ -1617,3 +2173,36 @@ type dbMethods interface {
|
||||
func openpgx(source string) (*sql.DB, error) {
|
||||
return sql.Open("pgx", source)
|
||||
}
|
||||
|
||||
var sqlite3DriverName = func() string {
|
||||
var id [16]byte
|
||||
rand.Read(id[:])
|
||||
return fmt.Sprintf("sqlite3_%x", string(id[:]))
|
||||
}()
|
||||
|
||||
func init() {
|
||||
sql.Register(sqlite3DriverName, &sqlite3.SQLiteDriver{
|
||||
ConnectHook: sqlite3SetupConn,
|
||||
})
|
||||
}
|
||||
|
||||
// SQLite3JournalMode controls the journal_mode pragma for all new connections.
|
||||
// Since it is read without a mutex, it must be changed to the value you want
|
||||
// before any Open calls.
|
||||
var SQLite3JournalMode = "WAL"
|
||||
|
||||
func sqlite3SetupConn(conn *sqlite3.SQLiteConn) (err error) {
|
||||
_, err = conn.Exec("PRAGMA foreign_keys = ON", nil)
|
||||
if err != nil {
|
||||
return makeErr(err)
|
||||
}
|
||||
_, err = conn.Exec("PRAGMA journal_mode = "+SQLite3JournalMode, nil)
|
||||
if err != nil {
|
||||
return makeErr(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func opensqlite3(source string) (*sql.DB, error) {
|
||||
return sql.Open(sqlite3DriverName, source)
|
||||
}
|
||||
|
17
multinode/multinodedb/dbx/multinodedb.dbx.sqlite3.sql
Normal file
17
multinode/multinodedb/dbx/multinodedb.dbx.sqlite3.sql
Normal file
@ -0,0 +1,17 @@
|
||||
-- AUTOGENERATED BY storj.io/dbx
|
||||
-- DO NOT EDIT
|
||||
CREATE TABLE members (
|
||||
id BLOB NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
password_hash BLOB NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
CREATE TABLE nodes (
|
||||
id BLOB NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
public_address TEXT NOT NULL,
|
||||
api_secret BLOB NOT NULL,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
@ -104,38 +104,52 @@ func CreateMasterDBOnTopOf(ctx context.Context, log *zap.Logger, tempDB *dbutil.
|
||||
// Run method will iterate over all supported databases. Will establish
|
||||
// connection and will create tables for each DB.
|
||||
func Run(t *testing.T, test func(ctx *testcontext.Context, t *testing.T, db multinode.DB)) {
|
||||
masterDB := Database{
|
||||
Name: "Postgres",
|
||||
URL: pgtest.PickPostgres(ignoreSkip{}),
|
||||
Message: "Postgres flag missing, example: -postgres-test-db=" + pgtest.DefaultPostgres + " or use STORJ_TEST_POSTGRES environment variable.",
|
||||
databases := []Database{
|
||||
{
|
||||
Name: "Postgres",
|
||||
URL: pgtest.PickPostgres(ignoreSkip{}),
|
||||
Message: "Postgres flag missing, example: -postgres-test-db=" + pgtest.DefaultPostgres + " or use STORJ_TEST_POSTGRES environment variable.",
|
||||
},
|
||||
{
|
||||
Name: "Sqlite3",
|
||||
URL: "sqlite3://file::memory:",
|
||||
},
|
||||
}
|
||||
|
||||
t.Run(masterDB.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, database := range databases {
|
||||
dbConfig := database
|
||||
|
||||
ctx := testcontext.New(t)
|
||||
defer ctx.Cleanup()
|
||||
t.Run(dbConfig.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if masterDB.URL == "" {
|
||||
t.Skipf("Database %s connection string not provided. %s", masterDB.Name, masterDB.Message)
|
||||
}
|
||||
if dbConfig.URL == "" {
|
||||
t.Skipf("Database %s connection string not provided. %s", dbConfig.Name, dbConfig.Message)
|
||||
}
|
||||
|
||||
db, err := CreateMasterDB(ctx, zaptest.NewLogger(t), t.Name(), "T", 0, masterDB)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
err := db.Close()
|
||||
log := zaptest.NewLogger(t)
|
||||
ctx := testcontext.New(t)
|
||||
defer ctx.Cleanup()
|
||||
|
||||
var db multinode.DB
|
||||
var err error
|
||||
|
||||
if dbConfig.Name == "Postgres" {
|
||||
db, err = CreateMasterDB(ctx, log, t.Name(), "T", 0, dbConfig)
|
||||
} else {
|
||||
db, err = multinodedb.Open(ctx, log, dbConfig.URL)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
err = db.CreateSchema(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ctx.Check(db.Close)
|
||||
|
||||
test(ctx, t, db)
|
||||
})
|
||||
err = db.CreateSchema(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
test(ctx, t, db)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user