Trade history, recent trades, live trade processing and storage (#558)

* End of day commit moving packages and setting foundation into how trade processing will go

* Conformity

* tdd candle generation based on received trade data, renames orderbookbuffer back to buffer for now...

* Formalises test functions and designs the trade processor

* Theoretical amending old candles to allow any trades that were part of an old processed candle to be more accurate. Saving of candles will only occur on previous cycles, extending memory usage a bit longer

* Figures out sqlboiler for sqlite. Updates websocket entries to process trade data

* One more trade data

* Adds more exchange support

* Adds PSQL stuff

* Begins creating sql implementation

* End of day commit. Helper functions and understanding sql usage in GCT

* Adds delete and cleans up table design

* Finishes trades conceptually. Awaits candle data update in order to translate trades to candles

* Initial handling of trades in coinbene

* Proto

* Fixing of some bugs, attempting to address coinbene asset type ws issues

* Fixes up coinbene websocket implementation for the most part

* finalises coinbene websocket implementation. Adds new ability to parse currencies without a delimiter

* Implements rpc commands and adds testing

* updates the following to be compatible with trade data update: Theoretical amending old candles to allow any trades that were part of an old processed candle to be more accurate. Saving of candles will only occur on previous cycles, extending memory usage a bit longer

* Changes trade to be its own entity rather than attached to a websocket.

* Adds coverage to trades. Changes signature of `AddTradesToBuffer` to return error. Now automatically shuts down without need for channel listening. Will automatically start up again if it gets data

* Implements trade fetching at the wrapper level for a bunch of exchanges. Adds trade id to script updoot. Probably breaking change

* Implements trade fetching for all wrappers hurray hurrah. Updates all the tests

* Adds new interface func to get recent trades. Ensures GetExchangeHistory continues until conditions are met

* Adds new readme, tests all new wrapper endpoints, updates exchange_wrapper_issues to test new endpoints. Updates exchange_wrapper_coverage with new coverage... Fixes lame bug causing wrapper tests to fail from being poorly setup. Adds loopy loop to ensure that all data is captured when requesting exchange history

* Bugfix on psql migrations. Rebases latest changes, updates table design to use base and quote, updates trades to use exchange_name_id

* Adds new config field for saving trades to the database per exchange. Now exits trade processing when trade saving is not enabled. Similarly for wrapper, does not save if not enabled

* Minor bitfinex trade fixes. continues on buffer processing errors, now saves transactionid to the db

* Adds support for generating candles from candlesextended. May extend it further, idk

* Updates trade candles to be able to fill missing data with trades. Adds more tests. Also does a thing where you can forcefully override a candle based on internal trade data instead of API data

* Fixes bug where force deletions did not follow up with insertions. Adds force to candle commands

* Fixes specific exchange based issues. Extends recent trades to 24 hours where possible

* Fixes issue with saved tests. Fixes tests for trades. Adds parallel to tests. Pre-fixes people's nits

* Adds new GRPC functions to find out what data is missing from trades and candles. Fixes some assumptions from missing period code.

* Adds unique constraint. Fixes up niggling issues for wrappers and websockets

* Fixes issues with using unix times in the database trying to retrieve data via the CLI. Reduces save time to 15 seconds

* Updates trades to use timestamps instead of int64 unix

* Adds missing FTX wrapper implementation. Regens docs

* Linting the linters. Updating readme

* Adds new command to set whether an exchange can process trades

* Doc update

* Adds recent trades and historic trade endpoints to grpc

* formats pair_test.go to appease linter gods

* Addresses data race. Removes logging of missing intervals on unrelated function (now that it has its own rpc command). The buffer time isnt customisable, but I don't feel it needs to be at a config level at all really.

* Fixes a few niterinos regarding spacing, type conversion, a weird Bitmex 0 trade value error, unsubscriptions and cli command references

* Reduces map lookups. Adds base func and moves wrappers to use it

* Uses better currency formatter. Adds time based validation to trade history. Reverts configtest.json

* Reverts config and updates test names. Also WAYYYYY LESS SPAMMY

* oopsie doopsie missed a whoopsie

* mint flavoured lint

* Fixes issues caused by rebase

* Fixes issue with timestamps not converting properly from command to RPCServer. Adds new error type. Adds shorthand entries to some commands. Removes os.Exit from tests. Makes Gemini test rolling. Adds enabled exchange check to RPC function. Escapes timestamp on bitstamp. Renames var

* fixes whoopsie oopsie doopsie I forgot to remove code shoopsie

* missed a line

* 🎉 🎉 :tada:Breaks everything in an end of day commit 🎉 🎉 🎉

* Modifies function 'createlocaloffset' to return a string instead. Uses strings for all time based start and end commands. Uses UTC times in RPC server and updates SQLITE to use formatted time based queries

* Adds concurrency-safe way of changing SaveTradeData and checking it. Fixes embarrassing typo

* End of day fix, adds bitfinex update to loop until either the return trades shows no new dates, or meets specifications. Fixes egregious typo

* Improves testing and handling of historical trades function

* Fixes tests after latest changes

* Fix potential fatal err now that db is enabled in test config now

* Fixes up some database settings to use a local engine instead of global var

* DELICIOUS LINT CHOCOLATE FIXES

* Fixes data race by slashing competitor's tyres

* Adds mock test fixes to allow for live and stored data test

* Removes verbosity in engine level tests. Adds new timezone format to highlight the timezone for RPC functions. Removes reference to Preix index fund

* Oopsie doopsie, fixed a whoopsie

* Loggers can no longer do data drag races on my lawn 👴

* Removes bad lock

* Addresses command nits. End of day conceptual commit, trying to calculate spans of time in the context of missing periods. Tests will fail

* Adds new stream response for retrieving trade history as it can take time to do. Unsuccessfully attempts to simplify time range calculation for missing trades response

* Adds new timeperiods package to calculate time periods, time ranges and whether data is in those ranges. Removes kline basic implementation of same concept

* Fixes lint issues. Fixes test. Moves trade cli commands to their own trade subcommands

* Updates lakebtc to no longer have gethistorictrades as it is unsupported. Adds more validation to rpc functions

* Removes requirement to have trades when testing trade wrapper functions. Doesn't really prove it works if there are no trades for a given currency in a time period.

* Addresses nits, runs linting fix and ensures a test is consistent

* Fix merge issues

* Moves sort to timeperiods. Adds test coverage. Fixes typo

* Removes log package in CLI

* Fixes `GetTrades` url

* Reorders all instances of validation occuring after settingup RPC connection

* Fixes test to ensure that it is setup before testing that it is setup

* Fixed issue with bool retrieval. Removes double append

* Fixes Binance times, fixes bitfinex sell sides, fixes huobi times, sorts all responses

* Fixes poloniex trade id consistency. Makes recent trade for poloniex consistent with others (15 minutes). Fixes coinbene. Fixes localbitcoins to use quote currency. Fixes coinut times. Updates huobi trade id, saves okgroup trades. Fixes bid and ask to buy and sell

* Removes websocket trades for lakebtc as it did not meet our requirements for processing. Adds new constraints to the database to ensure we have uniqueness on trades where ID doesn't exist and doesn't trigger errors for trades where the tid does

* Fixes migration for postgres to downscale properly

* Really really fixes the psql index changes

* Fixes broken tests

* Now with working tests and no pocket lint

* Makes the side column nullable with no more constraint for it. adds migrations and runs generation. comments lakebtc

* Lint & Sprüngli

* Updates zb to use more appropriate side

* Fixes oopsie

* Attempts to address a data race from globals

* Fixes build

* Fixes missed regen rpc files

* Updates readme to point to trade readme. Fixes exchange_wrapper_coverage wrapper count and untested panics, tests bitfinex funding pair test for `fUSD`, adds shiny new param `tradeprocessinginterval`

* mint flavoured lint

* Uses the real default to set the default value by default

* Fixes some extra tests surrounding email sending and number incompatibility

* Reverts test config

* re-adds gom2/usdt currency

* Fixes typo, don't look!

* Fixes minor codelingo pickups

* Adds more precision to handling of trade data from Kraken. Expands test

* interface christmas tree

* lint
This commit is contained in:
Scott
2020-10-29 13:00:02 +11:00
committed by GitHub
parent 12263997c0
commit 80bc8c7e9e
204 changed files with 51177 additions and 6023 deletions

View File

@@ -0,0 +1,20 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS trade
(
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
exchange_name_id uuid REFERENCES exchange(id) NOT NULL,
tid varchar,
base varchar(30) NOT NULL,
quote varchar(30) NOT NULL,
asset varchar NOT NULL,
price DOUBLE PRECISION NOT NULL,
amount DOUBLE PRECISION NOT NULL,
side varchar NOT NULL,
timestamp TIMESTAMPTZ NOT NULL,
CONSTRAINT uniquetradeid
unique(exchange_name_id, tid),
CONSTRAINT uniquetrade
unique(exchange_name_id, base, quote, asset, price, amount, side, timestamp)
);
-- +goose Down
DROP TABLE trade;

View File

@@ -0,0 +1,20 @@
-- +goose Up
CREATE TABLE IF NOT EXISTS trade
(
id text not null primary key,
exchange_name_id uuid REFERENCES exchange(id) NOT NULL,
tid TEXT,
base text NOT NULL,
quote text NOT NULL,
asset TEXT NOT NULL,
price REAL NOT NULL,
amount REAL NOT NULL,
side TEXT NOT NULL,
timestamp TIMESTAMP NOT NULL,
CONSTRAINT uniquetradeid
unique(exchange_name_id, tid) ON CONFLICT IGNORE,
CONSTRAINT uniquetrade
unique(exchange_name_id, base, quote, asset, price, amount, side, timestamp) ON CONFLICT IGNORE
);
-- +goose Down
DROP TABLE trade;

View File

@@ -0,0 +1,14 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE trade DROP CONSTRAINT uniquetrade;
CREATE UNIQUE INDEX unique_trade_no_id ON trade (base,quote,asset,price,amount,side, timestamp)
WHERE tid IS NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP INDEX unique_trade_no_id;
ALTER TABLE trade ADD CONSTRAINT uniquetrade
unique(exchange_name_id, base, quote, asset, price, amount, side, timestamp);
-- +goose StatementEnd

View File

@@ -0,0 +1,47 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE "trade_new" (
id text not null primary key,
exchange_name_id uuid REFERENCES exchange(id) NOT NULL,
tid TEXT,
base text NOT NULL,
quote text NOT NULL,
asset TEXT NOT NULL,
price REAL NOT NULL,
amount REAL NOT NULL,
side TEXT NOT NULL,
timestamp TIMESTAMP NOT NULL,
CONSTRAINT uniquetradeid
unique(exchange_name_id, tid) ON CONFLICT IGNORE
);
INSERT INTO trade_new SELECT id, exchange_name_id, tid, base, quote, asset, price, amount, side, timestamp FROM trade;
DROP TABLE trade;
ALTER TABLE trade_new RENAME TO trade;
CREATE UNIQUE INDEX unique_trade_no_id ON trade (base,quote,asset,price,amount,side, timestamp)
WHERE tid IS NULL;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
CREATE TABLE "trade_new" (
id text not null primary key,
exchange_name_id uuid REFERENCES exchange(id) NOT NULL,
tid TEXT,
base text NOT NULL,
quote text NOT NULL,
asset TEXT NOT NULL,
price REAL NOT NULL,
amount REAL NOT NULL,
side TEXT NOT NULL,
timestamp TIMESTAMP NOT NULL,
CONSTRAINT uniquetradeid
unique(exchange_name_id, tid) ON CONFLICT IGNORE,
CONSTRAINT uniquetrade
unique(exchange_name_id, base, quote, asset, price, amount, side, timestamp) ON CONFLICT IGNORE
);
INSERT INTO trade_new SELECT id, exchange_name_id, tid, base, quote, asset, price, amount, side, timestamp FROM trade;
DROP TABLE trade;
ALTER TABLE trade_new RENAME TO trade;
-- +goose StatementEnd

View File

@@ -0,0 +1,23 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE trade ALTER COLUMN side DROP NOT NULL;
DROP INDEX unique_trade_no_id;
CREATE UNIQUE INDEX unique_trade_no_id ON trade (base,quote,asset,price,amount,timestamp)
WHERE tid IS NULL;
UPDATE TRADE set side = null where side = 'UNKNOWN';
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
UPDATE TRADE set side = '' where side IS NULL;
ALTER TABLE trade ALTER COLUMN side SET NOT NULL;
DROP INDEX unique_trade_no_id;
CREATE UNIQUE INDEX unique_trade_no_id ON trade (base,quote,asset,price,amount,side,timestamp)
WHERE tid IS NULL;
-- +goose StatementEnd

View File

@@ -0,0 +1,55 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE "trade_new" (
id text not null primary key,
exchange_name_id uuid REFERENCES exchange(id) NOT NULL,
tid TEXT,
base text NOT NULL,
quote text NOT NULL,
asset TEXT NOT NULL,
price REAL NOT NULL,
amount REAL NOT NULL,
side TEXT,
timestamp TIMESTAMP NOT NULL,
CONSTRAINT uniquetradeid
unique(exchange_name_id, tid) ON CONFLICT IGNORE
);
INSERT INTO trade_new SELECT id, exchange_name_id, tid, base, quote, asset, price, amount, side, timestamp FROM trade;
DROP TABLE trade;
ALTER TABLE trade_new RENAME TO trade;
CREATE UNIQUE INDEX unique_trade_no_id ON trade (base,quote,asset,price,amount,timestamp)
WHERE tid IS NULL;
UPDATE trade SET side = null WHERE side = 'UNKNOWN' OR side = '';
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
CREATE TABLE "trade_new" (
id text not null primary key,
exchange_name_id uuid REFERENCES exchange(id) NOT NULL,
tid TEXT,
base text NOT NULL,
quote text NOT NULL,
asset TEXT NOT NULL,
price REAL NOT NULL,
amount REAL NOT NULL,
side TEXT NOT NULL,
timestamp TIMESTAMP NOT NULL,
CONSTRAINT uniquetradeid
unique(exchange_name_id, tid) ON CONFLICT IGNORE
);
UPDATE trade SET side = '' WHERE side IS NULL;
INSERT INTO trade_new SELECT id, exchange_name_id, tid, base, quote, asset, price, amount, side, timestamp FROM trade;
DROP TABLE trade;
ALTER TABLE trade_new RENAME TO trade;
CREATE UNIQUE INDEX unique_trade_no_id ON trade (base,quote,asset,price,amount,side,timestamp)
WHERE tid IS NULL;
-- +goose StatementEnd

View File

@@ -9,6 +9,7 @@ var TableNames = struct {
Exchange string
Script string
ScriptExecution string
Trade string
WithdrawalCrypto string
WithdrawalFiat string
WithdrawalHistory string
@@ -18,6 +19,7 @@ var TableNames = struct {
Exchange: "exchange",
Script: "script",
ScriptExecution: "script_execution",
Trade: "trade",
WithdrawalCrypto: "withdrawal_crypto",
WithdrawalFiat: "withdrawal_fiat",
WithdrawalHistory: "withdrawal_history",

View File

@@ -51,15 +51,18 @@ var ExchangeWhere = struct {
// ExchangeRels is where relationship names are stored.
var ExchangeRels = struct {
ExchangeNameCandles string
ExchangeNameTrades string
ExchangeNameWithdrawalHistories string
}{
ExchangeNameCandles: "ExchangeNameCandles",
ExchangeNameTrades: "ExchangeNameTrades",
ExchangeNameWithdrawalHistories: "ExchangeNameWithdrawalHistories",
}
// exchangeR is where relationships are stored.
type exchangeR struct {
ExchangeNameCandles CandleSlice
ExchangeNameTrades TradeSlice
ExchangeNameWithdrawalHistories WithdrawalHistorySlice
}
@@ -374,6 +377,27 @@ func (o *Exchange) ExchangeNameCandles(mods ...qm.QueryMod) candleQuery {
return query
}
// ExchangeNameTrades retrieves all the trade's Trades with an executor via exchange_name_id column.
func (o *Exchange) ExchangeNameTrades(mods ...qm.QueryMod) tradeQuery {
var queryMods []qm.QueryMod
if len(mods) != 0 {
queryMods = append(queryMods, mods...)
}
queryMods = append(queryMods,
qm.Where("\"trade\".\"exchange_name_id\"=?", o.ID),
)
query := Trades(queryMods...)
queries.SetFrom(query.Query, "\"trade\"")
if len(queries.GetSelect(query.Query)) == 0 {
queries.SetSelect(query.Query, []string{"\"trade\".*"})
}
return query
}
// ExchangeNameWithdrawalHistories retrieves all the withdrawal_history's WithdrawalHistories with an executor via exchange_name_id column.
func (o *Exchange) ExchangeNameWithdrawalHistories(mods ...qm.QueryMod) withdrawalHistoryQuery {
var queryMods []qm.QueryMod
@@ -490,6 +514,101 @@ func (exchangeL) LoadExchangeNameCandles(ctx context.Context, e boil.ContextExec
return nil
}
// LoadExchangeNameTrades allows an eager lookup of values, cached into the
// loaded structs of the objects. This is for a 1-M or N-M relationship.
func (exchangeL) LoadExchangeNameTrades(ctx context.Context, e boil.ContextExecutor, singular bool, maybeExchange interface{}, mods queries.Applicator) error {
var slice []*Exchange
var object *Exchange
if singular {
object = maybeExchange.(*Exchange)
} else {
slice = *maybeExchange.(*[]*Exchange)
}
args := make([]interface{}, 0, 1)
if singular {
if object.R == nil {
object.R = &exchangeR{}
}
args = append(args, object.ID)
} else {
Outer:
for _, obj := range slice {
if obj.R == nil {
obj.R = &exchangeR{}
}
for _, a := range args {
if a == obj.ID {
continue Outer
}
}
args = append(args, obj.ID)
}
}
if len(args) == 0 {
return nil
}
query := NewQuery(qm.From(`trade`), qm.WhereIn(`trade.exchange_name_id in ?`, args...))
if mods != nil {
mods.Apply(query)
}
results, err := query.QueryContext(ctx, e)
if err != nil {
return errors.Wrap(err, "failed to eager load trade")
}
var resultSlice []*Trade
if err = queries.Bind(results, &resultSlice); err != nil {
return errors.Wrap(err, "failed to bind eager loaded slice trade")
}
if err = results.Close(); err != nil {
return errors.Wrap(err, "failed to close results in eager load on trade")
}
if err = results.Err(); err != nil {
return errors.Wrap(err, "error occurred during iteration of eager loaded relations for trade")
}
if len(tradeAfterSelectHooks) != 0 {
for _, obj := range resultSlice {
if err := obj.doAfterSelectHooks(ctx, e); err != nil {
return err
}
}
}
if singular {
object.R.ExchangeNameTrades = resultSlice
for _, foreign := range resultSlice {
if foreign.R == nil {
foreign.R = &tradeR{}
}
foreign.R.ExchangeName = object
}
return nil
}
for _, foreign := range resultSlice {
for _, local := range slice {
if local.ID == foreign.ExchangeNameID {
local.R.ExchangeNameTrades = append(local.R.ExchangeNameTrades, foreign)
if foreign.R == nil {
foreign.R = &tradeR{}
}
foreign.R.ExchangeName = local
break
}
}
}
return nil
}
// LoadExchangeNameWithdrawalHistories allows an eager lookup of values, cached into the
// loaded structs of the objects. This is for a 1-M or N-M relationship.
func (exchangeL) LoadExchangeNameWithdrawalHistories(ctx context.Context, e boil.ContextExecutor, singular bool, maybeExchange interface{}, mods queries.Applicator) error {
@@ -638,6 +757,59 @@ func (o *Exchange) AddExchangeNameCandles(ctx context.Context, exec boil.Context
return nil
}
// AddExchangeNameTrades adds the given related objects to the existing relationships
// of the exchange, optionally inserting them as new records.
// Appends related to o.R.ExchangeNameTrades.
// Sets related.R.ExchangeName appropriately.
func (o *Exchange) AddExchangeNameTrades(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Trade) error {
var err error
for _, rel := range related {
if insert {
rel.ExchangeNameID = o.ID
if err = rel.Insert(ctx, exec, boil.Infer()); err != nil {
return errors.Wrap(err, "failed to insert into foreign table")
}
} else {
updateQuery := fmt.Sprintf(
"UPDATE \"trade\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 1, []string{"exchange_name_id"}),
strmangle.WhereClause("\"", "\"", 2, tradePrimaryKeyColumns),
)
values := []interface{}{o.ID, rel.ID}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, updateQuery)
fmt.Fprintln(boil.DebugWriter, values)
}
if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil {
return errors.Wrap(err, "failed to update foreign table")
}
rel.ExchangeNameID = o.ID
}
}
if o.R == nil {
o.R = &exchangeR{
ExchangeNameTrades: related,
}
} else {
o.R.ExchangeNameTrades = append(o.R.ExchangeNameTrades, related...)
}
for _, rel := range related {
if rel.R == nil {
rel.R = &tradeR{
ExchangeName: o,
}
} else {
rel.R.ExchangeName = o
}
}
return nil
}
// AddExchangeNameWithdrawalHistories adds the given related objects to the existing relationships
// of the exchange, optionally inserting them as new records.
// Appends related to o.R.ExchangeNameWithdrawalHistories.

View File

@@ -572,6 +572,84 @@ func testExchangeToManyExchangeNameCandles(t *testing.T) {
}
}
func testExchangeToManyExchangeNameTrades(t *testing.T) {
var err error
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var a Exchange
var b, c Trade
seed := randomize.NewSeed()
if err = randomize.Struct(seed, &a, exchangeDBTypes, true, exchangeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Exchange struct: %s", err)
}
if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &b, tradeDBTypes, false, tradeColumnsWithDefault...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &c, tradeDBTypes, false, tradeColumnsWithDefault...); err != nil {
t.Fatal(err)
}
b.ExchangeNameID = a.ID
c.ExchangeNameID = a.ID
if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
check, err := a.ExchangeNameTrades().All(ctx, tx)
if err != nil {
t.Fatal(err)
}
bFound, cFound := false, false
for _, v := range check {
if v.ExchangeNameID == b.ExchangeNameID {
bFound = true
}
if v.ExchangeNameID == c.ExchangeNameID {
cFound = true
}
}
if !bFound {
t.Error("expected to find b")
}
if !cFound {
t.Error("expected to find c")
}
slice := ExchangeSlice{&a}
if err = a.L.LoadExchangeNameTrades(ctx, tx, false, (*[]*Exchange)(&slice), nil); err != nil {
t.Fatal(err)
}
if got := len(a.R.ExchangeNameTrades); got != 2 {
t.Error("number of eager loaded records wrong, got:", got)
}
a.R.ExchangeNameTrades = nil
if err = a.L.LoadExchangeNameTrades(ctx, tx, true, &a, nil); err != nil {
t.Fatal(err)
}
if got := len(a.R.ExchangeNameTrades); got != 2 {
t.Error("number of eager loaded records wrong, got:", got)
}
if t.Failed() {
t.Logf("%#v", check)
}
}
func testExchangeToManyExchangeNameWithdrawalHistories(t *testing.T) {
var err error
ctx := context.Background()
@@ -725,6 +803,81 @@ func testExchangeToManyAddOpExchangeNameCandles(t *testing.T) {
}
}
}
func testExchangeToManyAddOpExchangeNameTrades(t *testing.T) {
var err error
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var a Exchange
var b, c, d, e Trade
seed := randomize.NewSeed()
if err = randomize.Struct(seed, &a, exchangeDBTypes, false, strmangle.SetComplement(exchangePrimaryKeyColumns, exchangeColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
foreigners := []*Trade{&b, &c, &d, &e}
for _, x := range foreigners {
if err = randomize.Struct(seed, x, tradeDBTypes, false, strmangle.SetComplement(tradePrimaryKeyColumns, tradeColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
}
if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = c.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
foreignersSplitByInsertion := [][]*Trade{
{&b, &c},
{&d, &e},
}
for i, x := range foreignersSplitByInsertion {
err = a.AddExchangeNameTrades(ctx, tx, i != 0, x...)
if err != nil {
t.Fatal(err)
}
first := x[0]
second := x[1]
if a.ID != first.ExchangeNameID {
t.Error("foreign key was wrong value", a.ID, first.ExchangeNameID)
}
if a.ID != second.ExchangeNameID {
t.Error("foreign key was wrong value", a.ID, second.ExchangeNameID)
}
if first.R.ExchangeName != &a {
t.Error("relationship was not added properly to the foreign slice")
}
if second.R.ExchangeName != &a {
t.Error("relationship was not added properly to the foreign slice")
}
if a.R.ExchangeNameTrades[i*2] != first {
t.Error("relationship struct slice not set to correct value")
}
if a.R.ExchangeNameTrades[i*2+1] != second {
t.Error("relationship struct slice not set to correct value")
}
count, err := a.ExchangeNameTrades().Count(ctx, tx)
if err != nil {
t.Fatal(err)
}
if want := int64((i + 1) * 2); count != want {
t.Error("want", want, "got", count)
}
}
}
func testExchangeToManyAddOpExchangeNameWithdrawalHistories(t *testing.T) {
var err error

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,841 @@
// Code generated by SQLBoiler 3.5.0-gct (https://github.com/thrasher-corp/sqlboiler). DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package postgres
import (
"bytes"
"context"
"reflect"
"testing"
"github.com/thrasher-corp/sqlboiler/boil"
"github.com/thrasher-corp/sqlboiler/queries"
"github.com/thrasher-corp/sqlboiler/randomize"
"github.com/thrasher-corp/sqlboiler/strmangle"
)
var (
// Relationships sometimes use the reflection helper queries.Equal/queries.Assign
// so force a package dependency in case they don't.
_ = queries.Equal
)
func testTrades(t *testing.T) {
t.Parallel()
query := Trades()
if query.Query == nil {
t.Error("expected a query, got nothing")
}
}
func testTradesDelete(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if rowsAff, err := o.Delete(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testTradesQueryDeleteAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if rowsAff, err := Trades().DeleteAll(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testTradesSliceDeleteAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice := TradeSlice{o}
if rowsAff, err := slice.DeleteAll(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testTradesExists(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
e, err := TradeExists(ctx, tx, o.ID)
if err != nil {
t.Errorf("Unable to check if Trade exists: %s", err)
}
if !e {
t.Errorf("Expected TradeExists to return true, but got false.")
}
}
func testTradesFind(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
tradeFound, err := FindTrade(ctx, tx, o.ID)
if err != nil {
t.Error(err)
}
if tradeFound == nil {
t.Error("want a record, got nil")
}
}
func testTradesBind(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = Trades().Bind(ctx, tx, o); err != nil {
t.Error(err)
}
}
func testTradesOne(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if x, err := Trades().One(ctx, tx); err != nil {
t.Error(err)
} else if x == nil {
t.Error("expected to get a non nil record")
}
}
func testTradesAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
tradeOne := &Trade{}
tradeTwo := &Trade{}
if err = randomize.Struct(seed, tradeOne, tradeDBTypes, false, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
if err = randomize.Struct(seed, tradeTwo, tradeDBTypes, false, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = tradeOne.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = tradeTwo.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice, err := Trades().All(ctx, tx)
if err != nil {
t.Error(err)
}
if len(slice) != 2 {
t.Error("want 2 records, got:", len(slice))
}
}
func testTradesCount(t *testing.T) {
t.Parallel()
var err error
seed := randomize.NewSeed()
tradeOne := &Trade{}
tradeTwo := &Trade{}
if err = randomize.Struct(seed, tradeOne, tradeDBTypes, false, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
if err = randomize.Struct(seed, tradeTwo, tradeDBTypes, false, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = tradeOne.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = tradeTwo.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 2 {
t.Error("want 2 records, got:", count)
}
}
func tradeBeforeInsertHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeAfterInsertHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeAfterSelectHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeBeforeUpdateHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeAfterUpdateHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeBeforeDeleteHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeAfterDeleteHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeBeforeUpsertHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeAfterUpsertHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func testTradesHooks(t *testing.T) {
t.Parallel()
var err error
ctx := context.Background()
empty := &Trade{}
o := &Trade{}
seed := randomize.NewSeed()
if err = randomize.Struct(seed, o, tradeDBTypes, false); err != nil {
t.Errorf("Unable to randomize Trade object: %s", err)
}
AddTradeHook(boil.BeforeInsertHook, tradeBeforeInsertHook)
if err = o.doBeforeInsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeInsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeInsertHook function to empty object, but got: %#v", o)
}
tradeBeforeInsertHooks = []TradeHook{}
AddTradeHook(boil.AfterInsertHook, tradeAfterInsertHook)
if err = o.doAfterInsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterInsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterInsertHook function to empty object, but got: %#v", o)
}
tradeAfterInsertHooks = []TradeHook{}
AddTradeHook(boil.AfterSelectHook, tradeAfterSelectHook)
if err = o.doAfterSelectHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterSelectHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterSelectHook function to empty object, but got: %#v", o)
}
tradeAfterSelectHooks = []TradeHook{}
AddTradeHook(boil.BeforeUpdateHook, tradeBeforeUpdateHook)
if err = o.doBeforeUpdateHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeUpdateHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeUpdateHook function to empty object, but got: %#v", o)
}
tradeBeforeUpdateHooks = []TradeHook{}
AddTradeHook(boil.AfterUpdateHook, tradeAfterUpdateHook)
if err = o.doAfterUpdateHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterUpdateHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterUpdateHook function to empty object, but got: %#v", o)
}
tradeAfterUpdateHooks = []TradeHook{}
AddTradeHook(boil.BeforeDeleteHook, tradeBeforeDeleteHook)
if err = o.doBeforeDeleteHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeDeleteHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeDeleteHook function to empty object, but got: %#v", o)
}
tradeBeforeDeleteHooks = []TradeHook{}
AddTradeHook(boil.AfterDeleteHook, tradeAfterDeleteHook)
if err = o.doAfterDeleteHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterDeleteHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterDeleteHook function to empty object, but got: %#v", o)
}
tradeAfterDeleteHooks = []TradeHook{}
AddTradeHook(boil.BeforeUpsertHook, tradeBeforeUpsertHook)
if err = o.doBeforeUpsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeUpsertHook function to empty object, but got: %#v", o)
}
tradeBeforeUpsertHooks = []TradeHook{}
AddTradeHook(boil.AfterUpsertHook, tradeAfterUpsertHook)
if err = o.doAfterUpsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterUpsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o)
}
tradeAfterUpsertHooks = []TradeHook{}
}
func testTradesInsert(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
}
func testTradesInsertWhitelist(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Whitelist(tradeColumnsWithoutDefault...)); err != nil {
t.Error(err)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
}
func testTradeToOneExchangeUsingExchangeName(t *testing.T) {
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var local Trade
var foreign Exchange
seed := randomize.NewSeed()
if err := randomize.Struct(seed, &local, tradeDBTypes, false, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
if err := randomize.Struct(seed, &foreign, exchangeDBTypes, false, exchangeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Exchange struct: %s", err)
}
if err := foreign.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
local.ExchangeNameID = foreign.ID
if err := local.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
check, err := local.ExchangeName().One(ctx, tx)
if err != nil {
t.Fatal(err)
}
if check.ID != foreign.ID {
t.Errorf("want: %v, got %v", foreign.ID, check.ID)
}
slice := TradeSlice{&local}
if err = local.L.LoadExchangeName(ctx, tx, false, (*[]*Trade)(&slice), nil); err != nil {
t.Fatal(err)
}
if local.R.ExchangeName == nil {
t.Error("struct should have been eager loaded")
}
local.R.ExchangeName = nil
if err = local.L.LoadExchangeName(ctx, tx, true, &local, nil); err != nil {
t.Fatal(err)
}
if local.R.ExchangeName == nil {
t.Error("struct should have been eager loaded")
}
}
func testTradeToOneSetOpExchangeUsingExchangeName(t *testing.T) {
var err error
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var a Trade
var b, c Exchange
seed := randomize.NewSeed()
if err = randomize.Struct(seed, &a, tradeDBTypes, false, strmangle.SetComplement(tradePrimaryKeyColumns, tradeColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &b, exchangeDBTypes, false, strmangle.SetComplement(exchangePrimaryKeyColumns, exchangeColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &c, exchangeDBTypes, false, strmangle.SetComplement(exchangePrimaryKeyColumns, exchangeColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
for i, x := range []*Exchange{&b, &c} {
err = a.SetExchangeName(ctx, tx, i != 0, x)
if err != nil {
t.Fatal(err)
}
if a.R.ExchangeName != x {
t.Error("relationship struct not set to correct value")
}
if x.R.ExchangeNameTrades[0] != &a {
t.Error("failed to append to foreign relationship struct")
}
if a.ExchangeNameID != x.ID {
t.Error("foreign key was wrong value", a.ExchangeNameID)
}
zero := reflect.Zero(reflect.TypeOf(a.ExchangeNameID))
reflect.Indirect(reflect.ValueOf(&a.ExchangeNameID)).Set(zero)
if err = a.Reload(ctx, tx); err != nil {
t.Fatal("failed to reload", err)
}
if a.ExchangeNameID != x.ID {
t.Error("foreign key was wrong value", a.ExchangeNameID, x.ID)
}
}
}
func testTradesReload(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = o.Reload(ctx, tx); err != nil {
t.Error(err)
}
}
func testTradesReloadAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice := TradeSlice{o}
if err = slice.ReloadAll(ctx, tx); err != nil {
t.Error(err)
}
}
func testTradesSelect(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice, err := Trades().All(ctx, tx)
if err != nil {
t.Error(err)
}
if len(slice) != 1 {
t.Error("want one record, got:", len(slice))
}
}
var (
tradeDBTypes = map[string]string{`ID`: `uuid`, `ExchangeNameID`: `uuid`, `Tid`: `character varying`, `Base`: `character varying`, `Quote`: `character varying`, `Asset`: `character varying`, `Price`: `double precision`, `Amount`: `double precision`, `Side`: `character varying`, `Timestamp`: `timestamp with time zone`}
_ = bytes.MinRead
)
func testTradesUpdate(t *testing.T) {
t.Parallel()
if 0 == len(tradePrimaryKeyColumns) {
t.Skip("Skipping table with no primary key columns")
}
if len(tradeAllColumns) == len(tradePrimaryKeyColumns) {
t.Skip("Skipping table with only primary key columns")
}
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradePrimaryKeyColumns...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
if rowsAff, err := o.Update(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only affect one row but affected", rowsAff)
}
}
func testTradesSliceUpdateAll(t *testing.T) {
t.Parallel()
if len(tradeAllColumns) == len(tradePrimaryKeyColumns) {
t.Skip("Skipping table with only primary key columns")
}
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradePrimaryKeyColumns...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
// Remove Primary keys and unique columns from what we plan to update
var fields []string
if strmangle.StringSliceMatch(tradeAllColumns, tradePrimaryKeyColumns) {
fields = tradeAllColumns
} else {
fields = strmangle.SetComplement(
tradeAllColumns,
tradePrimaryKeyColumns,
)
}
value := reflect.Indirect(reflect.ValueOf(o))
typ := reflect.TypeOf(o).Elem()
n := typ.NumField()
updateMap := M{}
for _, col := range fields {
for i := 0; i < n; i++ {
f := typ.Field(i)
if f.Tag.Get("boil") == col {
updateMap[col] = value.Field(i).Interface()
}
}
}
slice := TradeSlice{o}
if rowsAff, err := slice.UpdateAll(ctx, tx, updateMap); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("wanted one record updated but got", rowsAff)
}
}
func testTradesUpsert(t *testing.T) {
t.Parallel()
if len(tradeAllColumns) == len(tradePrimaryKeyColumns) {
t.Skip("Skipping table with only primary key columns")
}
seed := randomize.NewSeed()
var err error
// Attempt the INSERT side of an UPSERT
o := Trade{}
if err = randomize.Struct(seed, &o, tradeDBTypes, true); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Upsert(ctx, tx, false, nil, boil.Infer(), boil.Infer()); err != nil {
t.Errorf("Unable to upsert Trade: %s", err)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
// Attempt the UPDATE side of an UPSERT
if err = randomize.Struct(seed, &o, tradeDBTypes, false, tradePrimaryKeyColumns...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
if err = o.Upsert(ctx, tx, true, nil, boil.Infer(), boil.Infer()); err != nil {
t.Errorf("Unable to upsert Trade: %s", err)
}
count, err = Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
}

View File

@@ -17,6 +17,7 @@ func TestParent(t *testing.T) {
t.Run("Exchanges", testExchanges)
t.Run("Scripts", testScripts)
t.Run("ScriptExecutions", testScriptExecutions)
t.Run("Trades", testTrades)
t.Run("WithdrawalCryptos", testWithdrawalCryptos)
t.Run("WithdrawalFiats", testWithdrawalFiats)
t.Run("WithdrawalHistories", testWithdrawalHistories)
@@ -28,6 +29,7 @@ func TestDelete(t *testing.T) {
t.Run("Exchanges", testExchangesDelete)
t.Run("Scripts", testScriptsDelete)
t.Run("ScriptExecutions", testScriptExecutionsDelete)
t.Run("Trades", testTradesDelete)
t.Run("WithdrawalCryptos", testWithdrawalCryptosDelete)
t.Run("WithdrawalFiats", testWithdrawalFiatsDelete)
t.Run("WithdrawalHistories", testWithdrawalHistoriesDelete)
@@ -39,6 +41,7 @@ func TestQueryDeleteAll(t *testing.T) {
t.Run("Exchanges", testExchangesQueryDeleteAll)
t.Run("Scripts", testScriptsQueryDeleteAll)
t.Run("ScriptExecutions", testScriptExecutionsQueryDeleteAll)
t.Run("Trades", testTradesQueryDeleteAll)
t.Run("WithdrawalCryptos", testWithdrawalCryptosQueryDeleteAll)
t.Run("WithdrawalFiats", testWithdrawalFiatsQueryDeleteAll)
t.Run("WithdrawalHistories", testWithdrawalHistoriesQueryDeleteAll)
@@ -50,6 +53,7 @@ func TestSliceDeleteAll(t *testing.T) {
t.Run("Exchanges", testExchangesSliceDeleteAll)
t.Run("Scripts", testScriptsSliceDeleteAll)
t.Run("ScriptExecutions", testScriptExecutionsSliceDeleteAll)
t.Run("Trades", testTradesSliceDeleteAll)
t.Run("WithdrawalCryptos", testWithdrawalCryptosSliceDeleteAll)
t.Run("WithdrawalFiats", testWithdrawalFiatsSliceDeleteAll)
t.Run("WithdrawalHistories", testWithdrawalHistoriesSliceDeleteAll)
@@ -61,6 +65,7 @@ func TestExists(t *testing.T) {
t.Run("Exchanges", testExchangesExists)
t.Run("Scripts", testScriptsExists)
t.Run("ScriptExecutions", testScriptExecutionsExists)
t.Run("Trades", testTradesExists)
t.Run("WithdrawalCryptos", testWithdrawalCryptosExists)
t.Run("WithdrawalFiats", testWithdrawalFiatsExists)
t.Run("WithdrawalHistories", testWithdrawalHistoriesExists)
@@ -72,6 +77,7 @@ func TestFind(t *testing.T) {
t.Run("Exchanges", testExchangesFind)
t.Run("Scripts", testScriptsFind)
t.Run("ScriptExecutions", testScriptExecutionsFind)
t.Run("Trades", testTradesFind)
t.Run("WithdrawalCryptos", testWithdrawalCryptosFind)
t.Run("WithdrawalFiats", testWithdrawalFiatsFind)
t.Run("WithdrawalHistories", testWithdrawalHistoriesFind)
@@ -83,6 +89,7 @@ func TestBind(t *testing.T) {
t.Run("Exchanges", testExchangesBind)
t.Run("Scripts", testScriptsBind)
t.Run("ScriptExecutions", testScriptExecutionsBind)
t.Run("Trades", testTradesBind)
t.Run("WithdrawalCryptos", testWithdrawalCryptosBind)
t.Run("WithdrawalFiats", testWithdrawalFiatsBind)
t.Run("WithdrawalHistories", testWithdrawalHistoriesBind)
@@ -94,6 +101,7 @@ func TestOne(t *testing.T) {
t.Run("Exchanges", testExchangesOne)
t.Run("Scripts", testScriptsOne)
t.Run("ScriptExecutions", testScriptExecutionsOne)
t.Run("Trades", testTradesOne)
t.Run("WithdrawalCryptos", testWithdrawalCryptosOne)
t.Run("WithdrawalFiats", testWithdrawalFiatsOne)
t.Run("WithdrawalHistories", testWithdrawalHistoriesOne)
@@ -105,6 +113,7 @@ func TestAll(t *testing.T) {
t.Run("Exchanges", testExchangesAll)
t.Run("Scripts", testScriptsAll)
t.Run("ScriptExecutions", testScriptExecutionsAll)
t.Run("Trades", testTradesAll)
t.Run("WithdrawalCryptos", testWithdrawalCryptosAll)
t.Run("WithdrawalFiats", testWithdrawalFiatsAll)
t.Run("WithdrawalHistories", testWithdrawalHistoriesAll)
@@ -116,6 +125,7 @@ func TestCount(t *testing.T) {
t.Run("Exchanges", testExchangesCount)
t.Run("Scripts", testScriptsCount)
t.Run("ScriptExecutions", testScriptExecutionsCount)
t.Run("Trades", testTradesCount)
t.Run("WithdrawalCryptos", testWithdrawalCryptosCount)
t.Run("WithdrawalFiats", testWithdrawalFiatsCount)
t.Run("WithdrawalHistories", testWithdrawalHistoriesCount)
@@ -127,6 +137,7 @@ func TestHooks(t *testing.T) {
t.Run("Exchanges", testExchangesHooks)
t.Run("Scripts", testScriptsHooks)
t.Run("ScriptExecutions", testScriptExecutionsHooks)
t.Run("Trades", testTradesHooks)
t.Run("WithdrawalCryptos", testWithdrawalCryptosHooks)
t.Run("WithdrawalFiats", testWithdrawalFiatsHooks)
t.Run("WithdrawalHistories", testWithdrawalHistoriesHooks)
@@ -143,6 +154,8 @@ func TestInsert(t *testing.T) {
t.Run("Scripts", testScriptsInsertWhitelist)
t.Run("ScriptExecutions", testScriptExecutionsInsert)
t.Run("ScriptExecutions", testScriptExecutionsInsertWhitelist)
t.Run("Trades", testTradesInsert)
t.Run("Trades", testTradesInsertWhitelist)
t.Run("WithdrawalCryptos", testWithdrawalCryptosInsert)
t.Run("WithdrawalCryptos", testWithdrawalCryptosInsertWhitelist)
t.Run("WithdrawalFiats", testWithdrawalFiatsInsert)
@@ -156,6 +169,7 @@ func TestInsert(t *testing.T) {
func TestToOne(t *testing.T) {
t.Run("CandleToExchangeUsingExchangeName", testCandleToOneExchangeUsingExchangeName)
t.Run("ScriptExecutionToScriptUsingScript", testScriptExecutionToOneScriptUsingScript)
t.Run("TradeToExchangeUsingExchangeName", testTradeToOneExchangeUsingExchangeName)
t.Run("WithdrawalCryptoToWithdrawalHistoryUsingWithdrawalHistory", testWithdrawalCryptoToOneWithdrawalHistoryUsingWithdrawalHistory)
t.Run("WithdrawalFiatToWithdrawalHistoryUsingWithdrawalHistory", testWithdrawalFiatToOneWithdrawalHistoryUsingWithdrawalHistory)
t.Run("WithdrawalHistoryToExchangeUsingExchangeName", testWithdrawalHistoryToOneExchangeUsingExchangeName)
@@ -165,6 +179,7 @@ func TestToOne(t *testing.T) {
// or deadlocks can occur.
func TestOneToOne(t *testing.T) {
t.Run("ExchangeToCandleUsingExchangeNameCandle", testExchangeOneToOneCandleUsingExchangeNameCandle)
t.Run("ExchangeToTradeUsingExchangeNameTrade", testExchangeOneToOneTradeUsingExchangeNameTrade)
}
// TestToMany tests cannot be run in parallel
@@ -181,6 +196,7 @@ func TestToMany(t *testing.T) {
func TestToOneSet(t *testing.T) {
t.Run("CandleToExchangeUsingExchangeNameCandle", testCandleToOneSetOpExchangeUsingExchangeName)
t.Run("ScriptExecutionToScriptUsingScriptExecutions", testScriptExecutionToOneSetOpScriptUsingScript)
t.Run("TradeToExchangeUsingExchangeNameTrade", testTradeToOneSetOpExchangeUsingExchangeName)
t.Run("WithdrawalCryptoToWithdrawalHistoryUsingWithdrawalCryptos", testWithdrawalCryptoToOneSetOpWithdrawalHistoryUsingWithdrawalHistory)
t.Run("WithdrawalFiatToWithdrawalHistoryUsingWithdrawalFiats", testWithdrawalFiatToOneSetOpWithdrawalHistoryUsingWithdrawalHistory)
t.Run("WithdrawalHistoryToExchangeUsingExchangeNameWithdrawalHistories", testWithdrawalHistoryToOneSetOpExchangeUsingExchangeName)
@@ -194,6 +210,7 @@ func TestToOneRemove(t *testing.T) {}
// or deadlocks can occur.
func TestOneToOneSet(t *testing.T) {
t.Run("ExchangeToCandleUsingExchangeNameCandle", testExchangeOneToOneSetOpCandleUsingExchangeNameCandle)
t.Run("ExchangeToTradeUsingExchangeNameTrade", testExchangeOneToOneSetOpTradeUsingExchangeNameTrade)
}
// TestOneToOneRemove tests cannot be run in parallel
@@ -223,6 +240,7 @@ func TestReload(t *testing.T) {
t.Run("Exchanges", testExchangesReload)
t.Run("Scripts", testScriptsReload)
t.Run("ScriptExecutions", testScriptExecutionsReload)
t.Run("Trades", testTradesReload)
t.Run("WithdrawalCryptos", testWithdrawalCryptosReload)
t.Run("WithdrawalFiats", testWithdrawalFiatsReload)
t.Run("WithdrawalHistories", testWithdrawalHistoriesReload)
@@ -234,6 +252,7 @@ func TestReloadAll(t *testing.T) {
t.Run("Exchanges", testExchangesReloadAll)
t.Run("Scripts", testScriptsReloadAll)
t.Run("ScriptExecutions", testScriptExecutionsReloadAll)
t.Run("Trades", testTradesReloadAll)
t.Run("WithdrawalCryptos", testWithdrawalCryptosReloadAll)
t.Run("WithdrawalFiats", testWithdrawalFiatsReloadAll)
t.Run("WithdrawalHistories", testWithdrawalHistoriesReloadAll)
@@ -245,6 +264,7 @@ func TestSelect(t *testing.T) {
t.Run("Exchanges", testExchangesSelect)
t.Run("Scripts", testScriptsSelect)
t.Run("ScriptExecutions", testScriptExecutionsSelect)
t.Run("Trades", testTradesSelect)
t.Run("WithdrawalCryptos", testWithdrawalCryptosSelect)
t.Run("WithdrawalFiats", testWithdrawalFiatsSelect)
t.Run("WithdrawalHistories", testWithdrawalHistoriesSelect)
@@ -256,6 +276,7 @@ func TestUpdate(t *testing.T) {
t.Run("Exchanges", testExchangesUpdate)
t.Run("Scripts", testScriptsUpdate)
t.Run("ScriptExecutions", testScriptExecutionsUpdate)
t.Run("Trades", testTradesUpdate)
t.Run("WithdrawalCryptos", testWithdrawalCryptosUpdate)
t.Run("WithdrawalFiats", testWithdrawalFiatsUpdate)
t.Run("WithdrawalHistories", testWithdrawalHistoriesUpdate)
@@ -267,6 +288,7 @@ func TestSliceUpdateAll(t *testing.T) {
t.Run("Exchanges", testExchangesSliceUpdateAll)
t.Run("Scripts", testScriptsSliceUpdateAll)
t.Run("ScriptExecutions", testScriptExecutionsSliceUpdateAll)
t.Run("Trades", testTradesSliceUpdateAll)
t.Run("WithdrawalCryptos", testWithdrawalCryptosSliceUpdateAll)
t.Run("WithdrawalFiats", testWithdrawalFiatsSliceUpdateAll)
t.Run("WithdrawalHistories", testWithdrawalHistoriesSliceUpdateAll)

View File

@@ -7,8 +7,10 @@ var TableNames = struct {
AuditEvent string
Candle string
Exchange string
GooseDBVersion string
Script string
ScriptExecution string
Trade string
WithdrawalCrypto string
WithdrawalFiat string
WithdrawalHistory string
@@ -16,8 +18,10 @@ var TableNames = struct {
AuditEvent: "audit_event",
Candle: "candle",
Exchange: "exchange",
GooseDBVersion: "goose_db_version",
Script: "script",
ScriptExecution: "script_execution",
Trade: "trade",
WithdrawalCrypto: "withdrawal_crypto",
WithdrawalFiat: "withdrawal_fiat",
WithdrawalHistory: "withdrawal_history",

View File

@@ -50,15 +50,18 @@ var ExchangeWhere = struct {
// ExchangeRels is where relationship names are stored.
var ExchangeRels = struct {
ExchangeNameCandle string
ExchangeNameTrade string
ExchangeNameWithdrawalHistories string
}{
ExchangeNameCandle: "ExchangeNameCandle",
ExchangeNameTrade: "ExchangeNameTrade",
ExchangeNameWithdrawalHistories: "ExchangeNameWithdrawalHistories",
}
// exchangeR is where relationships are stored.
type exchangeR struct {
ExchangeNameCandle *Candle
ExchangeNameTrade *Trade
ExchangeNameWithdrawalHistories WithdrawalHistorySlice
}
@@ -366,6 +369,20 @@ func (o *Exchange) ExchangeNameCandle(mods ...qm.QueryMod) candleQuery {
return query
}
// ExchangeNameTrade pointed to by the foreign key.
func (o *Exchange) ExchangeNameTrade(mods ...qm.QueryMod) tradeQuery {
queryMods := []qm.QueryMod{
qm.Where("\"exchange_name_id\" = ?", o.ID),
}
queryMods = append(queryMods, mods...)
query := Trades(queryMods...)
queries.SetFrom(query.Query, "\"trade\"")
return query
}
// ExchangeNameWithdrawalHistories retrieves all the withdrawal_history's WithdrawalHistories with an executor via exchange_name_id column.
func (o *Exchange) ExchangeNameWithdrawalHistories(mods ...qm.QueryMod) withdrawalHistoryQuery {
var queryMods []qm.QueryMod
@@ -485,6 +502,104 @@ func (exchangeL) LoadExchangeNameCandle(ctx context.Context, e boil.ContextExecu
return nil
}
// LoadExchangeNameTrade allows an eager lookup of values, cached into the
// loaded structs of the objects. This is for a 1-1 relationship.
func (exchangeL) LoadExchangeNameTrade(ctx context.Context, e boil.ContextExecutor, singular bool, maybeExchange interface{}, mods queries.Applicator) error {
var slice []*Exchange
var object *Exchange
if singular {
object = maybeExchange.(*Exchange)
} else {
slice = *maybeExchange.(*[]*Exchange)
}
args := make([]interface{}, 0, 1)
if singular {
if object.R == nil {
object.R = &exchangeR{}
}
args = append(args, object.ID)
} else {
Outer:
for _, obj := range slice {
if obj.R == nil {
obj.R = &exchangeR{}
}
for _, a := range args {
if a == obj.ID {
continue Outer
}
}
args = append(args, obj.ID)
}
}
if len(args) == 0 {
return nil
}
query := NewQuery(qm.From(`trade`), qm.WhereIn(`trade.exchange_name_id in ?`, args...))
if mods != nil {
mods.Apply(query)
}
results, err := query.QueryContext(ctx, e)
if err != nil {
return errors.Wrap(err, "failed to eager load Trade")
}
var resultSlice []*Trade
if err = queries.Bind(results, &resultSlice); err != nil {
return errors.Wrap(err, "failed to bind eager loaded slice Trade")
}
if err = results.Close(); err != nil {
return errors.Wrap(err, "failed to close results of eager load for trade")
}
if err = results.Err(); err != nil {
return errors.Wrap(err, "error occurred during iteration of eager loaded relations for trade")
}
if len(exchangeAfterSelectHooks) != 0 {
for _, obj := range resultSlice {
if err := obj.doAfterSelectHooks(ctx, e); err != nil {
return err
}
}
}
if len(resultSlice) == 0 {
return nil
}
if singular {
foreign := resultSlice[0]
object.R.ExchangeNameTrade = foreign
if foreign.R == nil {
foreign.R = &tradeR{}
}
foreign.R.ExchangeName = object
}
for _, local := range slice {
for _, foreign := range resultSlice {
if local.ID == foreign.ExchangeNameID {
local.R.ExchangeNameTrade = foreign
if foreign.R == nil {
foreign.R = &tradeR{}
}
foreign.R.ExchangeName = local
break
}
}
}
return nil
}
// LoadExchangeNameWithdrawalHistories allows an eager lookup of values, cached into the
// loaded structs of the objects. This is for a 1-M or N-M relationship.
func (exchangeL) LoadExchangeNameWithdrawalHistories(ctx context.Context, e boil.ContextExecutor, singular bool, maybeExchange interface{}, mods queries.Applicator) error {
@@ -631,6 +746,57 @@ func (o *Exchange) SetExchangeNameCandle(ctx context.Context, exec boil.ContextE
return nil
}
// SetExchangeNameTrade of the exchange to the related item.
// Sets o.R.ExchangeNameTrade to related.
// Adds o to related.R.ExchangeName.
func (o *Exchange) SetExchangeNameTrade(ctx context.Context, exec boil.ContextExecutor, insert bool, related *Trade) error {
var err error
if insert {
related.ExchangeNameID = o.ID
if err = related.Insert(ctx, exec, boil.Infer()); err != nil {
return errors.Wrap(err, "failed to insert into foreign table")
}
} else {
updateQuery := fmt.Sprintf(
"UPDATE \"trade\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, []string{"exchange_name_id"}),
strmangle.WhereClause("\"", "\"", 0, tradePrimaryKeyColumns),
)
values := []interface{}{o.ID, related.ID}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, updateQuery)
fmt.Fprintln(boil.DebugWriter, values)
}
if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil {
return errors.Wrap(err, "failed to update foreign table")
}
related.ExchangeNameID = o.ID
}
if o.R == nil {
o.R = &exchangeR{
ExchangeNameTrade: related,
}
} else {
o.R.ExchangeNameTrade = related
}
if related.R == nil {
related.R = &tradeR{
ExchangeName: o,
}
} else {
related.R.ExchangeName = o
}
return nil
}
// AddExchangeNameWithdrawalHistories adds the given related objects to the existing relationships
// of the exchange, optionally inserting them as new records.
// Appends related to o.R.ExchangeNameWithdrawalHistories.

View File

@@ -545,6 +545,57 @@ func testExchangeOneToOneCandleUsingExchangeNameCandle(t *testing.T) {
}
}
func testExchangeOneToOneTradeUsingExchangeNameTrade(t *testing.T) {
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var foreign Trade
var local Exchange
seed := randomize.NewSeed()
if err := randomize.Struct(seed, &foreign, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
if err := randomize.Struct(seed, &local, exchangeDBTypes, true, exchangeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Exchange struct: %s", err)
}
if err := local.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
foreign.ExchangeNameID = local.ID
if err := foreign.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
check, err := local.ExchangeNameTrade().One(ctx, tx)
if err != nil {
t.Fatal(err)
}
if check.ExchangeNameID != foreign.ExchangeNameID {
t.Errorf("want: %v, got %v", foreign.ExchangeNameID, check.ExchangeNameID)
}
slice := ExchangeSlice{&local}
if err = local.L.LoadExchangeNameTrade(ctx, tx, false, (*[]*Exchange)(&slice), nil); err != nil {
t.Fatal(err)
}
if local.R.ExchangeNameTrade == nil {
t.Error("struct should have been eager loaded")
}
local.R.ExchangeNameTrade = nil
if err = local.L.LoadExchangeNameTrade(ctx, tx, true, &local, nil); err != nil {
t.Fatal(err)
}
if local.R.ExchangeNameTrade == nil {
t.Error("struct should have been eager loaded")
}
}
func testExchangeOneToOneSetOpCandleUsingExchangeNameCandle(t *testing.T) {
var err error
@@ -606,6 +657,67 @@ func testExchangeOneToOneSetOpCandleUsingExchangeNameCandle(t *testing.T) {
}
}
}
func testExchangeOneToOneSetOpTradeUsingExchangeNameTrade(t *testing.T) {
var err error
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var a Exchange
var b, c Trade
seed := randomize.NewSeed()
if err = randomize.Struct(seed, &a, exchangeDBTypes, false, strmangle.SetComplement(exchangePrimaryKeyColumns, exchangeColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &b, tradeDBTypes, false, strmangle.SetComplement(tradePrimaryKeyColumns, tradeColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &c, tradeDBTypes, false, strmangle.SetComplement(tradePrimaryKeyColumns, tradeColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
for i, x := range []*Trade{&b, &c} {
err = a.SetExchangeNameTrade(ctx, tx, i != 0, x)
if err != nil {
t.Fatal(err)
}
if a.R.ExchangeNameTrade != x {
t.Error("relationship struct not set to correct value")
}
if x.R.ExchangeName != &a {
t.Error("failed to append to foreign relationship struct")
}
if a.ID != x.ExchangeNameID {
t.Error("foreign key was wrong value", a.ID)
}
zero := reflect.Zero(reflect.TypeOf(x.ExchangeNameID))
reflect.Indirect(reflect.ValueOf(&x.ExchangeNameID)).Set(zero)
if err = x.Reload(ctx, tx); err != nil {
t.Fatal("failed to reload", err)
}
if a.ID != x.ExchangeNameID {
t.Error("foreign key was wrong value", a.ID, x.ExchangeNameID)
}
if _, err = x.Delete(ctx, tx); err != nil {
t.Fatal("failed to delete x", err)
}
}
}
func testExchangeToManyExchangeNameWithdrawalHistories(t *testing.T) {
var err error

View File

@@ -0,0 +1,994 @@
// Code generated by SQLBoiler 3.5.0-gct (https://github.com/thrasher-corp/sqlboiler). DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package sqlite3
import (
"context"
"database/sql"
"fmt"
"reflect"
"strings"
"sync"
"time"
"github.com/pkg/errors"
"github.com/thrasher-corp/sqlboiler/boil"
"github.com/thrasher-corp/sqlboiler/queries"
"github.com/thrasher-corp/sqlboiler/queries/qm"
"github.com/thrasher-corp/sqlboiler/queries/qmhelper"
"github.com/thrasher-corp/sqlboiler/strmangle"
"github.com/volatiletech/null"
)
// Trade is an object representing the database table.
type Trade struct {
ID string `boil:"id" json:"id" toml:"id" yaml:"id"`
ExchangeNameID string `boil:"exchange_name_id" json:"exchange_name_id" toml:"exchange_name_id" yaml:"exchange_name_id"`
Tid null.String `boil:"tid" json:"tid,omitempty" toml:"tid" yaml:"tid,omitempty"`
Base string `boil:"base" json:"base" toml:"base" yaml:"base"`
Quote string `boil:"quote" json:"quote" toml:"quote" yaml:"quote"`
Asset string `boil:"asset" json:"asset" toml:"asset" yaml:"asset"`
Price float64 `boil:"price" json:"price" toml:"price" yaml:"price"`
Amount float64 `boil:"amount" json:"amount" toml:"amount" yaml:"amount"`
Side null.String `boil:"side" json:"side,omitempty" toml:"side" yaml:"side,omitempty"`
Timestamp string `boil:"timestamp" json:"timestamp" toml:"timestamp" yaml:"timestamp"`
R *tradeR `boil:"-" json:"-" toml:"-" yaml:"-"`
L tradeL `boil:"-" json:"-" toml:"-" yaml:"-"`
}
var TradeColumns = struct {
ID string
ExchangeNameID string
Tid string
Base string
Quote string
Asset string
Price string
Amount string
Side string
Timestamp string
}{
ID: "id",
ExchangeNameID: "exchange_name_id",
Tid: "tid",
Base: "base",
Quote: "quote",
Asset: "asset",
Price: "price",
Amount: "amount",
Side: "side",
Timestamp: "timestamp",
}
// Generated where
type whereHelpernull_String struct{ field string }
func (w whereHelpernull_String) EQ(x null.String) qm.QueryMod {
return qmhelper.WhereNullEQ(w.field, false, x)
}
func (w whereHelpernull_String) NEQ(x null.String) qm.QueryMod {
return qmhelper.WhereNullEQ(w.field, true, x)
}
func (w whereHelpernull_String) IsNull() qm.QueryMod { return qmhelper.WhereIsNull(w.field) }
func (w whereHelpernull_String) IsNotNull() qm.QueryMod { return qmhelper.WhereIsNotNull(w.field) }
func (w whereHelpernull_String) LT(x null.String) qm.QueryMod {
return qmhelper.Where(w.field, qmhelper.LT, x)
}
func (w whereHelpernull_String) LTE(x null.String) qm.QueryMod {
return qmhelper.Where(w.field, qmhelper.LTE, x)
}
func (w whereHelpernull_String) GT(x null.String) qm.QueryMod {
return qmhelper.Where(w.field, qmhelper.GT, x)
}
func (w whereHelpernull_String) GTE(x null.String) qm.QueryMod {
return qmhelper.Where(w.field, qmhelper.GTE, x)
}
var TradeWhere = struct {
ID whereHelperstring
ExchangeNameID whereHelperstring
Tid whereHelpernull_String
Base whereHelperstring
Quote whereHelperstring
Asset whereHelperstring
Price whereHelperfloat64
Amount whereHelperfloat64
Side whereHelpernull_String
Timestamp whereHelperstring
}{
ID: whereHelperstring{field: "\"trade\".\"id\""},
ExchangeNameID: whereHelperstring{field: "\"trade\".\"exchange_name_id\""},
Tid: whereHelpernull_String{field: "\"trade\".\"tid\""},
Base: whereHelperstring{field: "\"trade\".\"base\""},
Quote: whereHelperstring{field: "\"trade\".\"quote\""},
Asset: whereHelperstring{field: "\"trade\".\"asset\""},
Price: whereHelperfloat64{field: "\"trade\".\"price\""},
Amount: whereHelperfloat64{field: "\"trade\".\"amount\""},
Side: whereHelpernull_String{field: "\"trade\".\"side\""},
Timestamp: whereHelperstring{field: "\"trade\".\"timestamp\""},
}
// TradeRels is where relationship names are stored.
var TradeRels = struct {
ExchangeName string
}{
ExchangeName: "ExchangeName",
}
// tradeR is where relationships are stored.
type tradeR struct {
ExchangeName *Exchange
}
// NewStruct creates a new relationship struct
func (*tradeR) NewStruct() *tradeR {
return &tradeR{}
}
// tradeL is where Load methods for each relationship are stored.
type tradeL struct{}
var (
tradeAllColumns = []string{"id", "exchange_name_id", "tid", "base", "quote", "asset", "price", "amount", "side", "timestamp"}
tradeColumnsWithoutDefault = []string{"id", "exchange_name_id", "tid", "base", "quote", "asset", "price", "amount", "side", "timestamp"}
tradeColumnsWithDefault = []string{}
tradePrimaryKeyColumns = []string{"id"}
)
type (
// TradeSlice is an alias for a slice of pointers to Trade.
// This should generally be used opposed to []Trade.
TradeSlice []*Trade
// TradeHook is the signature for custom Trade hook methods
TradeHook func(context.Context, boil.ContextExecutor, *Trade) error
tradeQuery struct {
*queries.Query
}
)
// Cache for insert, update and upsert
var (
tradeType = reflect.TypeOf(&Trade{})
tradeMapping = queries.MakeStructMapping(tradeType)
tradePrimaryKeyMapping, _ = queries.BindMapping(tradeType, tradeMapping, tradePrimaryKeyColumns)
tradeInsertCacheMut sync.RWMutex
tradeInsertCache = make(map[string]insertCache)
tradeUpdateCacheMut sync.RWMutex
tradeUpdateCache = make(map[string]updateCache)
tradeUpsertCacheMut sync.RWMutex
tradeUpsertCache = make(map[string]insertCache)
)
var (
// Force time package dependency for automated UpdatedAt/CreatedAt.
_ = time.Second
// Force qmhelper dependency for where clause generation (which doesn't
// always happen)
_ = qmhelper.Where
)
var tradeBeforeInsertHooks []TradeHook
var tradeBeforeUpdateHooks []TradeHook
var tradeBeforeDeleteHooks []TradeHook
var tradeBeforeUpsertHooks []TradeHook
var tradeAfterInsertHooks []TradeHook
var tradeAfterSelectHooks []TradeHook
var tradeAfterUpdateHooks []TradeHook
var tradeAfterDeleteHooks []TradeHook
var tradeAfterUpsertHooks []TradeHook
// doBeforeInsertHooks executes all "before insert" hooks.
func (o *Trade) doBeforeInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range tradeBeforeInsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doBeforeUpdateHooks executes all "before Update" hooks.
func (o *Trade) doBeforeUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range tradeBeforeUpdateHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doBeforeDeleteHooks executes all "before Delete" hooks.
func (o *Trade) doBeforeDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range tradeBeforeDeleteHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doBeforeUpsertHooks executes all "before Upsert" hooks.
func (o *Trade) doBeforeUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range tradeBeforeUpsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterInsertHooks executes all "after Insert" hooks.
func (o *Trade) doAfterInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range tradeAfterInsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterSelectHooks executes all "after Select" hooks.
func (o *Trade) doAfterSelectHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range tradeAfterSelectHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterUpdateHooks executes all "after Update" hooks.
func (o *Trade) doAfterUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range tradeAfterUpdateHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterDeleteHooks executes all "after Delete" hooks.
func (o *Trade) doAfterDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range tradeAfterDeleteHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterUpsertHooks executes all "after Upsert" hooks.
func (o *Trade) doAfterUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range tradeAfterUpsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// AddTradeHook registers your hook function for all future operations.
func AddTradeHook(hookPoint boil.HookPoint, tradeHook TradeHook) {
switch hookPoint {
case boil.BeforeInsertHook:
tradeBeforeInsertHooks = append(tradeBeforeInsertHooks, tradeHook)
case boil.BeforeUpdateHook:
tradeBeforeUpdateHooks = append(tradeBeforeUpdateHooks, tradeHook)
case boil.BeforeDeleteHook:
tradeBeforeDeleteHooks = append(tradeBeforeDeleteHooks, tradeHook)
case boil.BeforeUpsertHook:
tradeBeforeUpsertHooks = append(tradeBeforeUpsertHooks, tradeHook)
case boil.AfterInsertHook:
tradeAfterInsertHooks = append(tradeAfterInsertHooks, tradeHook)
case boil.AfterSelectHook:
tradeAfterSelectHooks = append(tradeAfterSelectHooks, tradeHook)
case boil.AfterUpdateHook:
tradeAfterUpdateHooks = append(tradeAfterUpdateHooks, tradeHook)
case boil.AfterDeleteHook:
tradeAfterDeleteHooks = append(tradeAfterDeleteHooks, tradeHook)
case boil.AfterUpsertHook:
tradeAfterUpsertHooks = append(tradeAfterUpsertHooks, tradeHook)
}
}
// One returns a single trade record from the query.
func (q tradeQuery) One(ctx context.Context, exec boil.ContextExecutor) (*Trade, error) {
o := &Trade{}
queries.SetLimit(q.Query, 1)
err := q.Bind(ctx, exec, o)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, sql.ErrNoRows
}
return nil, errors.Wrap(err, "sqlite3: failed to execute a one query for trade")
}
if err := o.doAfterSelectHooks(ctx, exec); err != nil {
return o, err
}
return o, nil
}
// All returns all Trade records from the query.
func (q tradeQuery) All(ctx context.Context, exec boil.ContextExecutor) (TradeSlice, error) {
var o []*Trade
err := q.Bind(ctx, exec, &o)
if err != nil {
return nil, errors.Wrap(err, "sqlite3: failed to assign all query results to Trade slice")
}
if len(tradeAfterSelectHooks) != 0 {
for _, obj := range o {
if err := obj.doAfterSelectHooks(ctx, exec); err != nil {
return o, err
}
}
}
return o, nil
}
// Count returns the count of all Trade records in the query.
func (q tradeQuery) Count(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
var count int64
queries.SetSelect(q.Query, nil)
queries.SetCount(q.Query)
err := q.Query.QueryRowContext(ctx, exec).Scan(&count)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to count trade rows")
}
return count, nil
}
// Exists checks if the row exists in the table.
func (q tradeQuery) Exists(ctx context.Context, exec boil.ContextExecutor) (bool, error) {
var count int64
queries.SetSelect(q.Query, nil)
queries.SetCount(q.Query)
queries.SetLimit(q.Query, 1)
err := q.Query.QueryRowContext(ctx, exec).Scan(&count)
if err != nil {
return false, errors.Wrap(err, "sqlite3: failed to check if trade exists")
}
return count > 0, nil
}
// ExchangeName pointed to by the foreign key.
func (o *Trade) ExchangeName(mods ...qm.QueryMod) exchangeQuery {
queryMods := []qm.QueryMod{
qm.Where("\"id\" = ?", o.ExchangeNameID),
}
queryMods = append(queryMods, mods...)
query := Exchanges(queryMods...)
queries.SetFrom(query.Query, "\"exchange\"")
return query
}
// LoadExchangeName allows an eager lookup of values, cached into the
// loaded structs of the objects. This is for an N-1 relationship.
func (tradeL) LoadExchangeName(ctx context.Context, e boil.ContextExecutor, singular bool, maybeTrade interface{}, mods queries.Applicator) error {
var slice []*Trade
var object *Trade
if singular {
object = maybeTrade.(*Trade)
} else {
slice = *maybeTrade.(*[]*Trade)
}
args := make([]interface{}, 0, 1)
if singular {
if object.R == nil {
object.R = &tradeR{}
}
args = append(args, object.ExchangeNameID)
} else {
Outer:
for _, obj := range slice {
if obj.R == nil {
obj.R = &tradeR{}
}
for _, a := range args {
if a == obj.ExchangeNameID {
continue Outer
}
}
args = append(args, obj.ExchangeNameID)
}
}
if len(args) == 0 {
return nil
}
query := NewQuery(qm.From(`exchange`), qm.WhereIn(`exchange.id in ?`, args...))
if mods != nil {
mods.Apply(query)
}
results, err := query.QueryContext(ctx, e)
if err != nil {
return errors.Wrap(err, "failed to eager load Exchange")
}
var resultSlice []*Exchange
if err = queries.Bind(results, &resultSlice); err != nil {
return errors.Wrap(err, "failed to bind eager loaded slice Exchange")
}
if err = results.Close(); err != nil {
return errors.Wrap(err, "failed to close results of eager load for exchange")
}
if err = results.Err(); err != nil {
return errors.Wrap(err, "error occurred during iteration of eager loaded relations for exchange")
}
if len(tradeAfterSelectHooks) != 0 {
for _, obj := range resultSlice {
if err := obj.doAfterSelectHooks(ctx, e); err != nil {
return err
}
}
}
if len(resultSlice) == 0 {
return nil
}
if singular {
foreign := resultSlice[0]
object.R.ExchangeName = foreign
if foreign.R == nil {
foreign.R = &exchangeR{}
}
foreign.R.ExchangeNameTrade = object
return nil
}
for _, local := range slice {
for _, foreign := range resultSlice {
if local.ExchangeNameID == foreign.ID {
local.R.ExchangeName = foreign
if foreign.R == nil {
foreign.R = &exchangeR{}
}
foreign.R.ExchangeNameTrade = local
break
}
}
}
return nil
}
// SetExchangeName of the trade to the related item.
// Sets o.R.ExchangeName to related.
// Adds o to related.R.ExchangeNameTrade.
func (o *Trade) SetExchangeName(ctx context.Context, exec boil.ContextExecutor, insert bool, related *Exchange) error {
var err error
if insert {
if err = related.Insert(ctx, exec, boil.Infer()); err != nil {
return errors.Wrap(err, "failed to insert into foreign table")
}
}
updateQuery := fmt.Sprintf(
"UPDATE \"trade\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, []string{"exchange_name_id"}),
strmangle.WhereClause("\"", "\"", 0, tradePrimaryKeyColumns),
)
values := []interface{}{related.ID, o.ID}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, updateQuery)
fmt.Fprintln(boil.DebugWriter, values)
}
if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil {
return errors.Wrap(err, "failed to update local table")
}
o.ExchangeNameID = related.ID
if o.R == nil {
o.R = &tradeR{
ExchangeName: related,
}
} else {
o.R.ExchangeName = related
}
if related.R == nil {
related.R = &exchangeR{
ExchangeNameTrade: o,
}
} else {
related.R.ExchangeNameTrade = o
}
return nil
}
// Trades retrieves all the records using an executor.
func Trades(mods ...qm.QueryMod) tradeQuery {
mods = append(mods, qm.From("\"trade\""))
return tradeQuery{NewQuery(mods...)}
}
// FindTrade retrieves a single record by ID with an executor.
// If selectCols is empty Find will return all columns.
func FindTrade(ctx context.Context, exec boil.ContextExecutor, iD string, selectCols ...string) (*Trade, error) {
tradeObj := &Trade{}
sel := "*"
if len(selectCols) > 0 {
sel = strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, selectCols), ",")
}
query := fmt.Sprintf(
"select %s from \"trade\" where \"id\"=?", sel,
)
q := queries.Raw(query, iD)
err := q.Bind(ctx, exec, tradeObj)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, sql.ErrNoRows
}
return nil, errors.Wrap(err, "sqlite3: unable to select from trade")
}
return tradeObj, nil
}
// Insert a single record using an executor.
// See boil.Columns.InsertColumnSet documentation to understand column list inference for inserts.
func (o *Trade) Insert(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) error {
if o == nil {
return errors.New("sqlite3: no trade provided for insertion")
}
var err error
if err := o.doBeforeInsertHooks(ctx, exec); err != nil {
return err
}
nzDefaults := queries.NonZeroDefaultSet(tradeColumnsWithDefault, o)
key := makeCacheKey(columns, nzDefaults)
tradeInsertCacheMut.RLock()
cache, cached := tradeInsertCache[key]
tradeInsertCacheMut.RUnlock()
if !cached {
wl, returnColumns := columns.InsertColumnSet(
tradeAllColumns,
tradeColumnsWithDefault,
tradeColumnsWithoutDefault,
nzDefaults,
)
cache.valueMapping, err = queries.BindMapping(tradeType, tradeMapping, wl)
if err != nil {
return err
}
cache.retMapping, err = queries.BindMapping(tradeType, tradeMapping, returnColumns)
if err != nil {
return err
}
if len(wl) != 0 {
cache.query = fmt.Sprintf("INSERT INTO \"trade\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1))
} else {
cache.query = "INSERT INTO \"trade\" () VALUES ()%s%s"
}
var queryOutput, queryReturning string
if len(cache.retMapping) != 0 {
cache.retQuery = fmt.Sprintf("SELECT \"%s\" FROM \"trade\" WHERE %s", strings.Join(returnColumns, "\",\""), strmangle.WhereClause("\"", "\"", 0, tradePrimaryKeyColumns))
}
cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning)
}
value := reflect.Indirect(reflect.ValueOf(o))
vals := queries.ValuesFromMapping(value, cache.valueMapping)
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, cache.query)
fmt.Fprintln(boil.DebugWriter, vals)
}
_, err = exec.ExecContext(ctx, cache.query, vals...)
if err != nil {
return errors.Wrap(err, "sqlite3: unable to insert into trade")
}
var identifierCols []interface{}
if len(cache.retMapping) == 0 {
goto CacheNoHooks
}
identifierCols = []interface{}{
o.ID,
}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, cache.retQuery)
fmt.Fprintln(boil.DebugWriter, identifierCols...)
}
err = exec.QueryRowContext(ctx, cache.retQuery, identifierCols...).Scan(queries.PtrsFromMapping(value, cache.retMapping)...)
if err != nil {
return errors.Wrap(err, "sqlite3: unable to populate default values for trade")
}
CacheNoHooks:
if !cached {
tradeInsertCacheMut.Lock()
tradeInsertCache[key] = cache
tradeInsertCacheMut.Unlock()
}
return o.doAfterInsertHooks(ctx, exec)
}
// Update uses an executor to update the Trade.
// See boil.Columns.UpdateColumnSet documentation to understand column list inference for updates.
// Update does not automatically update the record in case of default values. Use .Reload() to refresh the records.
func (o *Trade) Update(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) (int64, error) {
var err error
if err = o.doBeforeUpdateHooks(ctx, exec); err != nil {
return 0, err
}
key := makeCacheKey(columns, nil)
tradeUpdateCacheMut.RLock()
cache, cached := tradeUpdateCache[key]
tradeUpdateCacheMut.RUnlock()
if !cached {
wl := columns.UpdateColumnSet(
tradeAllColumns,
tradePrimaryKeyColumns,
)
if len(wl) == 0 {
return 0, errors.New("sqlite3: unable to update trade, could not build whitelist")
}
cache.query = fmt.Sprintf("UPDATE \"trade\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, wl),
strmangle.WhereClause("\"", "\"", 0, tradePrimaryKeyColumns),
)
cache.valueMapping, err = queries.BindMapping(tradeType, tradeMapping, append(wl, tradePrimaryKeyColumns...))
if err != nil {
return 0, err
}
}
values := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), cache.valueMapping)
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, cache.query)
fmt.Fprintln(boil.DebugWriter, values)
}
var result sql.Result
result, err = exec.ExecContext(ctx, cache.query, values...)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to update trade row")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to get rows affected by update for trade")
}
if !cached {
tradeUpdateCacheMut.Lock()
tradeUpdateCache[key] = cache
tradeUpdateCacheMut.Unlock()
}
return rowsAff, o.doAfterUpdateHooks(ctx, exec)
}
// UpdateAll updates all rows with the specified column values.
func (q tradeQuery) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) {
queries.SetUpdate(q.Query, cols)
result, err := q.Query.ExecContext(ctx, exec)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to update all for trade")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to retrieve rows affected for trade")
}
return rowsAff, nil
}
// UpdateAll updates all rows with the specified column values, using an executor.
func (o TradeSlice) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) {
ln := int64(len(o))
if ln == 0 {
return 0, nil
}
if len(cols) == 0 {
return 0, errors.New("sqlite3: update all requires at least one column argument")
}
colNames := make([]string, len(cols))
args := make([]interface{}, len(cols))
i := 0
for name, value := range cols {
colNames[i] = name
args[i] = value
i++
}
// Append all of the primary key values for each column
for _, obj := range o {
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), tradePrimaryKeyMapping)
args = append(args, pkeyArgs...)
}
sql := fmt.Sprintf("UPDATE \"trade\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, colNames),
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, tradePrimaryKeyColumns, len(o)))
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, sql)
fmt.Fprintln(boil.DebugWriter, args...)
}
result, err := exec.ExecContext(ctx, sql, args...)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to update all in trade slice")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to retrieve rows affected all in update all trade")
}
return rowsAff, nil
}
// Delete deletes a single Trade record with an executor.
// Delete will match against the primary key column to find the record to delete.
func (o *Trade) Delete(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
if o == nil {
return 0, errors.New("sqlite3: no Trade provided for delete")
}
if err := o.doBeforeDeleteHooks(ctx, exec); err != nil {
return 0, err
}
args := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), tradePrimaryKeyMapping)
sql := "DELETE FROM \"trade\" WHERE \"id\"=?"
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, sql)
fmt.Fprintln(boil.DebugWriter, args...)
}
result, err := exec.ExecContext(ctx, sql, args...)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to delete from trade")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to get rows affected by delete for trade")
}
if err := o.doAfterDeleteHooks(ctx, exec); err != nil {
return 0, err
}
return rowsAff, nil
}
// DeleteAll deletes all matching rows.
func (q tradeQuery) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
if q.Query == nil {
return 0, errors.New("sqlite3: no tradeQuery provided for delete all")
}
queries.SetDelete(q.Query)
result, err := q.Query.ExecContext(ctx, exec)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to delete all from trade")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to get rows affected by deleteall for trade")
}
return rowsAff, nil
}
// DeleteAll deletes all rows in the slice, using an executor.
func (o TradeSlice) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
if len(o) == 0 {
return 0, nil
}
if len(tradeBeforeDeleteHooks) != 0 {
for _, obj := range o {
if err := obj.doBeforeDeleteHooks(ctx, exec); err != nil {
return 0, err
}
}
}
var args []interface{}
for _, obj := range o {
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), tradePrimaryKeyMapping)
args = append(args, pkeyArgs...)
}
sql := "DELETE FROM \"trade\" WHERE " +
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, tradePrimaryKeyColumns, len(o))
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, sql)
fmt.Fprintln(boil.DebugWriter, args)
}
result, err := exec.ExecContext(ctx, sql, args...)
if err != nil {
return 0, errors.Wrap(err, "sqlite3: unable to delete all from trade slice")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "sqlite3: failed to get rows affected by deleteall for trade")
}
if len(tradeAfterDeleteHooks) != 0 {
for _, obj := range o {
if err := obj.doAfterDeleteHooks(ctx, exec); err != nil {
return 0, err
}
}
}
return rowsAff, nil
}
// Reload refetches the object from the database
// using the primary keys with an executor.
func (o *Trade) Reload(ctx context.Context, exec boil.ContextExecutor) error {
ret, err := FindTrade(ctx, exec, o.ID)
if err != nil {
return err
}
*o = *ret
return nil
}
// ReloadAll refetches every row with matching primary key column values
// and overwrites the original object slice with the newly updated slice.
func (o *TradeSlice) ReloadAll(ctx context.Context, exec boil.ContextExecutor) error {
if o == nil || len(*o) == 0 {
return nil
}
slice := TradeSlice{}
var args []interface{}
for _, obj := range *o {
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), tradePrimaryKeyMapping)
args = append(args, pkeyArgs...)
}
sql := "SELECT \"trade\".* FROM \"trade\" WHERE " +
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, tradePrimaryKeyColumns, len(*o))
q := queries.Raw(sql, args...)
err := q.Bind(ctx, exec, &slice)
if err != nil {
return errors.Wrap(err, "sqlite3: unable to reload all in TradeSlice")
}
*o = slice
return nil
}
// TradeExists checks if the Trade row exists.
func TradeExists(ctx context.Context, exec boil.ContextExecutor, iD string) (bool, error) {
var exists bool
sql := "select exists(select 1 from \"trade\" where \"id\"=? limit 1)"
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, sql)
fmt.Fprintln(boil.DebugWriter, iD)
}
row := exec.QueryRowContext(ctx, sql, iD)
err := row.Scan(&exists)
if err != nil {
return false, errors.Wrap(err, "sqlite3: unable to check if trade exists")
}
return exists, nil
}

View File

@@ -0,0 +1,793 @@
// Code generated by SQLBoiler 3.5.0-gct (https://github.com/thrasher-corp/sqlboiler). DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package sqlite3
import (
"bytes"
"context"
"reflect"
"testing"
"github.com/thrasher-corp/sqlboiler/boil"
"github.com/thrasher-corp/sqlboiler/queries"
"github.com/thrasher-corp/sqlboiler/randomize"
"github.com/thrasher-corp/sqlboiler/strmangle"
)
var (
// Relationships sometimes use the reflection helper queries.Equal/queries.Assign
// so force a package dependency in case they don't.
_ = queries.Equal
)
func testTrades(t *testing.T) {
t.Parallel()
query := Trades()
if query.Query == nil {
t.Error("expected a query, got nothing")
}
}
func testTradesDelete(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if rowsAff, err := o.Delete(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testTradesQueryDeleteAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if rowsAff, err := Trades().DeleteAll(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testTradesSliceDeleteAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice := TradeSlice{o}
if rowsAff, err := slice.DeleteAll(ctx, tx); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only have deleted one row, but affected:", rowsAff)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 0 {
t.Error("want zero records, got:", count)
}
}
func testTradesExists(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
e, err := TradeExists(ctx, tx, o.ID)
if err != nil {
t.Errorf("Unable to check if Trade exists: %s", err)
}
if !e {
t.Errorf("Expected TradeExists to return true, but got false.")
}
}
func testTradesFind(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
tradeFound, err := FindTrade(ctx, tx, o.ID)
if err != nil {
t.Error(err)
}
if tradeFound == nil {
t.Error("want a record, got nil")
}
}
func testTradesBind(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = Trades().Bind(ctx, tx, o); err != nil {
t.Error(err)
}
}
func testTradesOne(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if x, err := Trades().One(ctx, tx); err != nil {
t.Error(err)
} else if x == nil {
t.Error("expected to get a non nil record")
}
}
func testTradesAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
tradeOne := &Trade{}
tradeTwo := &Trade{}
if err = randomize.Struct(seed, tradeOne, tradeDBTypes, false, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
if err = randomize.Struct(seed, tradeTwo, tradeDBTypes, false, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = tradeOne.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = tradeTwo.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice, err := Trades().All(ctx, tx)
if err != nil {
t.Error(err)
}
if len(slice) != 2 {
t.Error("want 2 records, got:", len(slice))
}
}
func testTradesCount(t *testing.T) {
t.Parallel()
var err error
seed := randomize.NewSeed()
tradeOne := &Trade{}
tradeTwo := &Trade{}
if err = randomize.Struct(seed, tradeOne, tradeDBTypes, false, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
if err = randomize.Struct(seed, tradeTwo, tradeDBTypes, false, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = tradeOne.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = tradeTwo.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 2 {
t.Error("want 2 records, got:", count)
}
}
func tradeBeforeInsertHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeAfterInsertHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeAfterSelectHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeBeforeUpdateHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeAfterUpdateHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeBeforeDeleteHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeAfterDeleteHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeBeforeUpsertHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func tradeAfterUpsertHook(ctx context.Context, e boil.ContextExecutor, o *Trade) error {
*o = Trade{}
return nil
}
func testTradesHooks(t *testing.T) {
t.Parallel()
var err error
ctx := context.Background()
empty := &Trade{}
o := &Trade{}
seed := randomize.NewSeed()
if err = randomize.Struct(seed, o, tradeDBTypes, false); err != nil {
t.Errorf("Unable to randomize Trade object: %s", err)
}
AddTradeHook(boil.BeforeInsertHook, tradeBeforeInsertHook)
if err = o.doBeforeInsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeInsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeInsertHook function to empty object, but got: %#v", o)
}
tradeBeforeInsertHooks = []TradeHook{}
AddTradeHook(boil.AfterInsertHook, tradeAfterInsertHook)
if err = o.doAfterInsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterInsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterInsertHook function to empty object, but got: %#v", o)
}
tradeAfterInsertHooks = []TradeHook{}
AddTradeHook(boil.AfterSelectHook, tradeAfterSelectHook)
if err = o.doAfterSelectHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterSelectHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterSelectHook function to empty object, but got: %#v", o)
}
tradeAfterSelectHooks = []TradeHook{}
AddTradeHook(boil.BeforeUpdateHook, tradeBeforeUpdateHook)
if err = o.doBeforeUpdateHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeUpdateHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeUpdateHook function to empty object, but got: %#v", o)
}
tradeBeforeUpdateHooks = []TradeHook{}
AddTradeHook(boil.AfterUpdateHook, tradeAfterUpdateHook)
if err = o.doAfterUpdateHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterUpdateHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterUpdateHook function to empty object, but got: %#v", o)
}
tradeAfterUpdateHooks = []TradeHook{}
AddTradeHook(boil.BeforeDeleteHook, tradeBeforeDeleteHook)
if err = o.doBeforeDeleteHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeDeleteHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeDeleteHook function to empty object, but got: %#v", o)
}
tradeBeforeDeleteHooks = []TradeHook{}
AddTradeHook(boil.AfterDeleteHook, tradeAfterDeleteHook)
if err = o.doAfterDeleteHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterDeleteHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterDeleteHook function to empty object, but got: %#v", o)
}
tradeAfterDeleteHooks = []TradeHook{}
AddTradeHook(boil.BeforeUpsertHook, tradeBeforeUpsertHook)
if err = o.doBeforeUpsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected BeforeUpsertHook function to empty object, but got: %#v", o)
}
tradeBeforeUpsertHooks = []TradeHook{}
AddTradeHook(boil.AfterUpsertHook, tradeAfterUpsertHook)
if err = o.doAfterUpsertHooks(ctx, nil); err != nil {
t.Errorf("Unable to execute doAfterUpsertHooks: %s", err)
}
if !reflect.DeepEqual(o, empty) {
t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o)
}
tradeAfterUpsertHooks = []TradeHook{}
}
func testTradesInsert(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
}
func testTradesInsertWhitelist(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Whitelist(tradeColumnsWithoutDefault...)); err != nil {
t.Error(err)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
}
func testTradeToOneExchangeUsingExchangeName(t *testing.T) {
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var local Trade
var foreign Exchange
seed := randomize.NewSeed()
if err := randomize.Struct(seed, &local, tradeDBTypes, false, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
if err := randomize.Struct(seed, &foreign, exchangeDBTypes, false, exchangeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Exchange struct: %s", err)
}
if err := foreign.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
local.ExchangeNameID = foreign.ID
if err := local.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
check, err := local.ExchangeName().One(ctx, tx)
if err != nil {
t.Fatal(err)
}
if check.ID != foreign.ID {
t.Errorf("want: %v, got %v", foreign.ID, check.ID)
}
slice := TradeSlice{&local}
if err = local.L.LoadExchangeName(ctx, tx, false, (*[]*Trade)(&slice), nil); err != nil {
t.Fatal(err)
}
if local.R.ExchangeName == nil {
t.Error("struct should have been eager loaded")
}
local.R.ExchangeName = nil
if err = local.L.LoadExchangeName(ctx, tx, true, &local, nil); err != nil {
t.Fatal(err)
}
if local.R.ExchangeName == nil {
t.Error("struct should have been eager loaded")
}
}
func testTradeToOneSetOpExchangeUsingExchangeName(t *testing.T) {
var err error
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
var a Trade
var b, c Exchange
seed := randomize.NewSeed()
if err = randomize.Struct(seed, &a, tradeDBTypes, false, strmangle.SetComplement(tradePrimaryKeyColumns, tradeColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &b, exchangeDBTypes, false, strmangle.SetComplement(exchangePrimaryKeyColumns, exchangeColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err = randomize.Struct(seed, &c, exchangeDBTypes, false, strmangle.SetComplement(exchangePrimaryKeyColumns, exchangeColumnsWithoutDefault)...); err != nil {
t.Fatal(err)
}
if err := a.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
if err = b.Insert(ctx, tx, boil.Infer()); err != nil {
t.Fatal(err)
}
for i, x := range []*Exchange{&b, &c} {
err = a.SetExchangeName(ctx, tx, i != 0, x)
if err != nil {
t.Fatal(err)
}
if a.R.ExchangeName != x {
t.Error("relationship struct not set to correct value")
}
if x.R.ExchangeNameTrade != &a {
t.Error("failed to append to foreign relationship struct")
}
if a.ExchangeNameID != x.ID {
t.Error("foreign key was wrong value", a.ExchangeNameID)
}
zero := reflect.Zero(reflect.TypeOf(a.ExchangeNameID))
reflect.Indirect(reflect.ValueOf(&a.ExchangeNameID)).Set(zero)
if err = a.Reload(ctx, tx); err != nil {
t.Fatal("failed to reload", err)
}
if a.ExchangeNameID != x.ID {
t.Error("foreign key was wrong value", a.ExchangeNameID, x.ID)
}
}
}
func testTradesReload(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
if err = o.Reload(ctx, tx); err != nil {
t.Error(err)
}
}
func testTradesReloadAll(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice := TradeSlice{o}
if err = slice.ReloadAll(ctx, tx); err != nil {
t.Error(err)
}
}
func testTradesSelect(t *testing.T) {
t.Parallel()
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
slice, err := Trades().All(ctx, tx)
if err != nil {
t.Error(err)
}
if len(slice) != 1 {
t.Error("want one record, got:", len(slice))
}
}
var (
tradeDBTypes = map[string]string{`ID`: `TEXT`, `ExchangeNameID`: `UUID`, `Tid`: `TEXT`, `Base`: `TEXT`, `Quote`: `TEXT`, `Asset`: `TEXT`, `Price`: `REAL`, `Amount`: `REAL`, `Side`: `TEXT`, `Timestamp`: `TIMESTAMP`}
_ = bytes.MinRead
)
func testTradesUpdate(t *testing.T) {
t.Parallel()
if 0 == len(tradePrimaryKeyColumns) {
t.Skip("Skipping table with no primary key columns")
}
if len(tradeAllColumns) == len(tradePrimaryKeyColumns) {
t.Skip("Skipping table with only primary key columns")
}
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradePrimaryKeyColumns...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
if rowsAff, err := o.Update(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("should only affect one row but affected", rowsAff)
}
}
func testTradesSliceUpdateAll(t *testing.T) {
t.Parallel()
if len(tradeAllColumns) == len(tradePrimaryKeyColumns) {
t.Skip("Skipping table with only primary key columns")
}
seed := randomize.NewSeed()
var err error
o := &Trade{}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradeColumnsWithDefault...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
ctx := context.Background()
tx := MustTx(boil.BeginTx(ctx, nil))
defer func() { _ = tx.Rollback() }()
if err = o.Insert(ctx, tx, boil.Infer()); err != nil {
t.Error(err)
}
count, err := Trades().Count(ctx, tx)
if err != nil {
t.Error(err)
}
if count != 1 {
t.Error("want one record, got:", count)
}
if err = randomize.Struct(seed, o, tradeDBTypes, true, tradePrimaryKeyColumns...); err != nil {
t.Errorf("Unable to randomize Trade struct: %s", err)
}
// Remove Primary keys and unique columns from what we plan to update
var fields []string
if strmangle.StringSliceMatch(tradeAllColumns, tradePrimaryKeyColumns) {
fields = tradeAllColumns
} else {
fields = strmangle.SetComplement(
tradeAllColumns,
tradePrimaryKeyColumns,
)
}
value := reflect.Indirect(reflect.ValueOf(o))
typ := reflect.TypeOf(o).Elem()
n := typ.NumField()
updateMap := M{}
for _, col := range fields {
for i := 0; i < n; i++ {
f := typ.Field(i)
if f.Tag.Get("boil") == col {
updateMap[col] = value.Field(i).Interface()
}
}
}
slice := TradeSlice{o}
if rowsAff, err := slice.UpdateAll(ctx, tx, updateMap); err != nil {
t.Error(err)
} else if rowsAff != 1 {
t.Error("wanted one record updated but got", rowsAff)
}
}

View File

@@ -49,29 +49,6 @@ var WithdrawalCryptoColumns = struct {
// Generated where
type whereHelpernull_String struct{ field string }
func (w whereHelpernull_String) EQ(x null.String) qm.QueryMod {
return qmhelper.WhereNullEQ(w.field, false, x)
}
func (w whereHelpernull_String) NEQ(x null.String) qm.QueryMod {
return qmhelper.WhereNullEQ(w.field, true, x)
}
func (w whereHelpernull_String) IsNull() qm.QueryMod { return qmhelper.WhereIsNull(w.field) }
func (w whereHelpernull_String) IsNotNull() qm.QueryMod { return qmhelper.WhereIsNotNull(w.field) }
func (w whereHelpernull_String) LT(x null.String) qm.QueryMod {
return qmhelper.Where(w.field, qmhelper.LT, x)
}
func (w whereHelpernull_String) LTE(x null.String) qm.QueryMod {
return qmhelper.Where(w.field, qmhelper.LTE, x)
}
func (w whereHelpernull_String) GT(x null.String) qm.QueryMod {
return qmhelper.Where(w.field, qmhelper.GT, x)
}
func (w whereHelpernull_String) GTE(x null.String) qm.QueryMod {
return qmhelper.Where(w.field, qmhelper.GTE, x)
}
var WithdrawalCryptoWhere = struct {
ID whereHelperint64
Address whereHelperstring

View File

@@ -33,8 +33,7 @@ func Series(exchangeName, base, quote string, interval int64, asset string, star
qm.Where("base = ?", strings.ToUpper(base)),
qm.Where("quote = ?", strings.ToUpper(quote)),
qm.Where("interval = ?", interval),
qm.Where("asset = ?", asset),
qm.Where("timestamp between ? and ?", start.UTC(), end.UTC()),
qm.Where("asset = ?", strings.ToLower(asset)),
}
exchangeUUID, errS := exchange.UUIDByName(exchangeName)
@@ -42,8 +41,8 @@ func Series(exchangeName, base, quote string, interval int64, asset string, star
return out, errS
}
queries = append(queries, qm.Where("exchange_name_id = ?", exchangeUUID.String()))
if repository.GetSQLDialect() == database.DBSQLite3 {
queries = append(queries, qm.Where("timestamp between ? and ?", start.UTC().Format(time.RFC3339), end.UTC().Format(time.RFC3339)))
retCandle, errC := modelSQLite.Candles(queries...).All(context.Background(), database.DB.SQL)
if errC != nil {
return out, errC
@@ -63,6 +62,7 @@ func Series(exchangeName, base, quote string, interval int64, asset string, star
})
}
} else {
queries = append(queries, qm.Where("timestamp between ? and ?", start.UTC(), end.UTC()))
retCandle, errC := modelPSQL.Candles(queries...).All(context.Background(), database.DB.SQL)
if errC != nil {
return out, errC
@@ -91,6 +91,86 @@ func Series(exchangeName, base, quote string, interval int64, asset string, star
return out, err
}
// DeleteCandles will delete all existing matching candles
func DeleteCandles(in *Item) (int64, error) {
if database.DB.SQL == nil {
return 0, database.ErrDatabaseSupportDisabled
}
if len(in.Candles) < 1 {
return 0, errNoCandleData
}
ctx := context.Background()
queries := []qm.QueryMod{
qm.Where("base = ?", strings.ToUpper(in.Base)),
qm.Where("quote = ?", strings.ToUpper(in.Quote)),
qm.Where("interval = ?", in.Interval),
qm.Where("asset = ?", strings.ToLower(in.Asset)),
qm.Where("exchange_name_id = ?", in.ExchangeID),
}
if repository.GetSQLDialect() == database.DBSQLite3 {
queries = append(queries, qm.Where("timestamp between ? and ?", in.Candles[0].Timestamp.UTC().Format(time.RFC3339), in.Candles[len(in.Candles)-1].Timestamp.UTC().Format(time.RFC3339)))
return deleteSQLite(ctx, queries)
}
queries = append(queries, qm.Where("timestamp between ? and ?", in.Candles[0].Timestamp.UTC(), in.Candles[len(in.Candles)-1].Timestamp.UTC()))
return deletePostgres(ctx, queries)
}
func deleteSQLite(ctx context.Context, queries []qm.QueryMod) (int64, error) {
retCandle, err := modelSQLite.Candles(queries...).All(context.Background(), database.DB.SQL)
if err != nil {
return 0, err
}
var tx *sql.Tx
tx, err = database.DB.SQL.BeginTx(ctx, nil)
if err != nil {
return 0, err
}
var totalDeleted int64
totalDeleted, err = retCandle.DeleteAll(ctx, tx)
if err != nil {
errRB := tx.Rollback()
if errRB != nil {
log.Errorln(log.DatabaseMgr, errRB)
}
return 0, err
}
err = tx.Commit()
if err != nil {
return 0, err
}
return totalDeleted, nil
}
func deletePostgres(ctx context.Context, queries []qm.QueryMod) (int64, error) {
retCandle, err := modelPSQL.Candles(queries...).All(context.Background(), database.DB.SQL)
if err != nil {
return 0, err
}
var tx *sql.Tx
tx, err = database.DB.SQL.BeginTx(ctx, nil)
if err != nil {
return 0, err
}
var totalDeleted int64
totalDeleted, err = retCandle.DeleteAll(ctx, tx)
if err != nil {
errRB := tx.Rollback()
if errRB != nil {
log.Errorln(log.DatabaseMgr, errRB)
}
return 0, err
}
err = tx.Commit()
if err != nil {
return 0, err
}
return totalDeleted, nil
}
// Insert series of candles
func Insert(in *Item) (uint64, error) {
if database.DB.SQL == nil {
@@ -136,7 +216,7 @@ func insertSQLite(ctx context.Context, tx *sql.Tx, in *Item) (uint64, error) {
Base: strings.ToUpper(in.Base),
Quote: strings.ToUpper(in.Quote),
Interval: strconv.FormatInt(in.Interval, 10),
Asset: in.Asset,
Asset: strings.ToLower(in.Asset),
Timestamp: in.Candles[x].Timestamp.UTC().Format(time.RFC3339),
Open: in.Candles[x].Open,
High: in.Candles[x].High,
@@ -168,7 +248,7 @@ func insertPostgresSQL(ctx context.Context, tx *sql.Tx, in *Item) (uint64, error
Base: strings.ToUpper(in.Base),
Quote: strings.ToUpper(in.Quote),
Interval: in.Interval,
Asset: in.Asset,
Asset: strings.ToLower(in.Asset),
Timestamp: in.Candles[x].Timestamp,
Open: in.Candles[x].Open,
High: in.Candles[x].High,

View File

@@ -105,8 +105,17 @@ func TestInsert(t *testing.T) {
}
if r != 365 {
t.Fatalf("unexpected number inserted: %v", r)
t.Errorf("unexpected number inserted: %v", r)
}
d, err := DeleteCandles(&data)
if err != nil {
t.Fatal(err)
}
if d != 365 {
t.Errorf("unexpected number deleted: %v", d)
}
err = testhelpers.CloseDatabase(dbConn)
if err != nil {
t.Error(err)

View File

@@ -10,7 +10,7 @@ const (
)
var (
errInvalidInput = errors.New("exchange, base , quote, asset, interval, start & end cannot be empty")
errInvalidInput = errors.New("exchange, base, quote, asset, interval, start & end cannot be empty")
errNoCandleData = errors.New("no candle data provided")
)

View File

@@ -72,7 +72,6 @@ func Insert(in Details) error {
if err != nil {
return err
}
if repository.GetSQLDialect() == database.DBSQLite3 {
err = insertSQLite(ctx, tx, []Details{in})
} else {
@@ -139,7 +138,7 @@ func insertSQLite(ctx context.Context, tx *sql.Tx, in []Details) (err error) {
return errUUID
}
var tempInsert = modelSQLite.Exchange{
Name: in[x].Name,
Name: strings.ToLower(in[x].Name),
ID: tempUUID.String(),
}
@@ -159,7 +158,7 @@ func insertSQLite(ctx context.Context, tx *sql.Tx, in []Details) (err error) {
func insertPostgresql(ctx context.Context, tx *sql.Tx, in []Details) (err error) {
for x := range in {
var tempInsert = modelPSQL.Exchange{
Name: in[x].Name,
Name: strings.ToLower(in[x].Name),
}
err = tempInsert.Upsert(ctx, tx, true, []string{"name"}, boil.Infer(), boil.Infer())

View File

@@ -0,0 +1,359 @@
package trade
import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
"time"
"github.com/gofrs/uuid"
"github.com/thrasher-corp/gocryptotrader/database"
modelPSQL "github.com/thrasher-corp/gocryptotrader/database/models/postgres"
modelSQLite "github.com/thrasher-corp/gocryptotrader/database/models/sqlite3"
"github.com/thrasher-corp/gocryptotrader/database/repository"
"github.com/thrasher-corp/gocryptotrader/database/repository/exchange"
"github.com/thrasher-corp/gocryptotrader/log"
"github.com/thrasher-corp/sqlboiler/boil"
"github.com/thrasher-corp/sqlboiler/queries/qm"
)
// Insert saves trade data to the database
func Insert(trades ...Data) error {
for i := range trades {
if trades[i].ExchangeNameID == "" && trades[i].Exchange != "" {
exchangeUUID, err := exchange.UUIDByName(trades[i].Exchange)
if err != nil {
return err
}
trades[i].ExchangeNameID = exchangeUUID.String()
} else if trades[i].ExchangeNameID == "" && trades[i].Exchange == "" {
return errors.New("exchange name/uuid not set, cannot insert")
}
}
ctx := context.Background()
ctx = boil.SkipTimestamps(ctx)
tx, err := database.DB.SQL.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("beginTx %w", err)
}
defer func() {
if err != nil {
errRB := tx.Rollback()
if errRB != nil {
log.Errorf(log.DatabaseMgr, "Insert tx.Rollback %v", errRB)
}
}
}()
if repository.GetSQLDialect() == database.DBSQLite3 || repository.GetSQLDialect() == database.DBSQLite {
err = insertSQLite(ctx, tx, trades...)
} else {
err = insertPostgres(ctx, tx, trades...)
}
if err != nil {
return err
}
return tx.Commit()
}
func insertSQLite(ctx context.Context, tx *sql.Tx, trades ...Data) error {
for i := range trades {
if trades[i].ID == "" {
freshUUID, err := uuid.NewV4()
if err != nil {
return err
}
trades[i].ID = freshUUID.String()
}
var tempEvent = modelSQLite.Trade{
ID: trades[i].ID,
ExchangeNameID: trades[i].ExchangeNameID,
Base: strings.ToUpper(trades[i].Base),
Quote: strings.ToUpper(trades[i].Quote),
Asset: strings.ToLower(trades[i].AssetType),
Price: trades[i].Price,
Amount: trades[i].Amount,
Timestamp: trades[i].Timestamp.UTC().Format(time.RFC3339),
}
if trades[i].Side != "" {
tempEvent.Side.SetValid(strings.ToUpper(trades[i].Side))
}
if trades[i].TID != "" {
tempEvent.Tid.SetValid(trades[i].TID)
}
err := tempEvent.Insert(ctx, tx, boil.Infer())
if err != nil {
return err
}
}
return nil
}
func insertPostgres(ctx context.Context, tx *sql.Tx, trades ...Data) error {
var err error
for i := range trades {
if trades[i].ID == "" {
var freshUUID uuid.UUID
freshUUID, err = uuid.NewV4()
if err != nil {
return err
}
trades[i].ID = freshUUID.String()
}
var tempEvent = modelPSQL.Trade{
ExchangeNameID: trades[i].ExchangeNameID,
Base: strings.ToUpper(trades[i].Base),
Quote: strings.ToUpper(trades[i].Quote),
Asset: strings.ToLower(trades[i].AssetType),
Price: trades[i].Price,
Amount: trades[i].Amount,
Timestamp: trades[i].Timestamp.UTC(),
ID: trades[i].ID,
}
if trades[i].Side != "" {
tempEvent.Side.SetValid(strings.ToUpper(trades[i].Side))
}
if trades[i].TID != "" {
tempEvent.Tid.SetValid(trades[i].TID)
}
err = tempEvent.Upsert(ctx, tx, false, nil, boil.Infer(), boil.Infer())
if err != nil {
return err
}
}
return nil
}
// GetByUUID returns a trade by its unique ID
func GetByUUID(uuid string) (td Data, err error) {
if repository.GetSQLDialect() == database.DBSQLite3 || repository.GetSQLDialect() == database.DBSQLite {
td, err = getByUUIDSQLite(uuid)
if err != nil {
return td, fmt.Errorf("trade.Get getByUUIDSQLite %w", err)
}
} else {
td, err = getByUUIDPostgres(uuid)
if err != nil {
return td, fmt.Errorf("trade.Get getByUUIDPostgres %w", err)
}
}
return td, nil
}
func getByUUIDSQLite(uuid string) (Data, error) {
var td Data
var ts time.Time
query := modelSQLite.Trades(qm.Where("id = ?", uuid))
result, err := query.One(context.Background(), database.DB.SQL)
if err != nil {
return td, err
}
ts, err = time.Parse(time.RFC3339, result.Timestamp)
if err != nil {
return td, err
}
td = Data{
ID: result.ID,
Exchange: result.ExchangeNameID,
Base: strings.ToUpper(result.Base),
Quote: strings.ToUpper(result.Quote),
AssetType: strings.ToLower(result.Asset),
Price: result.Price,
Amount: result.Amount,
Timestamp: ts,
}
if result.Side.Valid {
td.Side = result.Side.String
}
return td, nil
}
func getByUUIDPostgres(uuid string) (td Data, err error) {
query := modelPSQL.Trades(qm.Where("id = ?", uuid))
var result *modelPSQL.Trade
result, err = query.One(context.Background(), database.DB.SQL)
if err != nil {
return td, err
}
td = Data{
ID: result.ID,
Timestamp: result.Timestamp,
Exchange: result.ExchangeNameID,
Base: strings.ToUpper(result.Base),
Quote: strings.ToUpper(result.Quote),
AssetType: strings.ToLower(result.Asset),
Price: result.Price,
Amount: result.Amount,
}
if result.Side.Valid {
td.Side = result.Side.String
}
return td, nil
}
// GetInRange returns all trades by an exchange in a date range
func GetInRange(exchangeName, assetType, base, quote string, startDate, endDate time.Time) (td []Data, err error) {
if repository.GetSQLDialect() == database.DBSQLite3 || repository.GetSQLDialect() == database.DBSQLite {
td, err = getInRangeSQLite(exchangeName, assetType, base, quote, startDate, endDate)
if err != nil {
return td, fmt.Errorf("trade.GetByExchangeInRange getInRangeSQLite %w", err)
}
} else {
td, err = getInRangePostgres(exchangeName, assetType, base, quote, startDate, endDate)
if err != nil {
return td, fmt.Errorf("trade.GetByExchangeInRange getInRangePostgres %w", err)
}
}
return td, nil
}
func getInRangeSQLite(exchangeName, assetType, base, quote string, startDate, endDate time.Time) (td []Data, err error) {
var exchangeUUID uuid.UUID
exchangeUUID, err = exchange.UUIDByName(exchangeName)
if err != nil {
return nil, err
}
wheres := map[string]interface{}{
"exchange_name_id": exchangeUUID,
"asset": strings.ToLower(assetType),
"base": strings.ToUpper(base),
"quote": strings.ToUpper(quote),
}
q := generateQuery(wheres, startDate, endDate)
query := modelSQLite.Trades(q...)
var result []*modelSQLite.Trade
result, err = query.All(context.Background(), database.DB.SQL)
if err != nil {
return td, err
}
for i := range result {
ts, err := time.Parse(time.RFC3339, result[i].Timestamp)
if err != nil {
return td, err
}
t := Data{
ID: result[i].ID,
Timestamp: ts,
Exchange: strings.ToLower(exchangeName),
Base: strings.ToUpper(result[i].Base),
Quote: strings.ToUpper(result[i].Quote),
AssetType: strings.ToLower(result[i].Asset),
Price: result[i].Price,
Amount: result[i].Amount,
}
if result[i].Side.Valid {
t.Side = result[i].Side.String
}
td = append(td, t)
}
return td, nil
}
func getInRangePostgres(exchangeName, assetType, base, quote string, startDate, endDate time.Time) (td []Data, err error) {
var exchangeUUID uuid.UUID
exchangeUUID, err = exchange.UUIDByName(exchangeName)
if err != nil {
return nil, err
}
wheres := map[string]interface{}{
"exchange_name_id": exchangeUUID,
"asset": strings.ToLower(assetType),
"base": strings.ToUpper(base),
"quote": strings.ToUpper(quote),
}
q := generateQuery(wheres, startDate, endDate)
query := modelPSQL.Trades(q...)
var result []*modelPSQL.Trade
result, err = query.All(context.Background(), database.DB.SQL)
if err != nil {
return td, err
}
for i := range result {
t := Data{
ID: result[i].ID,
Timestamp: result[i].Timestamp,
Exchange: strings.ToLower(exchangeName),
Base: strings.ToUpper(result[i].Base),
Quote: strings.ToUpper(result[i].Quote),
AssetType: strings.ToLower(result[i].Asset),
Price: result[i].Price,
Amount: result[i].Amount,
}
if result[i].Side.Valid {
t.Side = result[i].Side.String
}
td = append(td, t)
}
return td, nil
}
// DeleteTrades will remove trades from the database using trade.Data
func DeleteTrades(trades ...Data) error {
ctx := context.Background()
ctx = boil.SkipTimestamps(ctx)
tx, err := database.DB.SQL.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("beginTx %w", err)
}
defer func() {
if err != nil {
errRB := tx.Rollback()
if errRB != nil {
log.Errorf(log.DatabaseMgr, "DeleteTrades tx.Rollback %v", errRB)
}
}
}()
if repository.GetSQLDialect() == database.DBSQLite3 || repository.GetSQLDialect() == database.DBSQLite {
err = deleteTradesSQLite(context.Background(), tx, trades...)
} else {
err = deleteTradesPostgres(context.Background(), tx, trades...)
}
if err != nil {
return err
}
return tx.Commit()
}
func deleteTradesSQLite(ctx context.Context, tx *sql.Tx, trades ...Data) error {
var tradeIDs []interface{}
for i := range trades {
tradeIDs = append(tradeIDs, trades[i].ID)
}
query := modelSQLite.Trades(qm.WhereIn(`id in ?`, tradeIDs...))
_, err := query.DeleteAll(ctx, tx)
return err
}
func deleteTradesPostgres(ctx context.Context, tx *sql.Tx, trades ...Data) error {
var tradeIDs []interface{}
for i := range trades {
tradeIDs = append(tradeIDs, trades[i].ID)
}
query := modelPSQL.Trades(qm.WhereIn(`id in ?`, tradeIDs...))
_, err := query.DeleteAll(ctx, tx)
return err
}
func generateQuery(clauses map[string]interface{}, start, end time.Time) []qm.QueryMod {
query := []qm.QueryMod{
qm.Where("timestamp BETWEEN ? AND ?", start.UTC().Format(time.RFC3339), end.UTC().Format(time.RFC3339)),
}
for k, v := range clauses {
query = append(query, qm.Where(k+` = ?`, v))
}
return query
}

View File

@@ -0,0 +1,201 @@
package trade
import (
"fmt"
"io/ioutil"
"log"
"os"
"testing"
"time"
"github.com/gofrs/uuid"
"github.com/thrasher-corp/gocryptotrader/currency"
"github.com/thrasher-corp/gocryptotrader/database"
"github.com/thrasher-corp/gocryptotrader/database/drivers"
"github.com/thrasher-corp/gocryptotrader/database/repository/exchange"
"github.com/thrasher-corp/gocryptotrader/database/testhelpers"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
)
var (
verbose = false
testExchanges = []exchange.Details{
{
Name: "one",
},
{
Name: "two",
},
}
)
func TestMain(m *testing.M) {
if verbose {
testhelpers.EnableVerboseTestOutput()
}
var err error
testhelpers.PostgresTestDatabase = testhelpers.GetConnectionDetails()
testhelpers.TempDir, err = ioutil.TempDir("", "gct-temp")
if err != nil {
log.Fatal(err)
}
os.Exit(m.Run())
}
func TestTrades(t *testing.T) {
testCases := []struct {
name string
config *database.Config
seedDB func() error
runner func(t *testing.T)
closer func(dbConn *database.Instance) error
}{
{
name: "postgresql",
config: testhelpers.PostgresTestDatabase,
seedDB: seedDB,
},
{
name: "SQLite",
config: &database.Config{
Driver: database.DBSQLite3,
ConnectionDetails: drivers.ConnectionDetails{Database: "./testdb"},
},
seedDB: seedDB,
},
}
for x := range testCases {
test := testCases[x]
t.Run(test.name, func(t *testing.T) {
if !testhelpers.CheckValidConfig(&test.config.ConnectionDetails) {
t.Skip("database not configured skipping test")
}
dbConn, err := testhelpers.ConnectToDatabase(test.config)
if err != nil {
t.Fatal(err)
}
if test.seedDB != nil {
err = test.seedDB()
if err != nil {
t.Error(err)
}
}
tradeSQLTester(t)
err = testhelpers.CloseDatabase(dbConn)
if err != nil {
t.Error(err)
}
})
}
}
func tradeSQLTester(t *testing.T) {
var trades, trades2 []Data
for i := 0; i < 20; i++ {
uu, _ := uuid.NewV4()
trades = append(trades, Data{
ID: uu.String(),
Timestamp: time.Now(),
Exchange: testExchanges[0].Name,
Base: currency.BTC.String(),
Quote: currency.USD.String(),
AssetType: asset.Spot.String(),
Price: float64(i * (i + 3)),
Amount: float64(i * (i + 2)),
Side: order.Buy.String(),
TID: fmt.Sprintf("%v", i),
})
}
err := Insert(trades...)
if err != nil {
t.Fatal(err)
}
// insert the same trades to test conflict resolution
for i := 0; i < 20; i++ {
uu, _ := uuid.NewV4()
trades2 = append(trades2, Data{
ID: uu.String(),
Timestamp: time.Now(),
Exchange: testExchanges[0].Name,
Base: currency.BTC.String(),
Quote: currency.USD.String(),
AssetType: asset.Spot.String(),
Price: float64(i * (i + 3)),
Amount: float64(i * (i + 2)),
Side: order.Buy.String(),
TID: fmt.Sprintf("%v", i),
})
}
err = Insert(trades2...)
if err != nil {
t.Fatal(err)
}
resp, err := GetInRange(
testExchanges[0].Name,
asset.Spot.String(),
currency.BTC.String(),
currency.USD.String(),
time.Now().Add(-time.Hour),
time.Now().Add(time.Hour),
)
if err != nil {
t.Error(err)
}
if len(resp) != 20 {
t.Fatalf("unique constraints failing, got %v", resp)
}
v, err := GetInRange(
testExchanges[0].Name,
asset.Spot.String(),
currency.BTC.String(),
currency.USD.String(),
time.Now().Add(-time.Hour),
time.Now().Add(time.Hour))
if err != nil {
t.Error(err)
}
if len(v) == 0 {
t.Error("Bad get!")
}
err = DeleteTrades(trades...)
if err != nil {
t.Error(err)
}
err = DeleteTrades(trades2...)
if err != nil {
t.Error(err)
}
v, err = GetInRange(
testExchanges[0].Name,
asset.Spot.String(),
currency.BTC.String(),
currency.USD.String(),
time.Now().Add(-time.Hour),
time.Now().Add(time.Hour))
if err != nil {
t.Error(err)
}
if len(v) != 0 {
t.Errorf("should all be ded %v", v)
}
}
func seedDB() error {
err := exchange.InsertMany(testExchanges)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,19 @@
package trade
import "time"
// Data defines trade data in its simplest
// db friendly form
type Data struct {
ID string
TID string
Exchange string
ExchangeNameID string
Base string
Quote string
AssetType string
Price float64
Amount float64
Side string
Timestamp time.Time
}

View File

@@ -117,7 +117,9 @@ func migrateDB(db *sql.DB) error {
// EnableVerboseTestOutput enables debug output for SQL queries
func EnableVerboseTestOutput() {
c := log.GenDefaultSettings()
log.RWM.Lock()
log.GlobalLogConfig = &c
log.RWM.Unlock()
log.SetupGlobalLogger()
DBLogger := database.Logger{}