package postgres import ( "github.com/kcotugno/tacitus" "database/sql" "strings" "time" ) 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_columns + `;` 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;` trade_product = `SELECT DISTINCT product FROM trades;` trade_after_all = `SELECT id,` + trade_columns + `FROM trades WHERE product = $1 ` + `AND trade_id >= $2 ORDER BY trade_id ASC;` trade_where = `SELECT id, ` + trade_columns + `WHERE` ) type TradeService struct { client *Client } func (s *TradeService) Trade(id int) (tacitus.Trade, error) { var t tacitus.Trade s.client.logQuery(trade_find, id) row := s.client.db.QueryRow(trade_find, id) err := row.Scan(&t.Id, &t.TradeId, &t.Product, &t.Price, &t.Size, &t.Buy, &t.Sell, &t.Timestamp) if err != nil { s.client.logError(trade_find, err, id) return t, err } return t, nil } func (s *TradeService) TradeByTradeId(id int, prod string) (tacitus.Trade, error) { var t tacitus.Trade s.client.logQuery(trade_find_trade_id, id, prod) row := s.client.db.QueryRow(trade_find_trade_id, id, prod) err := row.Scan(&t.Id, &t.TradeId, &t.Product, &t.Price, &t.Size, &t.Buy, &t.Sell, &t.Timestamp) if err != nil { s.client.logError(trade_find_trade_id, err, id, prod) return t, err } return t, nil } func (s *TradeService) CreateTrade(trade tacitus.Trade) (tacitus.Trade, error) { var t tacitus.Trade params := []interface{}{trade.TradeId, trade.Product, trade.Price, trade.Size, trade.Buy, trade.Sell, trade.Timestamp} s.client.logQuery(trade_insert, params...) res := s.client.db.QueryRow(trade_insert, params...) if err := res.Scan(&t.Id, &t.TradeId, &t.Product, &t.Price, &t.Size, &t.Buy, &t.Sell, &t.Timestamp); err != nil { s.client.logError(trade_insert, err, params...) return t, err } return t, nil } func (s *TradeService) DeleteTrade(id int) { s.client.logQuery(trade_delete, id) _, err := s.client.db.Exec(trade_delete, id) if err != nil { s.client.logError(trade_delete, err, id) } } func (s *TradeService) TradesInDateRange(product string, start, end time.Time) ([]tacitus.Trade, error) { s.client.logQuery(trade_in_date_range, product, start, end) rows, err := s.client.db.Query(trade_in_date_range, product, start, end) if err != nil { s.client.logError(trade_in_date_range, err, product, start, end) return nil, err } return deserializeTrades(rows) } func (s *TradeService) FirstTrades(product string, limit int) ([]tacitus.Trade, error) { s.client.logQuery(trade_first, product, limit) rows, err := s.client.db.Query(trade_first, product, limit) if err != nil { s.client.logError(trade_first, err, product, limit) return nil, err } return deserializeTrades(rows) } func (s *TradeService) LastTrades(product string, limit int) ([]tacitus.Trade, error) { s.client.logQuery(trade_last, product, limit) rows, err := s.client.db.Query(trade_last, product, limit) if err != nil { s.client.logError(trade_last, err, product, limit) return nil, err } return deserializeTrades(rows) } func (s *TradeService) TradesAfter(product string, id, limit int) ([]tacitus.Trade, error) { s.client.logQuery(trade_after, product, id, limit) rows, err := s.client.db.Query(trade_after, product, id, limit) if err != nil { s.client.logError(trade_after, err, product, id, limit) return nil, err } return deserializeTrades(rows) } func (s *TradeService) TradesBefore(product string, id, limit int) ([]tacitus.Trade, error) { s.client.logQuery(trade_before, product, id, limit) rows, err := s.client.db.Query(trade_before, product, id, limit) if err != nil { s.client.logError(trade_before, err, product, id, limit) return nil, err } return deserializeTrades(rows) } func (s *TradeService) TradesWhere(sql string, params ...interface{}) ([]tacitus.Trade, error) { stmt := strings.Join([]string{trade_where, sql, ";"}, " ") s.client.logQuery(stmt, params...) rows, err := s.client.db.Query(stmt, params) if err != nil { s.client.logError(stmt, err, params...) return nil, err } return deserializeTrades(rows) } func (s *TradeService) TradeProducts() ([]string, error) { s.client.logQuery(trade_product) rows, err := s.client.db.Query(trade_product) if err != nil { s.client.logError(trade_product, err) return nil, err } products := []string{} for rows.Next() { var p string if err = rows.Scan(&p); err != nil { s.client.logError(trade_product, err) return nil, err } products = append(products, p) } return products, nil } func (s *TradeService) TradesAfterAll(product string, id int) (tacitus.Results, error) { s.client.logQuery(trade_after_all, product, id) rows, err := s.client.db.Query(trade_after_all, product, id) if err != nil { s.client.logError(trade_after_all, err, product, id) return nil, err } results := TradeResults{} results.rows = rows return &results, nil } 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 }