database(feat) add logging
This commit is contained in:
parent
2eb52068f6
commit
3820bd3aa8
|
@ -13,14 +13,17 @@ import (
|
|||
"errors"
|
||||
"os"
|
||||
|
||||
hlogger "git.licolas.net/hoffman/server/logger"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var (
|
||||
/* *** Errors *** */
|
||||
ErrInvalidOperator = errors.New("invalid sql constraint joining operator - must be AND/OR")
|
||||
|
||||
sqlSelectLastId = "SELECT last_insert_rowid();"
|
||||
sqlSelectLastId = "SELECT last_insert_rowid();"
|
||||
logger zerolog.Logger = hlogger.GetLogger("database")
|
||||
)
|
||||
|
||||
// Database Repository structure
|
||||
|
@ -29,6 +32,7 @@ type R struct {
|
|||
}
|
||||
|
||||
func Connect(path string) (*R, error) {
|
||||
logger.Info().Str("path", path).Msg("connecting to database")
|
||||
_, errStat := os.Stat(path)
|
||||
|
||||
db, errSql := sql.Open("sqlite3", path)
|
||||
|
@ -39,6 +43,7 @@ func Connect(path string) (*R, error) {
|
|||
r := &R{db}
|
||||
|
||||
if os.IsNotExist(errStat) {
|
||||
logger.Debug().Err(errStat).Msg("creating hoffman database")
|
||||
if err := r.CreateHoffmanDatabase(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -46,6 +51,7 @@ func Connect(path string) (*R, error) {
|
|||
return nil, errStat
|
||||
}
|
||||
|
||||
logger.Debug().Str("path", path).Msg("done connecting to database")
|
||||
return r, nil
|
||||
}
|
||||
|
||||
|
@ -54,6 +60,7 @@ func Use(db *sql.DB) *R {
|
|||
}
|
||||
|
||||
func (r *R) Disconnect() error {
|
||||
logger.Info().Msg("disconnecting from database")
|
||||
return r.db.Close()
|
||||
}
|
||||
|
||||
|
|
|
@ -58,48 +58,57 @@ func convertEANtoSQL(ean uint) *uint {
|
|||
// convert uint EAN from SQL number
|
||||
// NULL SQL EANs are mapped to 0 to allow multiple EANless products
|
||||
// other uints stay the same
|
||||
func convertEANfromSQL(ean any) (uint, error) {
|
||||
func convertEANfromSQL(ean any) (e uint, err error) {
|
||||
logger.Trace().Any("ean", ean).Msg("converting ean from sql equivalent")
|
||||
switch v := ean.(type) {
|
||||
case nil:
|
||||
return 0, nil
|
||||
e, err = 0, nil
|
||||
case int:
|
||||
if v < 0 {
|
||||
return 0, ErrNegativeEAN
|
||||
e, err = 0, ErrNegativeEAN
|
||||
} else {
|
||||
e, err = uint(v), nil
|
||||
}
|
||||
return uint(v), nil
|
||||
case int8:
|
||||
if v < 0 {
|
||||
return 0, ErrNegativeEAN
|
||||
e, err = 0, ErrNegativeEAN
|
||||
} else {
|
||||
e, err = uint(v), nil
|
||||
}
|
||||
return uint(v), nil
|
||||
case int16:
|
||||
if v < 0 {
|
||||
return 0, ErrNegativeEAN
|
||||
e, err = 0, ErrNegativeEAN
|
||||
} else {
|
||||
e, err = uint(v), nil
|
||||
}
|
||||
return uint(v), nil
|
||||
case int32:
|
||||
if v < 0 {
|
||||
return 0, ErrNegativeEAN
|
||||
e, err = 0, ErrNegativeEAN
|
||||
} else {
|
||||
e, err = uint(v), nil
|
||||
}
|
||||
return uint(v), nil
|
||||
case int64:
|
||||
if v < 0 {
|
||||
return 0, ErrNegativeEAN
|
||||
e, err = 0, ErrNegativeEAN
|
||||
} else {
|
||||
e, err = uint(v), nil
|
||||
}
|
||||
return uint(v), nil
|
||||
case uint:
|
||||
return uint(v), nil
|
||||
e, err = uint(v), nil
|
||||
case uint8:
|
||||
return uint(v), nil
|
||||
e, err = uint(v), nil
|
||||
case uint16:
|
||||
return uint(v), nil
|
||||
e, err = uint(v), nil
|
||||
case uint32:
|
||||
return uint(v), nil
|
||||
e, err = uint(v), nil
|
||||
case uint64:
|
||||
return uint(v), nil
|
||||
e, err = uint(v), nil
|
||||
default:
|
||||
return 0, ErrInvalidEANType
|
||||
e, err = 0, ErrInvalidEANType
|
||||
}
|
||||
|
||||
logger.Trace().Uint("ean", e).Msg("done converting ean")
|
||||
return
|
||||
}
|
||||
|
||||
// Scan products from SQL rows into product structures
|
||||
|
@ -107,6 +116,7 @@ func convertEANfromSQL(ean any) (uint, error) {
|
|||
// The first argument is a SQL Transaction pointer. If none (nil) is passed,
|
||||
// then one is created. The other two arguments are directly passed to tx.Query
|
||||
func (r *R) scanProducts(tx *sql.Tx, query string, args ...any) ([]*types.Product, error) {
|
||||
logger.Trace().Bool("transaction", tx != nil).Str("query", query).Any("args", args).Msg("excuting query and scanning products")
|
||||
if tx == nil {
|
||||
var err error
|
||||
tx, err = r.db.Begin()
|
||||
|
@ -142,50 +152,61 @@ func (r *R) scanProducts(tx *sql.Tx, query string, args ...any) ([]*types.Produc
|
|||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Any("ean", ean).Msg("converting ean")
|
||||
if product.EAN, err = convertEANfromSQL(ean); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Uint("product-id", product.Id).Msg("adding tags")
|
||||
if product.Tags, err = r.SelectProductTaggingByPid(product.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Any("product", product).Msg("adding found product")
|
||||
|
||||
products = append(products, &product)
|
||||
}
|
||||
|
||||
// This is only a query, no data is modified so there is no need to commit.
|
||||
|
||||
logger.Trace().Int("products", len(products)).Msg("done querying and scanning products")
|
||||
return products, nil
|
||||
}
|
||||
|
||||
// Scan products and assert uniqueness
|
||||
func (r *R) scanUniqueProduct(tx *sql.Tx, query string, args ...any) (*types.Product, error) {
|
||||
products, err := r.scanProducts(tx, query, args...)
|
||||
func (r *R) scanUniqueProduct(tx *sql.Tx, query string, args ...any) (product *types.Product, err error) {
|
||||
logger.Trace().Bool("transaction", tx != nil).Str("query", query).Any("args", args).Msg("excuting query and scanning one product")
|
||||
|
||||
products, err := r.scanProducts(tx, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch len(products) {
|
||||
case 0:
|
||||
return new(types.Product), nil
|
||||
product, err = new(types.Product), nil
|
||||
case 1:
|
||||
return products[0], nil
|
||||
product, err = products[0], nil
|
||||
default:
|
||||
return nil, ErrNonUniqueIdentifier
|
||||
product, err = nil, ErrNonUniqueIdentifier
|
||||
}
|
||||
|
||||
logger.Trace().Any("product", product).Msg("found product")
|
||||
return
|
||||
}
|
||||
|
||||
func (r *R) CreateProductTable() error {
|
||||
logger.Debug().Msg("creating product table")
|
||||
_, err := r.db.Exec(sqlCreateProducts)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *R) SelectAllProducts() ([]*types.Product, error) {
|
||||
logger.Trace().Msg("selecting all products")
|
||||
return r.scanProducts(nil, sqlSelectAllProducts)
|
||||
}
|
||||
|
||||
func (r *R) SelectProductById(id uint) (*types.Product, error) {
|
||||
logger.Trace().Uint("id", id).Msg("selecting product by id")
|
||||
return r.scanUniqueProduct(nil, sqlSelectProductById, id)
|
||||
}
|
||||
|
||||
|
@ -194,6 +215,7 @@ func (r *R) SelectProductByEAN(ean uint) (*types.Product, error) {
|
|||
}
|
||||
|
||||
func (r *R) SelectProductsLike(ps *types.ProductSearch) ([]*types.Product, error) {
|
||||
logger.Trace().Any("search", ps).Msg("selecting product like search")
|
||||
if !ps.Valid() {
|
||||
return nil, types.ErrInvalidSearch
|
||||
}
|
||||
|
@ -202,6 +224,7 @@ func (r *R) SelectProductsLike(ps *types.ProductSearch) ([]*types.Product, error
|
|||
}
|
||||
|
||||
func (r *R) InsertProduct(product *types.Product) error {
|
||||
logger.Trace().Any("product", product).Msg("inserting new product")
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -225,24 +248,29 @@ func (r *R) InsertProduct(product *types.Product) error {
|
|||
return err
|
||||
}
|
||||
product.Id = uint(id)
|
||||
logger.Trace().Any("product", product).Int64("new-id", id).Msg("new product id")
|
||||
|
||||
for _, tag := range product.Tags {
|
||||
logger.Trace().Any("product", product).Int64("new-id", id).Uint("tag", tag.Id).Msg("adding tag")
|
||||
if _, err := tx.Exec(sqlInsertProductTagging, id, tag.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logger.Trace().Any("product", product).Int64("new-id", id).Msg("selecting insertion")
|
||||
newProduct, err := r.scanUniqueProduct(tx, sqlSelectProductById, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Trace().Any("product", product).Int64("new-id", id).Msg("updating product")
|
||||
product.Tags = newProduct.Tags
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (r *R) UpdateProduct(product *types.Product) (*types.Product, error) {
|
||||
logger.Trace().Any("product", product).Msg("updating product")
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -261,6 +289,7 @@ func (r *R) UpdateProduct(product *types.Product) (*types.Product, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Any("product", product).Msg("deleting taggings")
|
||||
if _, err := tx.Exec(sqlDeleteProductProductTagging, product.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -271,6 +300,7 @@ func (r *R) UpdateProduct(product *types.Product) (*types.Product, error) {
|
|||
}
|
||||
|
||||
for _, tag := range product.Tags {
|
||||
logger.Trace().Any("product", product).Uint("tag-id", tag.Id).Msg("inserting tagging")
|
||||
if _, err := stmt.Exec(product.Id, tag.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -280,25 +310,31 @@ func (r *R) UpdateProduct(product *types.Product) (*types.Product, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Any("product", product).Msg("returning selection")
|
||||
return r.scanUniqueProduct(nil, sqlSelectProductById, product.Id)
|
||||
}
|
||||
|
||||
func (r *R) DeleteProduct(id uint) (*types.Product, error) {
|
||||
logger := logger.With().Uint("product-id", id).Logger()
|
||||
logger.Trace().Msg("deleting product")
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
logger.Trace().Msg("selecting product")
|
||||
product, err := r.scanUniqueProduct(tx, sqlSelectProductById, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Msg("deleting taggings")
|
||||
if _, err := tx.Exec(sqlDeleteProductProductTagging, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Msg("deleting product")
|
||||
if _, err := tx.Exec(sqlDeleteProduct, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -307,6 +343,7 @@ func (r *R) DeleteProduct(id uint) (*types.Product, error) {
|
|||
}
|
||||
|
||||
func (r *R) DropProductTable() error {
|
||||
logger.Info().Msg("dropping product table")
|
||||
_, err := r.db.Exec(sqlDropProducts)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ const (
|
|||
)
|
||||
|
||||
func (r *R) scanProductTags(tx *sql.Tx, query string, args ...any) ([]*types.Tag, error) {
|
||||
logger.Trace().Bool("transaction", tx != nil).Str("query", query).Any("args", args).Msg("excuting query and scanning tags")
|
||||
if tx == nil {
|
||||
var err error
|
||||
tx, err = r.db.Begin()
|
||||
|
@ -45,6 +46,7 @@ func (r *R) scanProductTags(tx *sql.Tx, query string, args ...any) ([]*types.Tag
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var tags []*types.Tag = []*types.Tag{}
|
||||
for rows.Next() {
|
||||
|
@ -54,13 +56,17 @@ func (r *R) scanProductTags(tx *sql.Tx, query string, args ...any) ([]*types.Tag
|
|||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Any("tag", tag).Msg("adding found tag")
|
||||
tags = append(tags, &tag)
|
||||
}
|
||||
|
||||
logger.Trace().Int("tags", len(tags)).Msg("done querying and scanning tags")
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func (r *R) scanUniqueProductTag(tx *sql.Tx, query string, args ...any) (*types.Tag, error) {
|
||||
func (r *R) scanUniqueProductTag(tx *sql.Tx, query string, args ...any) (tag *types.Tag, err error) {
|
||||
logger.Trace().Bool("transaction", tx != nil).Str("query", query).Any("args", args).Msg("excuting query and scanning one tag")
|
||||
|
||||
tags, err := r.scanProductTags(tx, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -68,41 +74,51 @@ func (r *R) scanUniqueProductTag(tx *sql.Tx, query string, args ...any) (*types.
|
|||
|
||||
switch len(tags) {
|
||||
case 0:
|
||||
return new(types.Tag), nil
|
||||
tag, err = new(types.Tag), nil
|
||||
case 1:
|
||||
return tags[0], nil
|
||||
tag, err = tags[0], nil
|
||||
default:
|
||||
return nil, ErrNonUniqueIdentifier
|
||||
tag, err = nil, ErrNonUniqueIdentifier
|
||||
}
|
||||
|
||||
logger.Trace().Any("tag", tag).Msg("found tag")
|
||||
return
|
||||
}
|
||||
|
||||
func (r *R) DropProductTagTable() error {
|
||||
logger.Info().Msg("dropping product tag table")
|
||||
_, err := r.db.Exec(sqlDropProductTags)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *R) CreateProductTagTable() error {
|
||||
logger.Info().Msg("creating product tag table")
|
||||
_, err := r.db.Exec(sqlCreateProductTags)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *R) SelectAllProductTags() ([]*types.Tag, error) {
|
||||
logger.Trace().Msg("selecting all tags")
|
||||
return r.scanProductTags(nil, sqlSelectAllProductTags)
|
||||
}
|
||||
|
||||
func (r *R) SelectProductTagById(id uint) (*types.Tag, error) {
|
||||
logger.Trace().Uint("id", id).Msg("selecting tag by id")
|
||||
return r.scanUniqueProductTag(nil, sqlSelectProductTagById, id)
|
||||
}
|
||||
|
||||
func (r *R) SelectProductTagByName(name string) (*types.Tag, error) {
|
||||
logger.Trace().Str("name", name).Msg("selecting tag by name")
|
||||
return r.scanUniqueProductTag(nil, sqlSelectProductTagByName, name)
|
||||
}
|
||||
|
||||
func (r *R) SelectProductTagsLikeName(name string) ([]*types.Tag, error) {
|
||||
logger.Trace().Str("name", name).Msg("selecting tag like name")
|
||||
return r.scanProductTags(nil, sqlSelectProductTagsLikeName, name)
|
||||
}
|
||||
|
||||
func (r *R) InsertProductTag(tag *types.Tag) error {
|
||||
logger.Trace().Any("tag", tag).Msg("inserting new tag")
|
||||
result, err := r.db.Exec(sqlInsertProductTag, tag.Name, tag.Color)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -112,13 +128,14 @@ func (r *R) InsertProductTag(tag *types.Tag) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tag.Id = uint(id)
|
||||
logger.Trace().Uint("new-id", tag.Id).Msg("new tag id")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *R) UpdateProductTag(tag *types.Tag) (*types.Tag, error) {
|
||||
logger.Trace().Any("tag", tag).Msg("updating tag")
|
||||
if _, err := r.db.Exec(sqlUpdateProductTag, tag.Name, tag.Color, tag.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -127,21 +144,26 @@ func (r *R) UpdateProductTag(tag *types.Tag) (*types.Tag, error) {
|
|||
}
|
||||
|
||||
func (r *R) DeleteProductTag(id uint) (*types.Tag, error) {
|
||||
logger := logger.With().Uint("tag-id", id).Logger()
|
||||
logger.Trace().Msg("deleting tag")
|
||||
tx, err := r.db.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
logger.Trace().Msg("selecting tag")
|
||||
tags, err := r.scanProductTags(tx, sqlSelectProductTagById, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Msg("deleting taggings")
|
||||
if _, err := tx.Exec(sqlDeleteTagProductTagging, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Msg("deleting tag")
|
||||
if _, err := tx.Exec(sqlDeleteProductTag, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -45,16 +45,22 @@ const (
|
|||
)
|
||||
|
||||
func (r *R) DropProductTaggingTable() error {
|
||||
logger.Info().Msg("dropping product tagging table")
|
||||
_, err := r.db.Exec(sqlDropProductTagging)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *R) CreateProductTaggingTable() error {
|
||||
logger.Info().Msg("creating product tagging table")
|
||||
_, err := r.db.Exec(sqlCreateProductTagging)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *R) SelectProductTaggingByPid(pid uint) ([]types.Tag, error) {
|
||||
logger.
|
||||
Trace().
|
||||
Uint("product-id", pid).
|
||||
Msg("querying taggings by product and scanning tags")
|
||||
rows, err := r.db.Query(sqlSelectProductTaggingByPid, pid)
|
||||
|
||||
if err != nil {
|
||||
|
@ -67,13 +73,22 @@ func (r *R) SelectProductTaggingByPid(pid uint) ([]types.Tag, error) {
|
|||
if err := rows.Scan(&tag.Id, &tag.Name, &tag.Color); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Trace().Any("tag", tag).Msg("adding tag")
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
|
||||
logger.
|
||||
Trace().
|
||||
Int("tags", len(tags)).
|
||||
Msg("done querying taggings by product and scanning tags")
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func (r *R) SelectProductTaggingByTid(tid uint) ([]*types.Product, error) {
|
||||
logger := logger.With().Uint("tag-id", tid).Logger()
|
||||
logger.
|
||||
Trace().
|
||||
Msg("querying taggings by tag and scanning products")
|
||||
rows, err := r.db.Query(sqlSelectProductTaggingByTid, tid)
|
||||
|
||||
if err != nil {
|
||||
|
@ -88,18 +103,23 @@ func (r *R) SelectProductTaggingByTid(tid uint) ([]*types.Product, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Uint("product-id", pid).Msg("selecting product")
|
||||
product, err := r.SelectProductById(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Any("product", pid).Msg("adding found product")
|
||||
products = append(products, product)
|
||||
}
|
||||
|
||||
logger.Trace().Int("products", len(products)).Msg("found products")
|
||||
return products, nil
|
||||
}
|
||||
|
||||
func (r *R) SelectProductTaggingLikeTagName(name string) ([]*types.Product, error) {
|
||||
logger := logger.With().Str("name", name).Logger()
|
||||
logger.Trace().Msg("selecting product taggings like tag name")
|
||||
rows, err := r.db.Query(sqlSelectProductTaggingLikeTagName, name)
|
||||
|
||||
if err != nil {
|
||||
|
@ -114,33 +134,40 @@ func (r *R) SelectProductTaggingLikeTagName(name string) ([]*types.Product, erro
|
|||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Uint("product-id", pid).Msg("selecting product")
|
||||
product, err := r.SelectProductById(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Trace().Any("product", pid).Msg("adding found product")
|
||||
products = append(products, product)
|
||||
}
|
||||
|
||||
logger.Trace().Int("products", len(products)).Msg("found products")
|
||||
return products, nil
|
||||
}
|
||||
|
||||
func (r *R) InsertProductTagging(pid, tid uint) error {
|
||||
logger.Trace().Uint("product-id", pid).Uint("tag-id", tid).Msg("inserting tagging")
|
||||
_, err := r.db.Exec(sqlInsertProductTagging, pid, tid)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *R) DeleteOneProductTagging(pid, tid uint) error {
|
||||
logger.Trace().Uint("product-id", pid).Uint("tag-id", tid).Msg("deleting tagging")
|
||||
_, err := r.db.Exec(sqlDeleteOneProductTagging, pid, tid)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *R) DeleteProductProductTagging(pid uint) error {
|
||||
logger.Trace().Uint("product-id", pid).Msg("deleting tagging by product")
|
||||
_, err := r.db.Exec(sqlDeleteProductProductTagging, pid)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *R) DeleteTagProductTagging(tid uint) error {
|
||||
logger.Trace().Uint("tag-id", tid).Msg("deleting tagging by tag")
|
||||
_, err := r.db.Exec(sqlDeleteTagProductTagging, tid)
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue