package postgres import ( "database/sql" "time" "github.com/kcotugno/tacitus" ) const ( trade_columns = `trade_id, product, price, size, buy, sell, timestamp` trade_insert = `INSERT INTO trades (` + trade_columns + `) VALUES ` + `($1, $2, $3, $4, $5, $6, $7) RETURNING id;` trade_find = `SELECT id, ` + trade_columns + ` FROM trades WHERE id = $1;` trade_find_trade_id = `SELECT id, ` + trade_columns + ` FROM trades ` + `WHERE trade_id = $1 AND product = $2;` trade_delete = `DELETE FROM trades WHERE id = $1;` trade_in_date_range = `SELECT id, ` + trade_columns + ` FROM ` + `trades WHERE product = $1 AND timestamp >= $2 AND timestamp < $3;` trade_first = `SELECT id, ` + trade_columns + ` FROM trades WHERE ` + `product = $1 ORDER BY trade_id ASC LIMIT $2;` trade_last = `SELECT id, ` + trade_columns + ` FROM trades WHERE ` + `product = $1 ORDER BY trade_id DESC LIMIT $2;` trade_after = `SELECT id, ` + trade_columns + ` FROM trades ` + `WHERE product = $1 AND trade_id < $2 ORDER BY trade_id DESC LIMIT $3;` trade_before = `SELECT id, ` + trade_columns + ` FROM trades ` + `WHERE product = $1 AND trade_id > $2 ORDER BY trade_id ASC LIMIT $3;` ) type TradeService struct { Logger tacitus.Logger client *Client } func (t *TradeService) Trade(id int) (*tacitus.Trade, error) { var tr tacitus.Trade t.logQuery(trade_find, id) row := t.client.db.QueryRow(trade_find, id) if err := row.Scan(&tr.Id, &tr.TradeId, &tr.Product, &tr.Price, &tr.Size, &tr.Buy, &tr.Sell, &tr.Timestamp); err != nil { t.logError(trade_find, err, id) return nil, err } return &tr, nil } func (t *TradeService) TradeByTradeId(id int, prod string) (*tacitus.Trade, error) { var tr tacitus.Trade t.logQuery(trade_find_trade_id, id, prod) row := t.client.db.QueryRow(trade_find_trade_id, id, prod) if err := row.Scan(&tr.Id, &tr.TradeId, &tr.Product, &tr.Price, &tr.Size, &tr.Buy, &tr.Sell, &tr.Timestamp); err != nil { t.logError(trade_find_trade_id, err, id, prod) return nil, err } return &tr, nil } func (t *TradeService) CreateTrade(trade *tacitus.Trade) error { var id int params := []interface{}{trade.TradeId, trade.Product, trade.Price, trade.Size, trade.Buy, trade.Sell, trade.Timestamp} t.logQuery(trade_insert, params...) res := t.client.db.QueryRow(trade_insert, params...) if err := res.Scan(&id); err != nil { t.logError(trade_insert, err, params...) return err } trade.Id = id return nil } func (t *TradeService) DeleteTrade(id int) { t.logQuery(trade_delete, id) _, err := t.client.db.Exec(trade_delete, id) if err != nil{ t.logError(trade_delete, err, id) } } func (t *TradeService) TradesInDateRange(product string, start, end time.Time) ([]tacitus.Trade, error) { t.logQuery(trade_in_date_range, product, start, end) rows, err := t.client.db.Query(trade_in_date_range, product, start, end) if err != nil { t.logError(trade_in_date_range, err, product, start, end) return nil, err } return deserializeTrades(rows) } func (t *TradeService) FirstTrades(product string, limit int) ([]tacitus.Trade, error) { t.logQuery(trade_first, product, limit) rows, err := t.client.db.Query(trade_first, product, limit) if err != nil { t.logError(trade_first, err, product, limit) return nil, err } return deserializeTrades(rows) } func (t *TradeService) LastTrades(product string, limit int) ([]tacitus.Trade, error) { t.logQuery(trade_last, product, limit) rows, err := t.client.db.Query(trade_last, product, limit) if err != nil { t.logError(trade_last, err, product, limit) return nil, err } return deserializeTrades(rows) } func (t *TradeService) TradesAfter(product string, id, limit int) ([]tacitus.Trade, error) { t.logQuery(trade_after, product, id, limit) rows, err := t.client.db.Query(trade_after, product, id, limit) if err != nil { t.logError(trade_after, err, product, id, limit) return nil, err } return deserializeTrades(rows) } func (t *TradeService) TradesBefore(product string, id, limit int) ([]tacitus.Trade, error) { t.logQuery(trade_before, product, id, limit) rows, err := t.client.db.Query(trade_before, product, id, limit) if err != nil { t.logError(trade_before, err, product, id, limit) return nil, err } return deserializeTrades(rows) } func (t *TradeService) logQuery(sql string, params ...interface{}) { t.log(`SQL query="%v" params="%v"`, sql, params) } func (t *TradeService) logError(sql string, err error, params ...interface{}) { t.log(`SQL FAIL query="%v" params="%v" error="%v"`, sql, params, err) } func (t *TradeService) log(format string, params ...interface{}) { if t.Logger != nil { t.Logger.Info(format, params...) } } func deserializeTrades(rows *sql.Rows) ([]tacitus.Trade, error) { defer rows.Close() trades := make([]tacitus.Trade, 0) for rows.Next() { var trade tacitus.Trade if err := rows.Scan(&trade.Id, &trade.TradeId, &trade.Product, &trade.Price, &trade.Size, &trade.Buy, &trade.Sell, &trade.Timestamp); err != nil { return nil, err } trades = append(trades, trade) } if rows.Err() != nil { return nil, rows.Err() } return trades, nil }