diff options
Diffstat (limited to 'src/pool-import/rddb.go')
-rw-r--r-- | src/pool-import/rddb.go | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/src/pool-import/rddb.go b/src/pool-import/rddb.go new file mode 100644 index 0000000..7d2e4c2 --- /dev/null +++ b/src/pool-import/rddb.go @@ -0,0 +1,228 @@ +// +// pool-import +// +// Copyright (C) 2016 Christian Pointner <equinox@helsinki.at> +// +// This file is part of pool-import. +// +// pool-import is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// pool-import is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with pool-import. If not, see <http://www.gnu.org/licenses/>. +// + +package main + +import ( + "database/sql" + "fmt" + "regexp" + "strconv" + "strings" + "time" + + "github.com/ziutek/mymysql/godrv" +) + +var ( + mysqlTableNameRe = regexp.MustCompile(`^[_0-9a-zA-Z-]+$`) +) + +const ( + DB_VERSION = 182 + defaultCartTitle = "[new cart]" +) + +type CutListEntry struct { + Number uint + Evergreen bool + Description string + Duration time.Duration + Imported NullTime + NumPlayed uint + LastPlayed NullTime +} + +type CartListEntry struct { + Number uint + Exists bool + Artist string + Title string + Album string + Cuts []CutListEntry +} + +type PoolListEntry struct { + Group string + Description string +} + +type getPoolCartListResult struct { + carts map[uint]CartListEntry + err error +} + +type getPoolCartListRequest struct { + pool PoolListEntry + response chan<- getPoolCartListResult +} + +type db struct { + conf *config + dbh *sql.DB + getPoolCartListChan chan getPoolCartListRequest + getPoolCartsStmt *sql.Stmt + quit chan bool + done chan bool +} + +func (d *db) init() (err error) { + godrv.Register("SET CHARACTER SET utf8;") + + dsn := fmt.Sprintf("tcp:%s:3306*%s/%s/%s", d.conf.dbHost, d.conf.dbDb, d.conf.dbUser, d.conf.dbPasswd) + if d.dbh, err = sql.Open("mymysql", dsn); err != nil { + return + } + + var dbver int + err = d.dbh.QueryRow("select DB from VERSION;").Scan(&dbver) + if err != nil { + err = fmt.Errorf("fetching version: %s", err) + return + } + if dbver != DB_VERSION { + err = fmt.Errorf("version mismatch is %d, should be %d", dbver, DB_VERSION) + return + } + + if d.getPoolCartsStmt, err = d.dbh.Prepare("select CART.NUMBER,CART.ARTIST,CART.TITLE,CART.ALBUM,CUTS.CUT_NAME,CUTS.EVERGREEN,CUTS.DESCRIPTION,CUTS.LENGTH,CUTS.ORIGIN_DATETIME,CUTS.PLAY_COUNTER,CUTS.LAST_PLAY_DATETIME from CUTS,CART where CUTS.CART_NUMBER = CART.NUMBER and CART.GROUP_NAME = ?;"); err != nil { + return + } + + return +} + +func (d *db) getPoolCartList(pool PoolListEntry) (result getPoolCartListResult) { + var rows *sql.Rows + if rows, result.err = d.getPoolCartsStmt.Query(pool.Group); result.err != nil { + return + } + defer rows.Close() + + result.carts = make(map[uint]CartListEntry) + for rows.Next() { + var cut CutListEntry + var cart, length uint + var cutName, evergreen string + var artist, title, album sql.NullString + + if result.err = rows.Scan(&cart, &artist, &title, &album, &cutName, &evergreen, &cut.Description, &length, &cut.Imported, &cut.NumPlayed, &cut.LastPlayed); result.err != nil { + return + } + + parts := strings.Split(cutName, "_") + if len(parts) == 2 { + if cn, converr := strconv.ParseUint(parts[1], 10, 32); converr != nil { + continue + } else { + cut.Number = uint(cn) + } + } else { + continue + } + switch evergreen { + case "Y": + cut.Evergreen = true + default: + cut.Evergreen = false + } + cut.Duration = time.Duration(length) * time.Millisecond + + if c, exists := result.carts[cart]; !exists { + c = CartListEntry{cart, true, artist.String, title.String, album.String, nil} + c.Cuts = append(c.Cuts, cut) + result.carts[cart] = c + } else { + c.Cuts = append(c.Cuts, cut) + } + } + result.err = rows.Err() + return +} + +func (d *db) dispatchRequests() { + defer func() { d.done <- true }() + for { + select { + case <-d.quit: + return + case req := <-d.getPoolCartListChan: + req.response <- d.getPoolCartList(req.pool) + } + } +} + +// ********************************************************* +// Public Interface + +type DB struct { + getPoolCartListChan chan<- getPoolCartListRequest +} + +func (d *DB) GetPoolCartList(pool PoolListEntry) (map[uint]CartListEntry, error) { + resCh := make(chan getPoolCartListResult) + req := getPoolCartListRequest{} + req.pool = pool + req.response = resCh + d.getPoolCartListChan <- req + + res := <-resCh + if res.err != nil { + return nil, res.err + } + return res.carts, nil +} + +func (d *db) GetInterface() *DB { + ch := &DB{} + ch.getPoolCartListChan = d.getPoolCartListChan + return ch +} + +func (d *db) Cleanup() { + d.quit <- true + <-d.done + close(d.quit) + close(d.done) + if d.dbh != nil { + d.dbh.Close() + } + if d.getPoolCartsStmt != nil { + d.getPoolCartsStmt.Close() + } +} + +func NewDB(configfile string) (d *db, err error) { + d = new(db) + if d.conf, err = newConfig(configfile); err != nil { + return + } + d.quit = make(chan bool) + d.done = make(chan bool) + d.getPoolCartListChan = make(chan getPoolCartListRequest, 10) + + if err = d.init(); err != nil { + return + } + + go d.dispatchRequests() + return +} |