diff --git a/services/horizon/internal/actions/helpers.go b/services/horizon/internal/actions/helpers.go index 2575b13251..bd9608d97a 100644 --- a/services/horizon/internal/actions/helpers.go +++ b/services/horizon/internal/actions/helpers.go @@ -3,6 +3,7 @@ package actions import ( "context" "encoding/hex" + "errors" "fmt" "net/http" "net/url" @@ -20,7 +21,6 @@ import ( "github.com/stellar/go/services/horizon/internal/db2" "github.com/stellar/go/services/horizon/internal/ledger" hProblem "github.com/stellar/go/services/horizon/internal/render/problem" - "github.com/stellar/go/support/errors" "github.com/stellar/go/support/ordered" "github.com/stellar/go/support/render/problem" "github.com/stellar/go/toid" @@ -171,7 +171,7 @@ func getLimit(r *http.Request, name string, def uint64, max uint64) (uint64, err if asI64 <= 0 { err = errors.New("invalid limit: non-positive value provided") } else if asI64 > int64(max) { - err = errors.Errorf("invalid limit: value provided that is over limit max of %d", max) + err = fmt.Errorf("invalid limit: value provided that is over limit max of %d", max) } if err != nil { @@ -221,12 +221,16 @@ func GetPageQuery(ledgerState *ledger.State, r *http.Request, opts ...Opt) (db2. return db2.PageQuery{}, err } - if cursor == "" && defaultTOID { - if pageQuery.Order == db2.OrderAscending { - pageQuery.Cursor = toid.AfterLedger( - ordered.Max(0, ledgerState.CurrentStatus().HistoryElder-1), - ).String() + elderCursor := toid.AfterLedger( + ordered.Max(0, ledgerState.CurrentStatus().HistoryElder-1), + ).String() + + if defaultTOID && pageQuery.Order == db2.OrderAscending { + if cursor == "" || errors.Is(validateCursorWithinHistory(ledgerState, pageQuery), &hProblem.BeforeHistory) { + pageQuery.Cursor = elderCursor } + } else if defaultTOID && pageQuery.Order == db2.OrderDescending { + pageQuery.ElderCursor = elderCursor } return pageQuery, nil @@ -596,7 +600,7 @@ func countNonEmpty(params ...interface{}) (int, error) { for _, param := range params { switch param := param.(type) { default: - return 0, errors.Errorf("unexpected type %T", param) + return 0, fmt.Errorf("unexpected type %T", param) case int32: if param != 0 { count++ diff --git a/services/horizon/internal/db2/history/account_signers.go b/services/horizon/internal/db2/history/account_signers.go index 78afcdf06c..8014379a4b 100644 --- a/services/horizon/internal/db2/history/account_signers.go +++ b/services/horizon/internal/db2/history/account_signers.go @@ -37,7 +37,7 @@ func (q *Q) SignersForAccounts(ctx context.Context, accounts []string) ([]Accoun // AccountsForSigner returns a list of `AccountSigner` rows for a given signer func (q *Q) AccountsForSigner(ctx context.Context, signer string, page db2.PageQuery) ([]AccountSigner, error) { sql := selectAccountSigners.Where("accounts_signers.signer = ?", signer) - sql, err := page.ApplyToUsingCursor(sql, "accounts_signers.account_id", page.Cursor) + sql, err := page.ApplyRawTo(sql, "accounts_signers.account_id") if err != nil { return nil, errors.Wrap(err, "could not apply query to page") } diff --git a/services/horizon/internal/db2/history/accounts.go b/services/horizon/internal/db2/history/accounts.go index 2af197da4f..8f3b79a4fe 100644 --- a/services/horizon/internal/db2/history/accounts.go +++ b/services/horizon/internal/db2/history/accounts.go @@ -144,7 +144,7 @@ func (q *Q) AccountsForAsset(ctx context.Context, asset xdr.Asset, page db2.Page "trust_lines.asset_code": code, }) - sql, err := page.ApplyToUsingCursor(sql, "trust_lines.account_id", page.Cursor) + sql, err := page.ApplyRawTo(sql, "trust_lines.account_id") if err != nil { return nil, errors.Wrap(err, "could not apply query to page") } @@ -168,7 +168,7 @@ func (q *Q) AccountsForLiquidityPool(ctx context.Context, poolID string, page db "trust_lines.liquidity_pool_id": poolID, }) - sql, err := page.ApplyToUsingCursor(sql, "trust_lines.account_id", page.Cursor) + sql, err := page.ApplyRawTo(sql, "trust_lines.account_id") if err != nil { return nil, errors.Wrap(err, "could not apply query to page") } @@ -189,7 +189,7 @@ func selectBySponsor(table, sponsor string, page db2.PageQuery) (sq.SelectBuilde "sponsor": sponsor, }) - sql, err := page.ApplyToUsingCursor(sql, "account_id", page.Cursor) + sql, err := page.ApplyRawTo(sql, "account_id") if err != nil { return sql, errors.Wrap(err, "could not apply query to page") } @@ -255,7 +255,7 @@ func (q *Q) AccountEntriesForSigner(ctx context.Context, signer string, page db2 "accounts_signers.signer": signer, }) - sql, err := page.ApplyToUsingCursor(sql, "accounts_signers.account_id", page.Cursor) + sql, err := page.ApplyRawTo(sql, "accounts_signers.account_id") if err != nil { return nil, errors.Wrap(err, "could not apply query to page") } diff --git a/services/horizon/internal/db2/main.go b/services/horizon/internal/db2/main.go index 5ec81eb0f3..c497817061 100644 --- a/services/horizon/internal/db2/main.go +++ b/services/horizon/internal/db2/main.go @@ -5,7 +5,8 @@ package db2 // PageQuery represents a portion of a Query struct concerned with paging // through a large dataset. type PageQuery struct { - Cursor string - Order string - Limit uint64 + Cursor string + Order string + Limit uint64 + ElderCursor string } diff --git a/services/horizon/internal/db2/page_query.go b/services/horizon/internal/db2/page_query.go index 3c458af855..988b294366 100644 --- a/services/horizon/internal/db2/page_query.go +++ b/services/horizon/internal/db2/page_query.go @@ -59,7 +59,7 @@ func (p PageQuery) ApplyTo( return sql, err } - return p.ApplyToUsingCursor(sql, col, cursor) + return p.applyToUsingCursor(sql, col, cursor) } // ApplyRawTo applies the raw PageQuery.Cursor cursor to the builder @@ -67,12 +67,12 @@ func (p PageQuery) ApplyRawTo( sql sq.SelectBuilder, col string, ) (sq.SelectBuilder, error) { - return p.ApplyToUsingCursor(sql, col, p.Cursor) + return p.applyToUsingCursor(sql, col, p.Cursor) } // ApplyToUsingCursor returns a new SelectBuilder after applying the paging effects of // `p` to `sql`. This method allows any type of cursor by a single column -func (p PageQuery) ApplyToUsingCursor( +func (p PageQuery) applyToUsingCursor( sql sq.SelectBuilder, col string, cursor interface{}, @@ -81,23 +81,23 @@ func (p PageQuery) ApplyToUsingCursor( switch p.Order { case "asc": - if cursor == "" { + if cursor != "" { sql = sql. - OrderBy(fmt.Sprintf("%s asc", col)) - } else { - sql = sql. - Where(fmt.Sprintf("%s > ?", col), cursor). - OrderBy(fmt.Sprintf("%s asc", col)) + Where(fmt.Sprintf("%s > ?", col), cursor) } + sql = sql. + OrderBy(fmt.Sprintf("%s asc", col)) case "desc": - if cursor == "" { + if cursor != "" { sql = sql. - OrderBy(fmt.Sprintf("%s desc", col)) - } else { + Where(fmt.Sprintf("%s < ?", col), cursor) + } + if p.ElderCursor != "" { sql = sql. - Where(fmt.Sprintf("%s < ?", col), cursor). - OrderBy(fmt.Sprintf("%s desc", col)) + Where(fmt.Sprintf("%s > ?", col), p.ElderCursor) } + sql = sql. + OrderBy(fmt.Sprintf("%s desc", col)) default: return sql, errors.Errorf("invalid order: %s", p.Order) }