Merge pull request #8052 from bhandras/sql-invoices

sqldb: `InvoiceDB` implementation
This commit is contained in:
Olaoluwa Osuntokun authored and GitHub committed 2024-03-06 21:15:14 -06:00
commit 716c6dddd8
39 files changed
+3129 -1124

No files matched your search

+4
View File
@@ -252,6 +252,10 @@ jobs:
args: backend=bitcoind dbbackend=postgres args: backend=bitcoind dbbackend=postgres
- name: bitcoind-sqlite - name: bitcoind-sqlite
args: backend=bitcoind dbbackend=sqlite args: backend=bitcoind dbbackend=sqlite
- name: bitcoind-postgres-nativesql
args: backend=bitcoind dbbackend=postgres nativesql=true
- name: bitcoind-sqlite-nativesql
args: backend=bitcoind dbbackend=sqlite nativesql=true
- name: neutrino - name: neutrino
args: backend=neutrino args: backend=neutrino
steps: steps:
+30 -10
View File
@@ -3,6 +3,7 @@ package lnd
import ( import (
"bytes" "bytes"
"context" "context"
"database/sql"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@@ -31,6 +32,7 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/chainreg"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/kvdb"
@@ -42,6 +44,7 @@ import (
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/rpcperms" "github.com/lightningnetwork/lnd/rpcperms"
"github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/signal"
"github.com/lightningnetwork/lnd/sqldb"
"github.com/lightningnetwork/lnd/walletunlocker" "github.com/lightningnetwork/lnd/walletunlocker"
"github.com/lightningnetwork/lnd/watchtower" "github.com/lightningnetwork/lnd/watchtower"
"github.com/lightningnetwork/lnd/watchtower/wtclient" "github.com/lightningnetwork/lnd/watchtower/wtclient"
@@ -869,6 +872,11 @@ type DatabaseInstances struct {
// WalletDB is the configuration for loading the wallet database using // WalletDB is the configuration for loading the wallet database using
// the btcwallet's loader. // the btcwallet's loader.
WalletDB btcwallet.LoaderOption WalletDB btcwallet.LoaderOption
// NativeSQLStore is a pointer to a native SQL store that can be used
// for native SQL queries for tables that already support it. This may
// be nil if the use-native-sql flag was not set.
NativeSQLStore *sqldb.BaseDB
} }
// DefaultDatabaseBuilder is a type that builds the default database backends // DefaultDatabaseBuilder is a type that builds the default database backends
@@ -923,10 +931,11 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
// state DB point to the same local or remote DB and the same namespace // state DB point to the same local or remote DB and the same namespace
// within that DB. // within that DB.
dbs := &DatabaseInstances{ dbs := &DatabaseInstances{
HeightHintDB: databaseBackends.HeightHintDB, HeightHintDB: databaseBackends.HeightHintDB,
MacaroonDB: databaseBackends.MacaroonDB, MacaroonDB: databaseBackends.MacaroonDB,
DecayedLogDB: databaseBackends.DecayedLogDB, DecayedLogDB: databaseBackends.DecayedLogDB,
WalletDB: databaseBackends.WalletDB, WalletDB: databaseBackends.WalletDB,
NativeSQLStore: databaseBackends.NativeSQLStore,
} }
cleanUp := func() { cleanUp := func() {
// We can just close the returned close functions directly. Even // We can just close the returned close functions directly. Even
@@ -1011,11 +1020,21 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
// using the same struct (and DB backend) instance. // using the same struct (and DB backend) instance.
dbs.ChanStateDB = dbs.GraphDB dbs.ChanStateDB = dbs.GraphDB
// For now the only InvoiceDB implementation is the *channeldb.DB. // Instantiate a native SQL invoice store if the flag is set.
// if d.cfg.DB.UseNativeSQL {
// TODO(positiveblue): use a sql first implementation for this executor := sqldb.NewTransactionExecutor(
// interface. dbs.NativeSQLStore,
dbs.InvoiceDB = dbs.GraphDB func(tx *sql.Tx) sqldb.InvoiceQueries {
return dbs.NativeSQLStore.WithTx(tx)
},
)
dbs.InvoiceDB = sqldb.NewInvoiceStore(
executor, clock.NewDefaultClock(),
)
} else {
dbs.InvoiceDB = dbs.GraphDB
}
// Wrap the watchtower client DB and make sure we clean up. // Wrap the watchtower client DB and make sure we clean up.
if cfg.WtClient.Active { if cfg.WtClient.Active {
@@ -1298,8 +1317,9 @@ func initNeutrinoBackend(ctx context.Context, cfg *Config, chainDir string,
) )
switch { switch {
case cfg.DB.Backend == kvdb.SqliteBackendName: case cfg.DB.Backend == kvdb.SqliteBackendName:
sqliteConfig := lncfg.GetSqliteConfigKVDB(cfg.DB.Sqlite)
db, err = kvdb.Open( db, err = kvdb.Open(
kvdb.SqliteBackendName, ctx, cfg.DB.Sqlite, dbPath, kvdb.SqliteBackendName, ctx, sqliteConfig, dbPath,
lncfg.SqliteNeutrinoDBName, lncfg.NSNeutrinoDB, lncfg.SqliteNeutrinoDBName, lncfg.NSNeutrinoDB,
) )
@@ -339,6 +339,10 @@
* [Refactor UpdateInvoice](https://github.com/lightningnetwork/lnd/pull/8100) to * [Refactor UpdateInvoice](https://github.com/lightningnetwork/lnd/pull/8100) to
make it simpler to adjust code to also support SQL InvoiceDB implementation. make it simpler to adjust code to also support SQL InvoiceDB implementation.
* [InvoiceDB implementation](https://github.com/lightningnetwork/lnd/pull/8052)
for SQL backends enabling new users to optionally use an experimental native
SQL invoices database.
## Code Health ## Code Health
* [Remove database pointers](https://github.com/lightningnetwork/lnd/pull/8117) * [Remove database pointers](https://github.com/lightningnetwork/lnd/pull/8117)
+51 -1
View File
@@ -3,6 +3,7 @@ package invoices_test
import ( import (
"context" "context"
"crypto/rand" "crypto/rand"
"database/sql"
"fmt" "fmt"
"math" "math"
"testing" "testing"
@@ -18,6 +19,7 @@ import (
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/record"
"github.com/lightningnetwork/lnd/sqldb"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -114,12 +116,60 @@ func TestInvoiceRegistry(t *testing.T) {
return db, testClock return db, testClock
} }
// First create a shared Postgres instance so we don't spawn a new
// docker container for each test.
pgFixture := sqldb.NewTestPgFixture(
t, sqldb.DefaultPostgresFixtureLifetime,
)
t.Cleanup(func() {
pgFixture.TearDown(t)
})
makeSQLDB := func(t *testing.T, sqlite bool) (invpkg.InvoiceDB,
*clock.TestClock) {
var db *sqldb.BaseDB
if sqlite {
db = sqldb.NewTestSqliteDB(t).BaseDB
} else {
db = sqldb.NewTestPostgresDB(t, pgFixture).BaseDB
}
executor := sqldb.NewTransactionExecutor(
db, func(tx *sql.Tx) sqldb.InvoiceQueries {
return db.WithTx(tx)
},
)
testClock := clock.NewTestClock(testNow)
return sqldb.NewInvoiceStore(executor, testClock), testClock
}
for _, test := range testList { for _, test := range testList {
test := test test := test
t.Run(test.name, func(t *testing.T) { t.Run(test.name+"_KV", func(t *testing.T) {
test.test(t, makeKeyValueDB) test.test(t, makeKeyValueDB)
}) })
t.Run(test.name+"_SQLite", func(t *testing.T) {
test.test(t,
func(t *testing.T) (
invpkg.InvoiceDB, *clock.TestClock) {
return makeSQLDB(t, true)
})
})
t.Run(test.name+"_Postgres", func(t *testing.T) {
test.test(t,
func(t *testing.T) (
invpkg.InvoiceDB, *clock.TestClock) {
return makeSQLDB(t, false)
})
})
} }
} }
+131 -30
View File
@@ -3,8 +3,10 @@ package invoices_test
import ( import (
"context" "context"
"crypto/rand" "crypto/rand"
"database/sql"
"fmt" "fmt"
"math" "math"
"strings"
"testing" "testing"
"time" "time"
@@ -16,6 +18,7 @@ import (
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/record"
"github.com/lightningnetwork/lnd/sqldb"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -34,6 +37,23 @@ var (
testNow = time.Unix(1, 0) testNow = time.Unix(1, 0)
) )
// randBytesToString will return a "safe" string from a byte slice. This is
// used to generate random strings for the invoice payment request.
func randBytesToString(buf []byte) (string, error) {
const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
var stringBuilder strings.Builder
stringBuilder.Grow(len(buf))
for i := 0; i < len(buf); i++ {
ch := charset[int(buf[i])%len(charset)]
if err := stringBuilder.WriteByte(ch); err != nil {
return "", err
}
}
return stringBuilder.String(), nil
}
func randInvoice(value lnwire.MilliSatoshi) (*invpkg.Invoice, error) { func randInvoice(value lnwire.MilliSatoshi) (*invpkg.Invoice, error) {
var ( var (
pre lntypes.Preimage pre lntypes.Preimage
@@ -68,7 +88,11 @@ func randInvoice(value lnwire.MilliSatoshi) (*invpkg.Invoice, error) {
return nil, err return nil, err
} }
if r[0]&1 == 0 { if r[0]&1 == 0 {
i.PaymentRequest = r[:] paymentReq, err := randBytesToString(r[:])
if err != nil {
return nil, err
}
i.PaymentRequest = []byte(paymentReq)
} else { } else {
i.PaymentRequest = []byte("") i.PaymentRequest = []byte("")
} }
@@ -77,11 +101,13 @@ func randInvoice(value lnwire.MilliSatoshi) (*invpkg.Invoice, error) {
} }
// settleTestInvoice settles a test invoice. // settleTestInvoice settles a test invoice.
func settleTestInvoice(invoice *invpkg.Invoice, settleIndex uint64) { func settleTestInvoice(invoice *invpkg.Invoice, htlcID uint64,
settleIndex uint64) {
invoice.SettleDate = testNow invoice.SettleDate = testNow
invoice.AmtPaid = invoice.Terms.Value invoice.AmtPaid = invoice.Terms.Value
invoice.State = invpkg.ContractSettled invoice.State = invpkg.ContractSettled
invoice.Htlcs[models.CircuitKey{}] = &invpkg.InvoiceHTLC{ invoice.Htlcs[models.CircuitKey{HtlcID: htlcID}] = &invpkg.InvoiceHTLC{
Amt: invoice.Terms.Value, Amt: invoice.Terms.Value,
AcceptTime: testNow, AcceptTime: testNow,
ResolveTime: testNow, ResolveTime: testNow,
@@ -196,12 +222,53 @@ func TestInvoices(t *testing.T) {
return db return db
} }
// First create a shared Postgres instance so we don't spawn a new
// docker container for each test.
pgFixture := sqldb.NewTestPgFixture(
t, sqldb.DefaultPostgresFixtureLifetime,
)
t.Cleanup(func() {
pgFixture.TearDown(t)
})
makeSQLDB := func(t *testing.T, sqlite bool) invpkg.InvoiceDB {
var db *sqldb.BaseDB
if sqlite {
db = sqldb.NewTestSqliteDB(t).BaseDB
} else {
db = sqldb.NewTestPostgresDB(t, pgFixture).BaseDB
}
executor := sqldb.NewTransactionExecutor(
db, func(tx *sql.Tx) sqldb.InvoiceQueries {
return db.WithTx(tx)
},
)
testClock := clock.NewTestClock(testNow)
return sqldb.NewInvoiceStore(executor, testClock)
}
for _, test := range testList { for _, test := range testList {
test := test test := test
t.Run(test.name+"_KV", func(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
test.test(t, makeKeyValueDB) test.test(t, makeKeyValueDB)
}) })
t.Run(test.name+"_SQLite", func(t *testing.T) {
test.test(t,
func(t *testing.T) invpkg.InvoiceDB {
return makeSQLDB(t, true)
})
})
t.Run(test.name+"_Postgres", func(t *testing.T) {
test.test(t,
func(t *testing.T) invpkg.InvoiceDB {
return makeSQLDB(t, false)
})
})
} }
} }
@@ -333,7 +400,7 @@ func testInvoiceWorkflowImpl(t *testing.T, test invWorkflowTest,
// now have the settled bit toggle to true and a non-default // now have the settled bit toggle to true and a non-default
// SettledDate // SettledDate
payAmt := fakeInvoice.Terms.Value * 2 payAmt := fakeInvoice.Terms.Value * 2
_, err = db.UpdateInvoice(ctxb, ref, nil, getUpdateInvoice(payAmt)) _, err = db.UpdateInvoice(ctxb, ref, nil, getUpdateInvoice(0, payAmt))
require.NoError(t, err, "unable to settle invoice") require.NoError(t, err, "unable to settle invoice")
dbInvoice2, err := db.LookupInvoice(ctxb, ref) dbInvoice2, err := db.LookupInvoice(ctxb, ref)
require.NoError(t, err, "unable to fetch invoice") require.NoError(t, err, "unable to fetch invoice")
@@ -512,7 +579,7 @@ func testFailInvoiceLookupMPPPayAddrOnly(t *testing.T,
// for the given HTLC. // for the given HTLC.
ref := invpkg.InvoiceRefByHashAndAddr(payHash, payAddr) ref := invpkg.InvoiceRefByHashAndAddr(payHash, payAddr)
_, err = db.LookupInvoice(ctxb, ref) _, err = db.LookupInvoice(ctxb, ref)
require.Equal(t, invpkg.ErrInvoiceNotFound, err) require.ErrorIs(t, err, invpkg.ErrInvoiceNotFound)
} }
// testInvRefEquivocation asserts that retrieving or updating an invoice using // testInvRefEquivocation asserts that retrieving or updating an invoice using
@@ -690,7 +757,7 @@ func testInvoiceCancelSingleHtlcAMP(t *testing.T,
// At this point, we should detect that 3k satoshis total has been // At this point, we should detect that 3k satoshis total has been
// paid. // paid.
require.Equal(t, dbInvoice.AmtPaid, amt*3) require.EqualValues(t, amt*3, dbInvoice.AmtPaid)
callback := func( callback := func(
invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) { invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) {
@@ -902,8 +969,11 @@ func testInvoiceAddTimeSeries(t *testing.T,
_, err = db.InvoicesSettledSince(ctxb, 0) _, err = db.InvoicesSettledSince(ctxb, 0)
require.NoError(t, err) require.NoError(t, err)
var settledInvoices []invpkg.Invoice var (
var settleIndex uint64 = 1 settledInvoices []invpkg.Invoice
settleIndex uint64 = 1
htlcID uint64 = 0
)
// We'll now only settle the latter half of each of those invoices. // We'll now only settle the latter half of each of those invoices.
for i := 10; i < len(invoices); i++ { for i := 10; i < len(invoices); i++ {
invoice := &invoices[i] invoice := &invoices[i]
@@ -912,15 +982,18 @@ func testInvoiceAddTimeSeries(t *testing.T,
ref := invpkg.InvoiceRefByHash(paymentHash) ref := invpkg.InvoiceRefByHash(paymentHash)
_, err := db.UpdateInvoice( _, err := db.UpdateInvoice(
ctxb, ref, nil, getUpdateInvoice(invoice.Terms.Value), ctxb, ref, nil, getUpdateInvoice(
htlcID, invoice.Terms.Value,
),
) )
if err != nil { if err != nil {
t.Fatalf("unable to settle invoice: %v", err) t.Fatalf("unable to settle invoice: %v", err)
} }
// Create the settled invoice for the expectation set. // Create the settled invoice for the expectation set.
settleTestInvoice(invoice, settleIndex) settleTestInvoice(invoice, htlcID, settleIndex)
settleIndex++ settleIndex++
htlcID++
settledInvoices = append(settledInvoices, *invoice) settledInvoices = append(settledInvoices, *invoice)
} }
@@ -960,7 +1033,10 @@ func testInvoiceAddTimeSeries(t *testing.T,
t.Fatalf("unable to query: %v", err) t.Fatalf("unable to query: %v", err)
} }
require.Equal(t, len(query.resp), len(resp)) require.Equal(
t, len(query.resp), len(resp),
fmt.Sprintf("test: #%v", i),
)
for j := 0; j < len(query.resp); j++ { for j := 0; j < len(query.resp); j++ {
require.Equal(t, require.Equal(t,
@@ -1138,7 +1214,11 @@ func testSettleIndexAmpPayments(t *testing.T,
AddIndex: testInvoice.AddIndex, AddIndex: testInvoice.AddIndex,
}, },
}) })
require.Nil(t, err) require.NoError(t, err)
settledInvoices, err = db.InvoicesSettledSince(ctxb, 0)
require.NoError(t, err)
require.Len(t, settledInvoices, 0)
} }
// testFetchPendingInvoices tests that we can fetch all pending invoices from // testFetchPendingInvoices tests that we can fetch all pending invoices from
@@ -1158,7 +1238,11 @@ func testFetchPendingInvoices(t *testing.T,
require.Empty(t, pending) require.Empty(t, pending)
const numInvoices = 20 const numInvoices = 20
var settleIndex uint64 = 1 var (
settleIndex uint64 = 1
htlcID uint64 = 0
)
pendingInvoices := make(map[lntypes.Hash]invpkg.Invoice) pendingInvoices := make(map[lntypes.Hash]invpkg.Invoice)
for i := 1; i <= numInvoices; i++ { for i := 1; i <= numInvoices; i++ {
@@ -1182,11 +1266,14 @@ func testFetchPendingInvoices(t *testing.T,
} }
ref := invpkg.InvoiceRefByHash(paymentHash) ref := invpkg.InvoiceRefByHash(paymentHash)
_, err = db.UpdateInvoice(ctxb, ref, nil, getUpdateInvoice(amt)) _, err = db.UpdateInvoice(
ctxb, ref, nil, getUpdateInvoice(htlcID, amt),
)
require.NoError(t, err) require.NoError(t, err)
settleTestInvoice(invoice, settleIndex) settleTestInvoice(invoice, htlcID, settleIndex)
settleIndex++ settleIndex++
htlcID++
} }
// Fetch all pending invoices. // Fetch all pending invoices.
@@ -1219,7 +1306,7 @@ func testDuplicateSettleInvoice(t *testing.T,
// With the invoice in the DB, we'll now attempt to settle the invoice. // With the invoice in the DB, we'll now attempt to settle the invoice.
ref := invpkg.InvoiceRefByHash(payHash) ref := invpkg.InvoiceRefByHash(payHash)
dbInvoice, err := db.UpdateInvoice( dbInvoice, err := db.UpdateInvoice(
ctxb, ref, nil, getUpdateInvoice(amt), ctxb, ref, nil, getUpdateInvoice(0, amt),
) )
require.NoError(t, err, "unable to settle invoice") require.NoError(t, err, "unable to settle invoice")
@@ -1244,7 +1331,9 @@ func testDuplicateSettleInvoice(t *testing.T,
// If we try to settle the invoice again, then we should get the very // If we try to settle the invoice again, then we should get the very
// same invoice back, but with an error this time. // same invoice back, but with an error this time.
dbInvoice, err = db.UpdateInvoice(ctxb, ref, nil, getUpdateInvoice(amt)) dbInvoice, err = db.UpdateInvoice(
ctxb, ref, nil, getUpdateInvoice(1, amt),
)
require.ErrorIs(t, err, invpkg.ErrInvoiceAlreadySettled) require.ErrorIs(t, err, invpkg.ErrInvoiceAlreadySettled)
if dbInvoice == nil { if dbInvoice == nil {
@@ -1269,9 +1358,12 @@ func testQueryInvoices(t *testing.T,
// assume that the index of the invoice within the database is the same // assume that the index of the invoice within the database is the same
// as the amount of the invoice itself. // as the amount of the invoice itself.
const numInvoices = 50 const numInvoices = 50
var settleIndex uint64 = 1 var (
var invoices []invpkg.Invoice settleIndex uint64 = 1
var pendingInvoices []invpkg.Invoice htlcID uint64 = 0
invoices []invpkg.Invoice
pendingInvoices []invpkg.Invoice
)
ctxb := context.Background() ctxb := context.Background()
for i := 1; i <= numInvoices; i++ { for i := 1; i <= numInvoices; i++ {
@@ -1296,15 +1388,16 @@ func testQueryInvoices(t *testing.T,
if i%2 == 0 { if i%2 == 0 {
ref := invpkg.InvoiceRefByHash(paymentHash) ref := invpkg.InvoiceRefByHash(paymentHash)
_, err := db.UpdateInvoice( _, err := db.UpdateInvoice(
ctxb, ref, nil, getUpdateInvoice(amt), ctxb, ref, nil, getUpdateInvoice(htlcID, amt),
) )
if err != nil { if err != nil {
t.Fatalf("unable to settle invoice: %v", err) t.Fatalf("unable to settle invoice: %v", err)
} }
// Create the settled invoice for the expectation set. // Create the settled invoice for the expectation set.
settleTestInvoice(invoice, settleIndex) settleTestInvoice(invoice, htlcID, settleIndex)
settleIndex++ settleIndex++
htlcID++
} else { } else {
pendingInvoices = append(pendingInvoices, *invoice) pendingInvoices = append(pendingInvoices, *invoice)
} }
@@ -1649,7 +1742,8 @@ func testQueryInvoices(t *testing.T,
t.Fatalf("unable to query invoice database: %v", err) t.Fatalf("unable to query invoice database: %v", err)
} }
require.Equal(t, len(testCase.expected), len(response.Invoices)) require.Equal(t, len(testCase.expected), len(response.Invoices),
fmt.Sprintf("test: #%v", i))
for j, expected := range testCase.expected { for j, expected := range testCase.expected {
require.Equal(t, require.Equal(t,
@@ -1662,7 +1756,9 @@ func testQueryInvoices(t *testing.T,
// getUpdateInvoice returns an invoice update callback that, when called, // getUpdateInvoice returns an invoice update callback that, when called,
// settles the invoice with the given amount. // settles the invoice with the given amount.
func getUpdateInvoice(amt lnwire.MilliSatoshi) invpkg.InvoiceUpdateCallback { func getUpdateInvoice(htlcID uint64,
amt lnwire.MilliSatoshi) invpkg.InvoiceUpdateCallback {
return func(invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, return func(invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc,
error) { error) {
@@ -1672,7 +1768,7 @@ func getUpdateInvoice(amt lnwire.MilliSatoshi) invpkg.InvoiceUpdateCallback {
noRecords := make(record.CustomSet) noRecords := make(record.CustomSet)
htlcs := map[models.CircuitKey]*invpkg.HtlcAcceptDesc{ htlcs := map[models.CircuitKey]*invpkg.HtlcAcceptDesc{
{}: { {HtlcID: htlcID}: {
Amt: amt, Amt: amt,
CustomRecords: noRecords, CustomRecords: noRecords,
}, },
@@ -2082,7 +2178,10 @@ func testSetIDIndex(t *testing.T, makeDB func(t *testing.T) invpkg.InvoiceDB) {
) )
_, err = db.UpdateInvoice( _, err = db.UpdateInvoice(
ctxb, ref2, (*invpkg.SetID)(setID), ctxb, ref2, (*invpkg.SetID)(setID),
updateAcceptAMPHtlc(0, amt, setID, true), // Note: we need a unique HTLC ID here otherwise the update will
// be rejected as a duplicate (due to SQL unique constraint
// violation).
updateAcceptAMPHtlc(55, amt, setID, true),
) )
require.Equal(t, invpkg.ErrDuplicateSetID{SetID: *setID}, err) require.Equal(t, invpkg.ErrDuplicateSetID{SetID: *setID}, err)
@@ -2366,7 +2465,7 @@ func testUnexpectedInvoicePreimage(t *testing.T,
// payment hash against which to validate the preimage. // payment hash against which to validate the preimage.
_, err = db.UpdateInvoice( _, err = db.UpdateInvoice(
ctxb, invpkg.InvoiceRefByAddr(invoice.Terms.PaymentAddr), nil, ctxb, invpkg.InvoiceRefByAddr(invoice.Terms.PaymentAddr), nil,
getUpdateInvoice(invoice.Terms.Value), getUpdateInvoice(0, invoice.Terms.Value),
) )
// Assert that we get ErrUnexpectedInvoicePreimage. // Assert that we get ErrUnexpectedInvoicePreimage.
@@ -2501,7 +2600,9 @@ func testDeleteInvoices(t *testing.T,
if i == 1 { if i == 1 {
invoice, err = db.UpdateInvoice( invoice, err = db.UpdateInvoice(
ctxb, invpkg.InvoiceRefByHash(paymentHash), nil, ctxb, invpkg.InvoiceRefByHash(paymentHash), nil,
getUpdateInvoice(invoice.Terms.Value), getUpdateInvoice(
uint64(i), invoice.Terms.Value,
),
) )
require.NoError(t, err, "unable to settle invoice") require.NoError(t, err, "unable to settle invoice")
} }
+1 -1
View File
@@ -231,7 +231,7 @@ func timeout() func() {
go func() { go func() {
select { select {
case <-time.After(5 * time.Second): case <-time.After(10 * time.Second):
err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
if err != nil { if err != nil {
panic(fmt.Sprintf("error writing to std out "+ panic(fmt.Sprintf("error writing to std out "+
+4 -1
View File
@@ -71,6 +71,9 @@ var (
dbBackendFlag = flag.String("dbbackend", "bbolt", "Database backend "+ dbBackendFlag = flag.String("dbbackend", "bbolt", "Database backend "+
"(bbolt, etcd, postgres)") "(bbolt, etcd, postgres)")
nativeSQLFlag = flag.Bool("nativesql", false, "Database backend to "+
"use native SQL when applicable (only for sqlite and postgres")
// lndExecutable is the full path to the lnd binary. // lndExecutable is the full path to the lnd binary.
lndExecutable = flag.String( lndExecutable = flag.String(
"lndexec", itestLndBinary, "full path to lnd binary", "lndexec", itestLndBinary, "full path to lnd binary",
@@ -95,7 +98,7 @@ func TestLightningNetworkDaemon(t *testing.T) {
// Get the binary path and setup the harness test. // Get the binary path and setup the harness test.
binary := getLndBinary(t) binary := getLndBinary(t)
harnessTest := lntest.SetupHarness( harnessTest := lntest.SetupHarness(
t, binary, *dbBackendFlag, feeService, t, binary, *dbBackendFlag, *nativeSQLFlag, feeService,
) )
defer harnessTest.Stop() defer harnessTest.Stop()
+93 -21
View File
@@ -3,6 +3,7 @@ package lncfg
import ( import (
"context" "context"
"fmt" "fmt"
"path"
"path/filepath" "path/filepath"
"time" "time"
@@ -14,6 +15,7 @@ import (
"github.com/lightningnetwork/lnd/kvdb/sqlite" "github.com/lightningnetwork/lnd/kvdb/sqlite"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/sqldb"
) )
const ( const (
@@ -28,6 +30,7 @@ const (
SqliteChainDBName = "chain.sqlite" SqliteChainDBName = "chain.sqlite"
SqliteNeutrinoDBName = "neutrino.sqlite" SqliteNeutrinoDBName = "neutrino.sqlite"
SqliteTowerDBName = "watchtower.sqlite" SqliteTowerDBName = "watchtower.sqlite"
SqliteNativeDBName = "lnd.sqlite"
BoltBackend = "bolt" BoltBackend = "bolt"
EtcdBackend = "etcd" EtcdBackend = "etcd"
@@ -78,9 +81,11 @@ type DB struct {
Bolt *kvdb.BoltConfig `group:"bolt" namespace:"bolt" description:"Bolt settings."` Bolt *kvdb.BoltConfig `group:"bolt" namespace:"bolt" description:"Bolt settings."`
Postgres *postgres.Config `group:"postgres" namespace:"postgres" description:"Postgres settings."` Postgres *sqldb.PostgresConfig `group:"postgres" namespace:"postgres" description:"Postgres settings."`
Sqlite *sqlite.Config `group:"sqlite" namespace:"sqlite" description:"Sqlite settings."` Sqlite *sqldb.SqliteConfig `group:"sqlite" namespace:"sqlite" description:"Sqlite settings."`
UseNativeSQL bool `long:"use-native-sql" description:"Use native SQL for tables that already support it."`
NoGraphCache bool `long:"no-graph-cache" description:"Don't use the in-memory graph cache for path finding. Much slower but uses less RAM. Can only be used with a bolt database backend."` NoGraphCache bool `long:"no-graph-cache" description:"Don't use the in-memory graph cache for path finding. Much slower but uses less RAM. Can only be used with a bolt database backend."`
@@ -103,13 +108,14 @@ func DefaultDB() *DB {
// Allow at most 32 MiB messages by default. // Allow at most 32 MiB messages by default.
MaxMsgSize: 32768 * 1024, MaxMsgSize: 32768 * 1024,
}, },
Postgres: &postgres.Config{ Postgres: &sqldb.PostgresConfig{
MaxConnections: defaultPostgresMaxConnections, MaxConnections: defaultPostgresMaxConnections,
}, },
Sqlite: &sqlite.Config{ Sqlite: &sqldb.SqliteConfig{
MaxConnections: defaultSqliteMaxConnections, MaxConnections: defaultSqliteMaxConnections,
BusyTimeout: defaultSqliteBusyTimeout, BusyTimeout: defaultSqliteBusyTimeout,
}, },
UseNativeSQL: false,
} }
} }
@@ -118,8 +124,8 @@ func (db *DB) Validate() error {
switch db.Backend { switch db.Backend {
case BoltBackend, SqliteBackend: case BoltBackend, SqliteBackend:
case PostgresBackend: case PostgresBackend:
if db.Postgres.Dsn == "" { if err := db.Postgres.Validate(); err != nil {
return fmt.Errorf("postgres dsn must be set") return err
} }
case EtcdBackend: case EtcdBackend:
@@ -214,6 +220,11 @@ type DatabaseBackends struct {
// the underlying wallet database from. // the underlying wallet database from.
WalletDB btcwallet.LoaderOption WalletDB btcwallet.LoaderOption
// NativeSQLStore is a pointer to a native SQL store that can be used
// for native SQL queries for tables that already support it. This may
// be nil if the use-native-sql flag was not set.
NativeSQLStore *sqldb.BaseDB
// Remote indicates whether the database backends are remote, possibly // Remote indicates whether the database backends are remote, possibly
// replicated instances or local bbolt or sqlite backed databases. // replicated instances or local bbolt or sqlite backed databases.
Remote bool Remote bool
@@ -223,6 +234,26 @@ type DatabaseBackends struct {
CloseFuncs map[string]func() error CloseFuncs map[string]func() error
} }
// GetPostgresConfigKVDB converts a sqldb.PostgresConfig to a kvdb
// postgres.Config.
func GetPostgresConfigKVDB(cfg *sqldb.PostgresConfig) *postgres.Config {
return &postgres.Config{
Dsn: cfg.Dsn,
Timeout: cfg.Timeout,
MaxConnections: cfg.MaxConnections,
}
}
// GetSqliteConfigKVDB converts a sqldb.SqliteConfig to a kvdb sqlite.Config.
func GetSqliteConfigKVDB(cfg *sqldb.SqliteConfig) *sqlite.Config {
return &sqlite.Config{
Timeout: cfg.Timeout,
BusyTimeout: cfg.BusyTimeout,
MaxConnections: cfg.MaxConnections,
PragmaOptions: cfg.PragmaOptions,
}
}
// GetBackends returns a set of kvdb.Backends as set in the DB config. // GetBackends returns a set of kvdb.Backends as set in the DB config.
func (db *DB) GetBackends(ctx context.Context, chanDBPath, func (db *DB) GetBackends(ctx context.Context, chanDBPath,
walletDBPath, towerServerDBPath string, towerClientEnabled, walletDBPath, towerServerDBPath string, towerClientEnabled,
@@ -342,9 +373,14 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
}, nil }, nil
case PostgresBackend: case PostgresBackend:
// Convert the sqldb PostgresConfig to a kvdb postgres.Config.
// This is a temporary measure until we migrate all kvdb SQL
// users to native SQL.
postgresConfig := GetPostgresConfigKVDB(db.Postgres)
postgresBackend, err := kvdb.Open( postgresBackend, err := kvdb.Open(
kvdb.PostgresBackendName, ctx, kvdb.PostgresBackendName, ctx,
db.Postgres, NSChannelDB, postgresConfig, NSChannelDB,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("error opening postgres graph "+ return nil, fmt.Errorf("error opening postgres graph "+
@@ -354,7 +390,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
postgresMacaroonBackend, err := kvdb.Open( postgresMacaroonBackend, err := kvdb.Open(
kvdb.PostgresBackendName, ctx, kvdb.PostgresBackendName, ctx,
db.Postgres, NSMacaroonDB, postgresConfig, NSMacaroonDB,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("error opening postgres "+ return nil, fmt.Errorf("error opening postgres "+
@@ -364,7 +400,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
postgresDecayedLogBackend, err := kvdb.Open( postgresDecayedLogBackend, err := kvdb.Open(
kvdb.PostgresBackendName, ctx, kvdb.PostgresBackendName, ctx,
db.Postgres, NSDecayedLogDB, postgresConfig, NSDecayedLogDB,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("error opening postgres "+ return nil, fmt.Errorf("error opening postgres "+
@@ -374,7 +410,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
postgresTowerClientBackend, err := kvdb.Open( postgresTowerClientBackend, err := kvdb.Open(
kvdb.PostgresBackendName, ctx, kvdb.PostgresBackendName, ctx,
db.Postgres, NSTowerClientDB, postgresConfig, NSTowerClientDB,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("error opening postgres tower "+ return nil, fmt.Errorf("error opening postgres tower "+
@@ -384,7 +420,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
postgresTowerServerBackend, err := kvdb.Open( postgresTowerServerBackend, err := kvdb.Open(
kvdb.PostgresBackendName, ctx, kvdb.PostgresBackendName, ctx,
db.Postgres, NSTowerServerDB, postgresConfig, NSTowerServerDB,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("error opening postgres tower "+ return nil, fmt.Errorf("error opening postgres tower "+
@@ -394,7 +430,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
postgresWalletBackend, err := kvdb.Open( postgresWalletBackend, err := kvdb.Open(
kvdb.PostgresBackendName, ctx, kvdb.PostgresBackendName, ctx,
db.Postgres, NSWalletDB, postgresConfig, NSWalletDB,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("error opening postgres macaroon "+ return nil, fmt.Errorf("error opening postgres macaroon "+
@@ -402,6 +438,20 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
} }
closeFuncs[NSWalletDB] = postgresWalletBackend.Close closeFuncs[NSWalletDB] = postgresWalletBackend.Close
var nativeSQLStore *sqldb.BaseDB
if db.UseNativeSQL {
nativePostgresStore, err := sqldb.NewPostgresStore(
db.Postgres,
)
if err != nil {
return nil, fmt.Errorf("error opening "+
"native postgres store: %v", err)
}
nativeSQLStore = nativePostgresStore.BaseDB
closeFuncs[PostgresBackend] = nativePostgresStore.Close
}
// Warn if the user is trying to switch over to a Postgres DB // Warn if the user is trying to switch over to a Postgres DB
// while there is a wallet or channel bbolt DB still present. // while there is a wallet or channel bbolt DB still present.
warnExistingBoltDBs( warnExistingBoltDBs(
@@ -429,11 +479,17 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
WalletDB: btcwallet.LoaderWithExternalWalletDB( WalletDB: btcwallet.LoaderWithExternalWalletDB(
postgresWalletBackend, postgresWalletBackend,
), ),
Remote: true, NativeSQLStore: nativeSQLStore,
CloseFuncs: closeFuncs, Remote: true,
CloseFuncs: closeFuncs,
}, nil }, nil
case SqliteBackend: case SqliteBackend:
// Convert the sqldb SqliteConfig to a kvdb sqlite.Config.
// This is a temporary measure until we migrate all kvdb SQL
// users to native SQL.
sqliteConfig := GetSqliteConfigKVDB(db.Sqlite)
// Note that for sqlite, we put kv tables for the channel.db, // Note that for sqlite, we put kv tables for the channel.db,
// wtclient.db and sphinxreplay.db all in the channel.sqlite db. // wtclient.db and sphinxreplay.db all in the channel.sqlite db.
// The tables for wallet.db and macaroon.db are in the // The tables for wallet.db and macaroon.db are in the
@@ -445,7 +501,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
// would cause deadlocks in the code due to the wallet db often // would cause deadlocks in the code due to the wallet db often
// being accessed during a write to another db. // being accessed during a write to another db.
sqliteBackend, err := kvdb.Open( sqliteBackend, err := kvdb.Open(
kvdb.SqliteBackendName, ctx, db.Sqlite, chanDBPath, kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
SqliteChannelDBName, NSChannelDB, SqliteChannelDBName, NSChannelDB,
) )
if err != nil { if err != nil {
@@ -455,7 +511,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
closeFuncs[NSChannelDB] = sqliteBackend.Close closeFuncs[NSChannelDB] = sqliteBackend.Close
sqliteMacaroonBackend, err := kvdb.Open( sqliteMacaroonBackend, err := kvdb.Open(
kvdb.SqliteBackendName, ctx, db.Sqlite, walletDBPath, kvdb.SqliteBackendName, ctx, sqliteConfig, walletDBPath,
SqliteChainDBName, NSMacaroonDB, SqliteChainDBName, NSMacaroonDB,
) )
if err != nil { if err != nil {
@@ -465,7 +521,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
closeFuncs[NSMacaroonDB] = sqliteMacaroonBackend.Close closeFuncs[NSMacaroonDB] = sqliteMacaroonBackend.Close
sqliteDecayedLogBackend, err := kvdb.Open( sqliteDecayedLogBackend, err := kvdb.Open(
kvdb.SqliteBackendName, ctx, db.Sqlite, chanDBPath, kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
SqliteChannelDBName, NSDecayedLogDB, SqliteChannelDBName, NSDecayedLogDB,
) )
if err != nil { if err != nil {
@@ -475,7 +531,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
closeFuncs[NSDecayedLogDB] = sqliteDecayedLogBackend.Close closeFuncs[NSDecayedLogDB] = sqliteDecayedLogBackend.Close
sqliteTowerClientBackend, err := kvdb.Open( sqliteTowerClientBackend, err := kvdb.Open(
kvdb.SqliteBackendName, ctx, db.Sqlite, chanDBPath, kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
SqliteChannelDBName, NSTowerClientDB, SqliteChannelDBName, NSTowerClientDB,
) )
if err != nil { if err != nil {
@@ -485,7 +541,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
closeFuncs[NSTowerClientDB] = sqliteTowerClientBackend.Close closeFuncs[NSTowerClientDB] = sqliteTowerClientBackend.Close
sqliteTowerServerBackend, err := kvdb.Open( sqliteTowerServerBackend, err := kvdb.Open(
kvdb.SqliteBackendName, ctx, db.Sqlite, kvdb.SqliteBackendName, ctx, sqliteConfig,
towerServerDBPath, SqliteTowerDBName, NSTowerServerDB, towerServerDBPath, SqliteTowerDBName, NSTowerServerDB,
) )
if err != nil { if err != nil {
@@ -495,7 +551,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
closeFuncs[NSTowerServerDB] = sqliteTowerServerBackend.Close closeFuncs[NSTowerServerDB] = sqliteTowerServerBackend.Close
sqliteWalletBackend, err := kvdb.Open( sqliteWalletBackend, err := kvdb.Open(
kvdb.SqliteBackendName, ctx, db.Sqlite, walletDBPath, kvdb.SqliteBackendName, ctx, sqliteConfig, walletDBPath,
SqliteChainDBName, NSWalletDB, SqliteChainDBName, NSWalletDB,
) )
if err != nil { if err != nil {
@@ -504,6 +560,21 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
} }
closeFuncs[NSWalletDB] = sqliteWalletBackend.Close closeFuncs[NSWalletDB] = sqliteWalletBackend.Close
var nativeSQLStore *sqldb.BaseDB
if db.UseNativeSQL {
nativeSQLiteStore, err := sqldb.NewSqliteStore(
db.Sqlite,
path.Join(chanDBPath, SqliteNativeDBName),
)
if err != nil {
return nil, fmt.Errorf("error opening "+
"native SQLite store: %v", err)
}
nativeSQLStore = nativeSQLiteStore.BaseDB
closeFuncs[SqliteBackend] = nativeSQLiteStore.Close
}
// Warn if the user is trying to switch over to a sqlite DB // Warn if the user is trying to switch over to a sqlite DB
// while there is a wallet or channel bbolt DB still present. // while there is a wallet or channel bbolt DB still present.
warnExistingBoltDBs( warnExistingBoltDBs(
@@ -531,7 +602,8 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
WalletDB: btcwallet.LoaderWithExternalWalletDB( WalletDB: btcwallet.LoaderWithExternalWalletDB(
sqliteWalletBackend, sqliteWalletBackend,
), ),
CloseFuncs: closeFuncs, NativeSQLStore: nativeSQLStore,
CloseFuncs: closeFuncs,
}, nil }, nil
} }
+2 -2
View File
@@ -103,14 +103,14 @@ type HarnessTest struct {
// NewHarnessTest creates a new instance of a harnessTest from a regular // NewHarnessTest creates a new instance of a harnessTest from a regular
// testing.T instance. // testing.T instance.
func NewHarnessTest(t *testing.T, lndBinary string, feeService WebFeeService, func NewHarnessTest(t *testing.T, lndBinary string, feeService WebFeeService,
dbBackend node.DatabaseBackend) *HarnessTest { dbBackend node.DatabaseBackend, nativeSQL bool) *HarnessTest {
t.Helper() t.Helper()
// Create the run context. // Create the run context.
ctxt, cancel := context.WithCancel(context.Background()) ctxt, cancel := context.WithCancel(context.Background())
manager := newNodeManager(lndBinary, dbBackend) manager := newNodeManager(lndBinary, dbBackend, nativeSQL)
return &HarnessTest{ return &HarnessTest{
T: t, T: t,
+8 -2
View File
@@ -31,6 +31,10 @@ type nodeManager struct {
// dbBackend sets the database backend to use. // dbBackend sets the database backend to use.
dbBackend node.DatabaseBackend dbBackend node.DatabaseBackend
// nativeSQL sets the database backend to use native SQL when
// applicable.
nativeSQL bool
// activeNodes is a map of all running nodes, format: // activeNodes is a map of all running nodes, format:
// {pubkey: *HarnessNode}. // {pubkey: *HarnessNode}.
activeNodes map[uint32]*node.HarnessNode activeNodes map[uint32]*node.HarnessNode
@@ -48,12 +52,13 @@ type nodeManager struct {
} }
// newNodeManager creates a new node manager instance. // newNodeManager creates a new node manager instance.
func newNodeManager(lndBinary string, func newNodeManager(lndBinary string, dbBackend node.DatabaseBackend,
dbBackend node.DatabaseBackend) *nodeManager { nativeSQL bool) *nodeManager {
return &nodeManager{ return &nodeManager{
lndBinary: lndBinary, lndBinary: lndBinary,
dbBackend: dbBackend, dbBackend: dbBackend,
nativeSQL: nativeSQL,
activeNodes: make(map[uint32]*node.HarnessNode), activeNodes: make(map[uint32]*node.HarnessNode),
standbyNodes: make(map[uint32]*node.HarnessNode), standbyNodes: make(map[uint32]*node.HarnessNode),
} }
@@ -80,6 +85,7 @@ func (nm *nodeManager) newNode(t *testing.T, name string, extraArgs []string,
ExtraArgs: extraArgs, ExtraArgs: extraArgs,
FeeURL: nm.feeServiceURL, FeeURL: nm.feeServiceURL,
DBBackend: nm.dbBackend, DBBackend: nm.dbBackend,
NativeSQL: nm.nativeSQL,
NodeID: nm.nextNodeID(), NodeID: nm.nextNodeID(),
LndBinary: nm.lndBinary, LndBinary: nm.lndBinary,
NetParams: harnessNetParams, NetParams: harnessNetParams,
+2 -2
View File
@@ -18,7 +18,7 @@ import (
// 4. connect the miner and the chain backend. // 4. connect the miner and the chain backend.
// 5. start the HarnessTest. // 5. start the HarnessTest.
func SetupHarness(t *testing.T, binaryPath, dbBackendName string, func SetupHarness(t *testing.T, binaryPath, dbBackendName string,
feeService WebFeeService) *HarnessTest { nativeSQL bool, feeService WebFeeService) *HarnessTest {
t.Log("Setting up HarnessTest...") t.Log("Setting up HarnessTest...")
@@ -30,7 +30,7 @@ func SetupHarness(t *testing.T, binaryPath, dbBackendName string,
dbBackend := prepareDBBackend(t, dbBackendName) dbBackend := prepareDBBackend(t, dbBackendName)
// Create a new HarnessTest. // Create a new HarnessTest.
ht := NewHarnessTest(t, binaryPath, feeService, dbBackend) ht := NewHarnessTest(t, binaryPath, feeService, dbBackend, nativeSQL)
// Init the miner. // Init the miner.
t.Log("Prepare the miner and mine blocks to activate segwit...") t.Log("Prepare the miner and mine blocks to activate segwit...")
+7
View File
@@ -119,6 +119,7 @@ type BaseNodeConfig struct {
DBBackend DatabaseBackend DBBackend DatabaseBackend
PostgresDsn string PostgresDsn string
NativeSQL bool
// NodeID is a unique ID used to identify the node. // NodeID is a unique ID used to identify the node.
NodeID uint32 NodeID uint32
@@ -277,11 +278,17 @@ func (cfg *BaseNodeConfig) GenArgs() []string {
case BackendPostgres: case BackendPostgres:
args = append(args, "--db.backend=postgres") args = append(args, "--db.backend=postgres")
args = append(args, "--db.postgres.dsn="+cfg.PostgresDsn) args = append(args, "--db.postgres.dsn="+cfg.PostgresDsn)
if cfg.NativeSQL {
args = append(args, "--db.use-native-sql")
}
case BackendSqlite: case BackendSqlite:
args = append(args, "--db.backend=sqlite") args = append(args, "--db.backend=sqlite")
args = append(args, fmt.Sprintf("--db.sqlite.busytimeout=%v", args = append(args, fmt.Sprintf("--db.sqlite.busytimeout=%v",
wait.SqliteBusyTimeout)) wait.SqliteBusyTimeout))
if cfg.NativeSQL {
args = append(args, "--db.use-native-sql")
}
} }
if cfg.FeeURL != "" { if cfg.FeeURL != "" {
+6
View File
@@ -60,10 +60,16 @@ DEV_TAGS += kvdb_etcd
endif endif
ifeq ($(dbbackend),postgres) ifeq ($(dbbackend),postgres)
ifneq ($(nativesql),)
ITEST_FLAGS += -nativesql
endif
DEV_TAGS += kvdb_postgres DEV_TAGS += kvdb_postgres
endif endif
ifeq ($(dbbackend),sqlite) ifeq ($(dbbackend),sqlite)
ifneq ($(nativesql),)
ITEST_FLAGS += -nativesql
endif
DEV_TAGS += kvdb_sqlite DEV_TAGS += kvdb_sqlite
endif endif
+1 -1
View File
@@ -5806,7 +5806,7 @@ func (r *rpcServer) ListInvoices(ctx context.Context,
CreationDateEnd: int64(req.CreationDateEnd), CreationDateEnd: int64(req.CreationDateEnd),
} }
invoiceSlice, err := r.server.miscDB.QueryInvoices(ctx, q) invoiceSlice, err := r.server.invoicesDB.QueryInvoices(ctx, q)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to query invoices: %w", err) return nil, fmt.Errorf("unable to query invoices: %w", err)
} }
+10
View File
@@ -1309,6 +1309,11 @@
; the future. ; the future.
; db.no-rev-log-amt-data=false ; db.no-rev-log-amt-data=false
; If set to true, native SQL will be used instead of KV emulation for tables
; that support it already. Note: this is an experimental feature, use at your
; own risk.
; db.use-native-sql=false
[etcd] [etcd]
@@ -1404,6 +1409,9 @@
; Example: ; Example:
; db.postgres.maxconnections= ; db.postgres.maxconnections=
; Whether to skip executing schema migrations.
; db.postgres.skipmigrations=false
[sqlite] [sqlite]
@@ -1428,6 +1436,8 @@
; db.sqlite.pragmaoptions=auto_vacuum=incremental ; db.sqlite.pragmaoptions=auto_vacuum=incremental
; db.sqlite.pragmaoptions=temp_store=MEMORY ; db.sqlite.pragmaoptions=temp_store=MEMORY
; Whether to skip executing schema migrations.
; db.sqlite.skipmigrations=false
[bolt] [bolt]
+1 -1
View File
@@ -16,4 +16,4 @@ docker run \
-e UID=$UID \ -e UID=$UID \
-v "$DIR/../:/build" \ -v "$DIR/../:/build" \
-w /build \ -w /build \
kjconroy/sqlc:1.18.0 generate sqlc/sqlc:1.25.0 generate
+3
View File
@@ -246,6 +246,8 @@ type server struct {
// channel DB that haven't been separated out yet. // channel DB that haven't been separated out yet.
miscDB *channeldb.DB miscDB *channeldb.DB
invoicesDB invoices.InvoiceDB
aliasMgr *aliasmgr.Manager aliasMgr *aliasmgr.Manager
htlcSwitch *htlcswitch.Switch htlcSwitch *htlcswitch.Switch
@@ -564,6 +566,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
chanStateDB: dbs.ChanStateDB.ChannelStateDB(), chanStateDB: dbs.ChanStateDB.ChannelStateDB(),
addrSource: dbs.ChanStateDB, addrSource: dbs.ChanStateDB,
miscDB: dbs.ChanStateDB, miscDB: dbs.ChanStateDB,
invoicesDB: dbs.InvoiceDB,
cc: cc, cc: cc,
sigPool: lnwallet.NewSigPool(cfg.Workers.Sig, cc.Signer), sigPool: lnwallet.NewSigPool(cfg.Workers.Sig, cc.Signer),
writePool: writePool, writePool: writePool,
+55
View File
@@ -0,0 +1,55 @@
package sqldb
import (
"fmt"
"net/url"
"time"
)
const (
// defaultMaxConns is the number of permitted active and idle
// connections. We want to limit this so it isn't unlimited. We use the
// same value for the number of idle connections as, this can speed up
// queries given a new connection doesn't need to be established each
// time.
defaultMaxConns = 25
// connIdleLifetime is the amount of time a connection can be idle.
connIdleLifetime = 5 * time.Minute
)
// SqliteConfig holds all the config arguments needed to interact with our
// sqlite DB.
//
//nolint:lll
type SqliteConfig struct {
Timeout time.Duration `long:"timeout" description:"The time after which a database query should be timed out."`
BusyTimeout time.Duration `long:"busytimeout" description:"The maximum amount of time to wait for a database connection to become available for a query."`
MaxConnections int `long:"maxconnections" description:"The maximum number of open connections to the database. Set to zero for unlimited."`
PragmaOptions []string `long:"pragmaoptions" description:"A list of pragma options to set on a database connection. For example, 'auto_vacuum=incremental'. Note that the flag must be specified multiple times if multiple options are to be set."`
SkipMigrations bool `long:"skipmigrations" description:"Skip applying migrations on startup."`
}
// PostgresConfig holds the postgres database configuration.
//
//nolint:lll
type PostgresConfig struct {
Dsn string `long:"dsn" description:"Database connection string."`
Timeout time.Duration `long:"timeout" description:"Database connection timeout. Set to zero to disable."`
MaxConnections int `long:"maxconnections" description:"The maximum number of open connections to the database. Set to zero for unlimited."`
SkipMigrations bool `long:"skipmigrations" description:"Skip applying migrations on startup."`
}
func (p *PostgresConfig) Validate() error {
if p.Dsn == "" {
return fmt.Errorf("DSN is required")
}
// Parse the DSN as a URL.
_, err := url.Parse(p.Dsn)
if err != nil {
return fmt.Errorf("invalid DSN: %w", err)
}
return nil
}
+1658
View File
File diff suppressed because it is too large. Load diff
+18
View File
@@ -0,0 +1,18 @@
//go:build js || (windows && (arm || 386)) || (linux && (ppc64 || mips || mipsle || mips64))
package sqldb
import "fmt"
// SqliteStore is a database store implementation that uses a sqlite backend.
type SqliteStore struct {
cfg *SqliteConfig
*BaseDB
}
// NewSqliteStore attempts to open a new sqlite database based on the passed
// config.
func NewSqliteStore(cfg *SqliteConfig, dbPath string) (*SqliteStore, error) {
return nil, fmt.Errorf("SQLite backend not supported in WebAssembly")
}
+54 -56
View File
@@ -2,18 +2,14 @@ package sqldb
import ( import (
"database/sql" "database/sql"
"fmt" "net/url"
"testing" "path"
"strings"
"time" "time"
postgres_migrate "github.com/golang-migrate/migrate/v4/database/postgres" postgres_migrate "github.com/golang-migrate/migrate/v4/database/postgres"
_ "github.com/golang-migrate/migrate/v4/source/file" // Read migrations from files. // nolint:lll _ "github.com/golang-migrate/migrate/v4/source/file" // Read migrations from files. // nolint:lll
"github.com/lightningnetwork/lnd/sqldb/sqlc" "github.com/lightningnetwork/lnd/sqldb/sqlc"
"github.com/stretchr/testify/require"
)
const (
dsnTemplate = "postgres://%v:%v@%v:%d/%v?sslmode=%v"
) )
var ( var (
@@ -25,35 +21,45 @@ var (
DefaultPostgresFixtureLifetime = 10 * time.Minute DefaultPostgresFixtureLifetime = 10 * time.Minute
) )
// PostgresConfig holds the postgres database configuration. // replacePasswordInDSN takes a DSN string and returns it with the password
// // replaced by "***".
//nolint:lll func replacePasswordInDSN(dsn string) (string, error) {
type PostgresConfig struct { // Parse the DSN as a URL
SkipMigrations bool `long:"skipmigrations" description:"Skip applying migrations on startup."` u, err := url.Parse(dsn)
Host string `long:"host" description:"Database server hostname."` if err != nil {
Port int `long:"port" description:"Database server port."` return "", err
User string `long:"user" description:"Database user."` }
Password string `long:"password" description:"Database user's password."`
DBName string `long:"dbname" description:"Database name to use."` // Check if the URL has a user info part
MaxOpenConnections int `long:"maxconnections" description:"Max open connections to keep alive to the database server."` if u.User != nil {
RequireSSL bool `long:"requiressl" description:"Whether to require using SSL (mode: require) when connecting to the server."` username := u.User.Username()
// Reconstruct user info with "***" as password
userInfo := username + ":***@"
// Rebuild the DSN with the modified user info
sanitizeDSN := strings.Replace(
dsn, u.User.String()+"@", userInfo, 1,
)
return sanitizeDSN, nil
}
// Return the original DSN if no user info is present
return dsn, nil
} }
// DSN returns the dns to connect to the database. // getDatabaseNameFromDSN extracts the database name from a DSN string.
func (s *PostgresConfig) DSN(hidePassword bool) string { func getDatabaseNameFromDSN(dsn string) (string, error) {
var sslMode = "disable" // Parse the DSN as a URL
if s.RequireSSL { u, err := url.Parse(dsn)
sslMode = "require" if err != nil {
return "", err
} }
password := s.Password // The database name is the last segment of the path. Trim leading slash
if hidePassword { // and return the last segment.
// Placeholder used for logging the DSN safely. return path.Base(u.Path), nil
password = "****"
}
return fmt.Sprintf(dsnTemplate, s.User, password, s.Host, s.Port,
s.DBName, sslMode)
} }
// PostgresStore is a database store implementation that uses a Postgres // PostgresStore is a database store implementation that uses a Postgres
@@ -67,16 +73,25 @@ type PostgresStore struct {
// NewPostgresStore creates a new store that is backed by a Postgres database // NewPostgresStore creates a new store that is backed by a Postgres database
// backend. // backend.
func NewPostgresStore(cfg *PostgresConfig) (*PostgresStore, error) { func NewPostgresStore(cfg *PostgresConfig) (*PostgresStore, error) {
log.Infof("Using SQL database '%s'", cfg.DSN(true)) sanitizedDSN, err := replacePasswordInDSN(cfg.Dsn)
if err != nil {
return nil, err
}
log.Infof("Using SQL database '%s'", sanitizedDSN)
rawDB, err := sql.Open("pgx", cfg.DSN(false)) dbName, err := getDatabaseNameFromDSN(cfg.Dsn)
if err != nil {
return nil, err
}
rawDB, err := sql.Open("pgx", cfg.Dsn)
if err != nil { if err != nil {
return nil, err return nil, err
} }
maxConns := defaultMaxConns maxConns := defaultMaxConns
if cfg.MaxOpenConnections > 0 { if cfg.MaxConnections > 0 {
maxConns = cfg.MaxOpenConnections maxConns = cfg.MaxConnections
} }
rawDB.SetMaxOpenConns(maxConns) rawDB.SetMaxOpenConns(maxConns)
@@ -89,7 +104,7 @@ func NewPostgresStore(cfg *PostgresConfig) (*PostgresStore, error) {
// system. // system.
// //
// First, we'll need to open up a new migration instance for // First, we'll need to open up a new migration instance for
// our current target database: sqlite. // our current target database: Postgres.
driver, err := postgres_migrate.WithInstance( driver, err := postgres_migrate.WithInstance(
rawDB, &postgres_migrate.Config{}, rawDB, &postgres_migrate.Config{},
) )
@@ -100,11 +115,12 @@ func NewPostgresStore(cfg *PostgresConfig) (*PostgresStore, error) {
postgresFS := newReplacerFS(sqlSchemas, map[string]string{ postgresFS := newReplacerFS(sqlSchemas, map[string]string{
"BLOB": "BYTEA", "BLOB": "BYTEA",
"INTEGER PRIMARY KEY": "SERIAL PRIMARY KEY", "INTEGER PRIMARY KEY": "SERIAL PRIMARY KEY",
"BIGINT PRIMARY KEY": "BIGSERIAL PRIMARY KEY",
"TIMESTAMP": "TIMESTAMP WITHOUT TIME ZONE", "TIMESTAMP": "TIMESTAMP WITHOUT TIME ZONE",
}) })
err = applyMigrations( err = applyMigrations(
postgresFS, driver, "sqlc/migrations", cfg.DBName, postgresFS, driver, "sqlc/migrations", dbName,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@@ -121,21 +137,3 @@ func NewPostgresStore(cfg *PostgresConfig) (*PostgresStore, error) {
}, },
}, nil }, nil
} }
// NewTestPostgresDB is a helper function that creates a Postgres database for
// testing.
func NewTestPostgresDB(t *testing.T) *PostgresStore {
t.Helper()
t.Logf("Creating new Postgres DB for testing")
sqlFixture := NewTestPgFixture(t, DefaultPostgresFixtureLifetime)
store, err := NewPostgresStore(sqlFixture.GetConfig())
require.NoError(t, err)
t.Cleanup(func() {
sqlFixture.TearDown(t)
})
return store
}
+37 -22
View File
@@ -1,8 +1,12 @@
//go:build !js && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64)) && !(netbsd || openbsd)
package sqldb package sqldb
import ( import (
"context" "context"
"crypto/rand"
"database/sql" "database/sql"
"encoding/hex"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
@@ -55,6 +59,7 @@ func NewTestPgFixture(t *testing.T, expiry time.Duration) *TestPgFixture {
"postgres", "postgres",
"-c", "log_statement=all", "-c", "log_statement=all",
"-c", "log_destination=stderr", "-c", "log_destination=stderr",
"-c", "max_connections=1000",
}, },
}, func(config *docker.HostConfig) { }, func(config *docker.HostConfig) {
// Set AutoRemove to true so that stopped container goes away // Set AutoRemove to true so that stopped container goes away
@@ -74,7 +79,7 @@ func NewTestPgFixture(t *testing.T, expiry time.Duration) *TestPgFixture {
host: host, host: host,
port: int(port), port: int(port),
} }
databaseURL := fixture.GetDSN() databaseURL := fixture.GetConfig(testPgDBName).Dsn
log.Infof("Connecting to Postgres fixture: %v\n", databaseURL) log.Infof("Connecting to Postgres fixture: %v\n", databaseURL)
// Tell docker to hard kill the container in "expiry" seconds. // Tell docker to hard kill the container in "expiry" seconds.
@@ -103,20 +108,13 @@ func NewTestPgFixture(t *testing.T, expiry time.Duration) *TestPgFixture {
return fixture return fixture
} }
// GetDSN returns the DSN (Data Source Name) for the started Postgres node.
func (f *TestPgFixture) GetDSN() string {
return f.GetConfig().DSN(false)
}
// GetConfig returns the full config of the Postgres node. // GetConfig returns the full config of the Postgres node.
func (f *TestPgFixture) GetConfig() *PostgresConfig { func (f *TestPgFixture) GetConfig(dbName string) *PostgresConfig {
return &PostgresConfig{ return &PostgresConfig{
Host: f.host, Dsn: fmt.Sprintf(
Port: f.port, "postgres://%v:%v@%v:%v/%v?sslmode=disable",
User: testPgUser, testPgUser, testPgPass, f.host, f.port, dbName,
Password: testPgPass, ),
DBName: testPgDBName,
RequireSSL: false,
} }
} }
@@ -126,15 +124,32 @@ func (f *TestPgFixture) TearDown(t *testing.T) {
require.NoError(t, err, "Could not purge resource") require.NoError(t, err, "Could not purge resource")
} }
// ClearDB clears the database. // NewTestPostgresDB is a helper function that creates a Postgres database for
func (f *TestPgFixture) ClearDB(t *testing.T) { // testing using the given fixture.
dbConn, err := sql.Open("postgres", f.GetDSN()) func NewTestPostgresDB(t *testing.T, fixture *TestPgFixture) *PostgresStore {
t.Helper()
// Create random database name.
randBytes := make([]byte, 8)
_, err := rand.Read(randBytes)
if err != nil {
t.Fatal(err)
}
dbName := "test_" + hex.EncodeToString(randBytes)
t.Logf("Creating new Postgres DB '%s' for testing", dbName)
_, err = fixture.db.ExecContext(
context.Background(), "CREATE DATABASE "+dbName,
)
if err != nil {
t.Fatal(err)
}
cfg := fixture.GetConfig(dbName)
store, err := NewPostgresStore(cfg)
require.NoError(t, err) require.NoError(t, err)
_, err = dbConn.ExecContext( return store
context.Background(),
`DROP SCHEMA IF EXISTS public CASCADE;
CREATE SCHEMA public;`,
)
require.NoError(t, err)
} }
+217 -203
View File
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.18.0 // sqlc v1.25.0
// source: amp_invoices.sql // source: amp_invoices.sql
package sqlc package sqlc
@@ -11,67 +11,69 @@ import (
"time" "time"
) )
const deleteAMPHTLCCustomRecords = `-- name: DeleteAMPHTLCCustomRecords :exec const fetchAMPSubInvoiceHTLCs = `-- name: FetchAMPSubInvoiceHTLCs :many
WITH htlc_ids AS ( SELECT
SELECT htlc_id amp.set_id, amp.root_share, amp.child_index, amp.hash, amp.preimage,
FROM amp_invoice_htlcs invoice_htlcs.id, invoice_htlcs.chan_id, invoice_htlcs.htlc_id, invoice_htlcs.amount_msat, invoice_htlcs.total_mpp_msat, invoice_htlcs.accept_height, invoice_htlcs.accept_time, invoice_htlcs.expiry_height, invoice_htlcs.state, invoice_htlcs.resolve_time, invoice_htlcs.invoice_id
WHERE invoice_id = $1 FROM amp_sub_invoice_htlcs amp
INNER JOIN invoice_htlcs ON amp.htlc_id = invoice_htlcs.id
WHERE amp.invoice_id = $1
AND (
set_id = $2 OR
$2 IS NULL
) )
DELETE
FROM invoice_htlc_custom_records
WHERE htlc_id IN (SELECT id FROM htlc_ids)
` `
func (q *Queries) DeleteAMPHTLCCustomRecords(ctx context.Context, invoiceID int32) error { type FetchAMPSubInvoiceHTLCsParams struct {
_, err := q.db.ExecContext(ctx, deleteAMPHTLCCustomRecords, invoiceID) InvoiceID int64
return err SetID []byte
} }
const deleteAMPHTLCs = `-- name: DeleteAMPHTLCs :exec type FetchAMPSubInvoiceHTLCsRow struct {
DELETE SetID []byte
FROM amp_invoice_htlcs RootShare []byte
WHERE invoice_id = $1 ChildIndex int64
` Hash []byte
Preimage []byte
func (q *Queries) DeleteAMPHTLCs(ctx context.Context, invoiceID int32) error { ID int64
_, err := q.db.ExecContext(ctx, deleteAMPHTLCs, invoiceID) ChanID string
return err HtlcID int64
AmountMsat int64
TotalMppMsat sql.NullInt64
AcceptHeight int32
AcceptTime time.Time
ExpiryHeight int32
State int16
ResolveTime sql.NullTime
InvoiceID int64
} }
const deleteAMPInvoiceHTLC = `-- name: DeleteAMPInvoiceHTLC :exec func (q *Queries) FetchAMPSubInvoiceHTLCs(ctx context.Context, arg FetchAMPSubInvoiceHTLCsParams) ([]FetchAMPSubInvoiceHTLCsRow, error) {
DELETE rows, err := q.db.QueryContext(ctx, fetchAMPSubInvoiceHTLCs, arg.InvoiceID, arg.SetID)
FROM amp_invoice_htlcs
WHERE set_id = $1
`
func (q *Queries) DeleteAMPInvoiceHTLC(ctx context.Context, setID []byte) error {
_, err := q.db.ExecContext(ctx, deleteAMPInvoiceHTLC, setID)
return err
}
const getAMPInvoiceHTLCsByInvoiceID = `-- name: GetAMPInvoiceHTLCsByInvoiceID :many
SELECT set_id, htlc_id, invoice_id, root_share, child_index, hash, preimage
FROM amp_invoice_htlcs
WHERE invoice_id = $1
`
func (q *Queries) GetAMPInvoiceHTLCsByInvoiceID(ctx context.Context, invoiceID int32) ([]AmpInvoiceHtlc, error) {
rows, err := q.db.QueryContext(ctx, getAMPInvoiceHTLCsByInvoiceID, invoiceID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []AmpInvoiceHtlc var items []FetchAMPSubInvoiceHTLCsRow
for rows.Next() { for rows.Next() {
var i AmpInvoiceHtlc var i FetchAMPSubInvoiceHTLCsRow
if err := rows.Scan( if err := rows.Scan(
&i.SetID, &i.SetID,
&i.HtlcID,
&i.InvoiceID,
&i.RootShare, &i.RootShare,
&i.ChildIndex, &i.ChildIndex,
&i.Hash, &i.Hash,
&i.Preimage, &i.Preimage,
&i.ID,
&i.ChanID,
&i.HtlcID,
&i.AmountMsat,
&i.TotalMppMsat,
&i.AcceptHeight,
&i.AcceptTime,
&i.ExpiryHeight,
&i.State,
&i.ResolveTime,
&i.InvoiceID,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -86,29 +88,37 @@ func (q *Queries) GetAMPInvoiceHTLCsByInvoiceID(ctx context.Context, invoiceID i
return items, nil return items, nil
} }
const getAMPInvoiceHTLCsBySetID = `-- name: GetAMPInvoiceHTLCsBySetID :many const fetchAMPSubInvoices = `-- name: FetchAMPSubInvoices :many
SELECT set_id, htlc_id, invoice_id, root_share, child_index, hash, preimage SELECT set_id, state, created_at, settled_at, settle_index, invoice_id
FROM amp_invoice_htlcs FROM amp_sub_invoices
WHERE set_id = $1 WHERE invoice_id = $1
AND (
set_id = $2 OR
$2 IS NULL
)
` `
func (q *Queries) GetAMPInvoiceHTLCsBySetID(ctx context.Context, setID []byte) ([]AmpInvoiceHtlc, error) { type FetchAMPSubInvoicesParams struct {
rows, err := q.db.QueryContext(ctx, getAMPInvoiceHTLCsBySetID, setID) InvoiceID int64
SetID []byte
}
func (q *Queries) FetchAMPSubInvoices(ctx context.Context, arg FetchAMPSubInvoicesParams) ([]AmpSubInvoice, error) {
rows, err := q.db.QueryContext(ctx, fetchAMPSubInvoices, arg.InvoiceID, arg.SetID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []AmpInvoiceHtlc var items []AmpSubInvoice
for rows.Next() { for rows.Next() {
var i AmpInvoiceHtlc var i AmpSubInvoice
if err := rows.Scan( if err := rows.Scan(
&i.SetID, &i.SetID,
&i.HtlcID, &i.State,
&i.CreatedAt,
&i.SettledAt,
&i.SettleIndex,
&i.InvoiceID, &i.InvoiceID,
&i.RootShare,
&i.ChildIndex,
&i.Hash,
&i.Preimage,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -123,28 +133,84 @@ func (q *Queries) GetAMPInvoiceHTLCsBySetID(ctx context.Context, setID []byte) (
return items, nil return items, nil
} }
const getSetIDHTLCsCustomRecords = `-- name: GetSetIDHTLCsCustomRecords :many const fetchSettledAMPSubInvoices = `-- name: FetchSettledAMPSubInvoices :many
SELECT ihcr.htlc_id, key, value SELECT
FROM amp_invoice_htlcs aih JOIN invoice_htlc_custom_records ihcr ON aih.id=ihcr.htlc_id a.set_id,
WHERE aih.set_id = $1 a.settle_index as amp_settle_index,
a.settled_at as amp_settled_at,
i.id, i.hash, i.preimage, i.settle_index, i.settled_at, i.memo, i.amount_msat, i.cltv_delta, i.expiry, i.payment_addr, i.payment_request, i.payment_request_hash, i.state, i.amount_paid_msat, i.is_amp, i.is_hodl, i.is_keysend, i.created_at
FROM amp_sub_invoices a
INNER JOIN invoices i ON a.invoice_id = i.id
WHERE (
a.settle_index >= $1 OR
$1 IS NULL
) AND (
a.settle_index <= $2 OR
$2 IS NULL
)
` `
type GetSetIDHTLCsCustomRecordsRow struct { type FetchSettledAMPSubInvoicesParams struct {
HtlcID int64 SettleIndexGet sql.NullInt64
Key int64 SettleIndexLet sql.NullInt64
Value []byte
} }
func (q *Queries) GetSetIDHTLCsCustomRecords(ctx context.Context, setID []byte) ([]GetSetIDHTLCsCustomRecordsRow, error) { type FetchSettledAMPSubInvoicesRow struct {
rows, err := q.db.QueryContext(ctx, getSetIDHTLCsCustomRecords, setID) SetID []byte
AmpSettleIndex sql.NullInt64
AmpSettledAt sql.NullTime
ID int64
Hash []byte
Preimage []byte
SettleIndex sql.NullInt64
SettledAt sql.NullTime
Memo sql.NullString
AmountMsat int64
CltvDelta sql.NullInt32
Expiry int32
PaymentAddr []byte
PaymentRequest sql.NullString
PaymentRequestHash []byte
State int16
AmountPaidMsat int64
IsAmp bool
IsHodl bool
IsKeysend bool
CreatedAt time.Time
}
func (q *Queries) FetchSettledAMPSubInvoices(ctx context.Context, arg FetchSettledAMPSubInvoicesParams) ([]FetchSettledAMPSubInvoicesRow, error) {
rows, err := q.db.QueryContext(ctx, fetchSettledAMPSubInvoices, arg.SettleIndexGet, arg.SettleIndexLet)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []GetSetIDHTLCsCustomRecordsRow var items []FetchSettledAMPSubInvoicesRow
for rows.Next() { for rows.Next() {
var i GetSetIDHTLCsCustomRecordsRow var i FetchSettledAMPSubInvoicesRow
if err := rows.Scan(&i.HtlcID, &i.Key, &i.Value); err != nil { if err := rows.Scan(
&i.SetID,
&i.AmpSettleIndex,
&i.AmpSettledAt,
&i.ID,
&i.Hash,
&i.Preimage,
&i.SettleIndex,
&i.SettledAt,
&i.Memo,
&i.AmountMsat,
&i.CltvDelta,
&i.Expiry,
&i.PaymentAddr,
&i.PaymentRequest,
&i.PaymentRequestHash,
&i.State,
&i.AmountPaidMsat,
&i.IsAmp,
&i.IsHodl,
&i.IsKeysend,
&i.CreatedAt,
); err != nil {
return nil, err return nil, err
} }
items = append(items, i) items = append(items, i)
@@ -158,15 +224,27 @@ func (q *Queries) GetSetIDHTLCsCustomRecords(ctx context.Context, setID []byte)
return items, nil return items, nil
} }
const insertAMPInvoiceHTLC = `-- name: InsertAMPInvoiceHTLC :exec const getAMPInvoiceID = `-- name: GetAMPInvoiceID :one
INSERT INTO amp_invoice_htlcs ( SELECT invoice_id FROM amp_sub_invoices WHERE set_id = $1
set_id, htlc_id, root_share, child_index, hash, preimage `
func (q *Queries) GetAMPInvoiceID(ctx context.Context, setID []byte) (int64, error) {
row := q.db.QueryRowContext(ctx, getAMPInvoiceID, setID)
var invoice_id int64
err := row.Scan(&invoice_id)
return invoice_id, err
}
const insertAMPSubInvoiceHTLC = `-- name: InsertAMPSubInvoiceHTLC :exec
INSERT INTO amp_sub_invoice_htlcs (
invoice_id, set_id, htlc_id, root_share, child_index, hash, preimage
) VALUES ( ) VALUES (
$1, $2, $3, $4, $5, $6 $1, $2, $3, $4, $5, $6, $7
) )
` `
type InsertAMPInvoiceHTLCParams struct { type InsertAMPSubInvoiceHTLCParams struct {
InvoiceID int64
SetID []byte SetID []byte
HtlcID int64 HtlcID int64
RootShare []byte RootShare []byte
@@ -175,8 +253,9 @@ type InsertAMPInvoiceHTLCParams struct {
Preimage []byte Preimage []byte
} }
func (q *Queries) InsertAMPInvoiceHTLC(ctx context.Context, arg InsertAMPInvoiceHTLCParams) error { func (q *Queries) InsertAMPSubInvoiceHTLC(ctx context.Context, arg InsertAMPSubInvoiceHTLCParams) error {
_, err := q.db.ExecContext(ctx, insertAMPInvoiceHTLC, _, err := q.db.ExecContext(ctx, insertAMPSubInvoiceHTLC,
arg.InvoiceID,
arg.SetID, arg.SetID,
arg.HtlcID, arg.HtlcID,
arg.RootShare, arg.RootShare,
@@ -187,140 +266,75 @@ func (q *Queries) InsertAMPInvoiceHTLC(ctx context.Context, arg InsertAMPInvoice
return err return err
} }
const insertAMPInvoicePayment = `-- name: InsertAMPInvoicePayment :exec const updateAMPSubInvoiceHTLCPreimage = `-- name: UpdateAMPSubInvoiceHTLCPreimage :execresult
INSERT INTO amp_invoice_payments ( UPDATE amp_sub_invoice_htlcs AS a
set_id, state, created_at, settled_index, invoice_id SET preimage = $4
) VALUES ( WHERE a.invoice_id = $1 AND a.set_id = $2 AND a.htlc_id = (
$1, $2, $3, $4, $5 SELECT id FROM invoice_htlcs AS i WHERE i.htlc_id = $3
) )
` `
type InsertAMPInvoicePaymentParams struct { type UpdateAMPSubInvoiceHTLCPreimageParams struct {
SetID []byte InvoiceID int64
State int16 SetID []byte
CreatedAt time.Time HtlcID int64
SettledIndex sql.NullInt32 Preimage []byte
InvoiceID int32
} }
func (q *Queries) InsertAMPInvoicePayment(ctx context.Context, arg InsertAMPInvoicePaymentParams) error { func (q *Queries) UpdateAMPSubInvoiceHTLCPreimage(ctx context.Context, arg UpdateAMPSubInvoiceHTLCPreimageParams) (sql.Result, error) {
_, err := q.db.ExecContext(ctx, insertAMPInvoicePayment, return q.db.ExecContext(ctx, updateAMPSubInvoiceHTLCPreimage,
arg.InvoiceID,
arg.SetID,
arg.HtlcID,
arg.Preimage,
)
}
const updateAMPSubInvoiceState = `-- name: UpdateAMPSubInvoiceState :exec
UPDATE amp_sub_invoices
SET state = $2,
settle_index = COALESCE(settle_index, $3),
settled_at = COALESCE(settled_at, $4)
WHERE set_id = $1
`
type UpdateAMPSubInvoiceStateParams struct {
SetID []byte
State int16
SettleIndex sql.NullInt64
SettledAt sql.NullTime
}
func (q *Queries) UpdateAMPSubInvoiceState(ctx context.Context, arg UpdateAMPSubInvoiceStateParams) error {
_, err := q.db.ExecContext(ctx, updateAMPSubInvoiceState,
arg.SetID,
arg.State,
arg.SettleIndex,
arg.SettledAt,
)
return err
}
const upsertAMPSubInvoice = `-- name: UpsertAMPSubInvoice :execresult
INSERT INTO amp_sub_invoices (
set_id, state, created_at, invoice_id
) VALUES (
$1, $2, $3, $4
) ON CONFLICT (set_id, invoice_id) DO NOTHING
`
type UpsertAMPSubInvoiceParams struct {
SetID []byte
State int16
CreatedAt time.Time
InvoiceID int64
}
func (q *Queries) UpsertAMPSubInvoice(ctx context.Context, arg UpsertAMPSubInvoiceParams) (sql.Result, error) {
return q.db.ExecContext(ctx, upsertAMPSubInvoice,
arg.SetID, arg.SetID,
arg.State, arg.State,
arg.CreatedAt, arg.CreatedAt,
arg.SettledIndex,
arg.InvoiceID, arg.InvoiceID,
) )
return err
}
const selectAMPInvoicePayments = `-- name: SelectAMPInvoicePayments :many
SELECT aip.set_id, aip.state, aip.created_at, aip.settled_index, aip.invoice_id, ip.id, ip.settled_at, ip.amount_paid_msat, ip.invoice_id
FROM amp_invoice_payments aip LEFT JOIN invoice_payments ip ON aip.settled_index = ip.id
WHERE (
set_id = $1 OR
$1 IS NULL
) AND (
aip.settled_index = $2 OR
$2 IS NULL
) AND (
aip.invoice_id = $3 OR
$3 IS NULL
)
`
type SelectAMPInvoicePaymentsParams struct {
SetID []byte
SettledIndex sql.NullInt32
InvoiceID sql.NullInt32
}
type SelectAMPInvoicePaymentsRow struct {
SetID []byte
State int16
CreatedAt time.Time
SettledIndex sql.NullInt32
InvoiceID int32
ID sql.NullInt32
SettledAt sql.NullTime
AmountPaidMsat sql.NullInt64
InvoiceID_2 sql.NullInt32
}
func (q *Queries) SelectAMPInvoicePayments(ctx context.Context, arg SelectAMPInvoicePaymentsParams) ([]SelectAMPInvoicePaymentsRow, error) {
rows, err := q.db.QueryContext(ctx, selectAMPInvoicePayments, arg.SetID, arg.SettledIndex, arg.InvoiceID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []SelectAMPInvoicePaymentsRow
for rows.Next() {
var i SelectAMPInvoicePaymentsRow
if err := rows.Scan(
&i.SetID,
&i.State,
&i.CreatedAt,
&i.SettledIndex,
&i.InvoiceID,
&i.ID,
&i.SettledAt,
&i.AmountPaidMsat,
&i.InvoiceID_2,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateAMPInvoiceHTLC = `-- name: UpdateAMPInvoiceHTLC :exec
UPDATE amp_invoice_htlcs
SET preimage = $1
WHERE htlc_id = $2
`
type UpdateAMPInvoiceHTLCParams struct {
Preimage []byte
HtlcID int64
}
func (q *Queries) UpdateAMPInvoiceHTLC(ctx context.Context, arg UpdateAMPInvoiceHTLCParams) error {
_, err := q.db.ExecContext(ctx, updateAMPInvoiceHTLC, arg.Preimage, arg.HtlcID)
return err
}
const updateAMPPayment = `-- name: UpdateAMPPayment :exec
UPDATE amp_invoice_payments
SET state = $1, settled_index = $2
WHERE state = 0 AND (
set_id = $3 OR
$3 IS NULL
) AND (
invoice_id = $4 OR
$4 IS NULL
)
`
type UpdateAMPPaymentParams struct {
State int16
SettledIndex sql.NullInt32
SetID []byte
InvoiceID sql.NullInt32
}
func (q *Queries) UpdateAMPPayment(ctx context.Context, arg UpdateAMPPaymentParams) error {
_, err := q.db.ExecContext(ctx, updateAMPPayment,
arg.State,
arg.SettledIndex,
arg.SetID,
arg.InvoiceID,
)
return err
} }
+1 -1
View File
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.18.0 // sqlc v1.25.0
package sqlc package sqlc
+96 -102
View File
@@ -1,128 +1,122 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.18.0 // sqlc v1.25.0
// source: invoice_events.sql // source: invoice_events.sql
package sqlc package sqlc
import ( import (
"context" "context"
"database/sql"
"time" "time"
) )
const deleteInvoiceEvents = `-- name: DeleteInvoiceEvents :exec const onAMPSubInvoiceCanceled = `-- name: OnAMPSubInvoiceCanceled :exec
DELETE
FROM invoice_events
WHERE invoice_id = $1
`
func (q *Queries) DeleteInvoiceEvents(ctx context.Context, invoiceID int32) error {
_, err := q.db.ExecContext(ctx, deleteInvoiceEvents, invoiceID)
return err
}
const insertInvoiceEvent = `-- name: InsertInvoiceEvent :exec
INSERT INTO invoice_events ( INSERT INTO invoice_events (
created_at, invoice_id, htlc_id, set_id, event_type, event_metadata added_at, event_type, invoice_id, set_id
) VALUES ( ) VALUES (
$1, $2, $3, $4, $5, $6 $1, 4, $2, $3
) )
` `
type InsertInvoiceEventParams struct { type OnAMPSubInvoiceCanceledParams struct {
CreatedAt time.Time AddedAt time.Time
InvoiceID int32 InvoiceID int64
HtlcID sql.NullInt64 SetID []byte
SetID []byte
EventType int32
EventMetadata []byte
} }
func (q *Queries) InsertInvoiceEvent(ctx context.Context, arg InsertInvoiceEventParams) error { func (q *Queries) OnAMPSubInvoiceCanceled(ctx context.Context, arg OnAMPSubInvoiceCanceledParams) error {
_, err := q.db.ExecContext(ctx, insertInvoiceEvent, _, err := q.db.ExecContext(ctx, onAMPSubInvoiceCanceled, arg.AddedAt, arg.InvoiceID, arg.SetID)
arg.CreatedAt,
arg.InvoiceID,
arg.HtlcID,
arg.SetID,
arg.EventType,
arg.EventMetadata,
)
return err return err
} }
const selectInvoiceEvents = `-- name: SelectInvoiceEvents :many const onAMPSubInvoiceCreated = `-- name: OnAMPSubInvoiceCreated :exec
SELECT id, created_at, invoice_id, htlc_id, set_id, event_type, event_metadata INSERT INTO invoice_events (
FROM invoice_events added_at, event_type, invoice_id, set_id
WHERE ( ) VALUES (
invoice_id = $1 OR $1, 3, $2, $3
$1 IS NULL )
) AND (
htlc_id = $2 OR
$2 IS NULL
) AND (
set_id = $3 OR
$3 IS NULL
) AND (
event_type = $4 OR
$4 IS NULL
) AND (
created_at >= $5 OR
$5 IS NULL
) AND (
created_at <= $6 OR
$6 IS NULL
)
LIMIT $8 OFFSET $7
` `
type SelectInvoiceEventsParams struct { type OnAMPSubInvoiceCreatedParams struct {
InvoiceID sql.NullInt32 AddedAt time.Time
HtlcID sql.NullInt64 InvoiceID int64
SetID []byte SetID []byte
EventType sql.NullInt32
CreatedAfter sql.NullTime
CreatedBefore sql.NullTime
NumOffset int32
NumLimit int32
} }
func (q *Queries) SelectInvoiceEvents(ctx context.Context, arg SelectInvoiceEventsParams) ([]InvoiceEvent, error) { func (q *Queries) OnAMPSubInvoiceCreated(ctx context.Context, arg OnAMPSubInvoiceCreatedParams) error {
rows, err := q.db.QueryContext(ctx, selectInvoiceEvents, _, err := q.db.ExecContext(ctx, onAMPSubInvoiceCreated, arg.AddedAt, arg.InvoiceID, arg.SetID)
arg.InvoiceID, return err
arg.HtlcID, }
arg.SetID,
arg.EventType, const onAMPSubInvoiceSettled = `-- name: OnAMPSubInvoiceSettled :exec
arg.CreatedAfter, INSERT INTO invoice_events (
arg.CreatedBefore, added_at, event_type, invoice_id, set_id
arg.NumOffset, ) VALUES (
arg.NumLimit, $1, 5, $2, $3
) )
if err != nil { `
return nil, err
} type OnAMPSubInvoiceSettledParams struct {
defer rows.Close() AddedAt time.Time
var items []InvoiceEvent InvoiceID int64
for rows.Next() { SetID []byte
var i InvoiceEvent }
if err := rows.Scan(
&i.ID, func (q *Queries) OnAMPSubInvoiceSettled(ctx context.Context, arg OnAMPSubInvoiceSettledParams) error {
&i.CreatedAt, _, err := q.db.ExecContext(ctx, onAMPSubInvoiceSettled, arg.AddedAt, arg.InvoiceID, arg.SetID)
&i.InvoiceID, return err
&i.HtlcID, }
&i.SetID,
&i.EventType, const onInvoiceCanceled = `-- name: OnInvoiceCanceled :exec
&i.EventMetadata, INSERT INTO invoice_events (
); err != nil { added_at, event_type, invoice_id
return nil, err ) VALUES (
} $1, 1, $2
items = append(items, i) )
} `
if err := rows.Close(); err != nil {
return nil, err type OnInvoiceCanceledParams struct {
} AddedAt time.Time
if err := rows.Err(); err != nil { InvoiceID int64
return nil, err }
}
return items, nil func (q *Queries) OnInvoiceCanceled(ctx context.Context, arg OnInvoiceCanceledParams) error {
_, err := q.db.ExecContext(ctx, onInvoiceCanceled, arg.AddedAt, arg.InvoiceID)
return err
}
const onInvoiceCreated = `-- name: OnInvoiceCreated :exec
INSERT INTO invoice_events (
added_at, event_type, invoice_id
) VALUES (
$1, 0, $2
)
`
type OnInvoiceCreatedParams struct {
AddedAt time.Time
InvoiceID int64
}
func (q *Queries) OnInvoiceCreated(ctx context.Context, arg OnInvoiceCreatedParams) error {
_, err := q.db.ExecContext(ctx, onInvoiceCreated, arg.AddedAt, arg.InvoiceID)
return err
}
const onInvoiceSettled = `-- name: OnInvoiceSettled :exec
INSERT INTO invoice_events (
added_at, event_type, invoice_id
) VALUES (
$1, 2, $2
)
`
type OnInvoiceSettledParams struct {
AddedAt time.Time
InvoiceID int64
}
func (q *Queries) OnInvoiceSettled(ctx context.Context, arg OnInvoiceSettledParams) error {
_, err := q.db.ExecContext(ctx, onInvoiceSettled, arg.AddedAt, arg.InvoiceID)
return err
} }
+158 -286
View File
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.18.0 // sqlc v1.25.0
// source: invoices.sql // source: invoices.sql
package sqlc package sqlc
@@ -11,7 +11,17 @@ import (
"time" "time"
) )
const deleteInvoice = `-- name: DeleteInvoice :exec const deleteCanceledInvoices = `-- name: DeleteCanceledInvoices :execresult
DELETE
FROM invoices
WHERE state = 2
`
func (q *Queries) DeleteCanceledInvoices(ctx context.Context) (sql.Result, error) {
return q.db.ExecContext(ctx, deleteCanceledInvoices)
}
const deleteInvoice = `-- name: DeleteInvoice :execresult
DELETE DELETE
FROM invoices FROM invoices
WHERE ( WHERE (
@@ -21,7 +31,7 @@ WHERE (
hash = $2 OR hash = $2 OR
$2 IS NULL $2 IS NULL
) AND ( ) AND (
preimage = $3 OR settle_index = $3 OR
$3 IS NULL $3 IS NULL
) AND ( ) AND (
payment_addr = $4 OR payment_addr = $4 OR
@@ -30,174 +40,24 @@ WHERE (
` `
type DeleteInvoiceParams struct { type DeleteInvoiceParams struct {
AddIndex sql.NullInt32 AddIndex sql.NullInt64
Hash []byte Hash []byte
Preimage []byte SettleIndex sql.NullInt64
PaymentAddr []byte PaymentAddr []byte
} }
func (q *Queries) DeleteInvoice(ctx context.Context, arg DeleteInvoiceParams) error { func (q *Queries) DeleteInvoice(ctx context.Context, arg DeleteInvoiceParams) (sql.Result, error) {
_, err := q.db.ExecContext(ctx, deleteInvoice, return q.db.ExecContext(ctx, deleteInvoice,
arg.AddIndex, arg.AddIndex,
arg.Hash, arg.Hash,
arg.Preimage, arg.SettleIndex,
arg.PaymentAddr, arg.PaymentAddr,
) )
return err
}
const deleteInvoiceFeatures = `-- name: DeleteInvoiceFeatures :exec
DELETE
FROM invoice_features
WHERE invoice_id = $1
`
func (q *Queries) DeleteInvoiceFeatures(ctx context.Context, invoiceID int32) error {
_, err := q.db.ExecContext(ctx, deleteInvoiceFeatures, invoiceID)
return err
}
const deleteInvoiceHTLC = `-- name: DeleteInvoiceHTLC :exec
DELETE
FROM invoice_htlcs
WHERE htlc_id = $1
`
func (q *Queries) DeleteInvoiceHTLC(ctx context.Context, htlcID int64) error {
_, err := q.db.ExecContext(ctx, deleteInvoiceHTLC, htlcID)
return err
}
const deleteInvoiceHTLCCustomRecords = `-- name: DeleteInvoiceHTLCCustomRecords :exec
WITH htlc_ids AS (
SELECT ih.id
FROM invoice_htlcs ih JOIN invoice_htlc_custom_records ihcr ON ih.id=ihcr.htlc_id
WHERE ih.invoice_id = $1
)
DELETE
FROM invoice_htlc_custom_records
WHERE htlc_id IN (SELECT id FROM htlc_ids)
`
func (q *Queries) DeleteInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int32) error {
_, err := q.db.ExecContext(ctx, deleteInvoiceHTLCCustomRecords, invoiceID)
return err
}
const deleteInvoiceHTLCs = `-- name: DeleteInvoiceHTLCs :exec
DELETE
FROM invoice_htlcs
WHERE invoice_id = $1
`
func (q *Queries) DeleteInvoiceHTLCs(ctx context.Context, invoiceID int32) error {
_, err := q.db.ExecContext(ctx, deleteInvoiceHTLCs, invoiceID)
return err
}
const filterInvoicePayments = `-- name: FilterInvoicePayments :many
SELECT
ip.id AS settle_index, ip.amount_paid_msat, ip.settled_at AS settle_date,
i.id, i.hash, i.preimage, i.memo, i.amount_msat, i.cltv_delta, i.expiry, i.payment_addr, i.payment_request, i.state, i.amount_paid_msat, i.is_amp, i.is_hodl, i.is_keysend, i.created_at
FROM invoice_payments ip JOIN invoices i ON ip.invoice_id = i.id
WHERE (
ip.id >= $1 OR
$1 IS NULL
) AND (
ip.settled_at >= $2 OR
$2 IS NULL
)
ORDER BY
CASE
WHEN $3 = FALSE THEN ip.id
ELSE NULL
END ASC,
CASE
WHEN $3 = TRUE THEN ip.id
ELSE NULL
END DESC
LIMIT $5 OFFSET $4
`
type FilterInvoicePaymentsParams struct {
SettleIndexGet sql.NullInt32
SettledAfter sql.NullTime
Reverse interface{}
NumOffset int32
NumLimit int32
}
type FilterInvoicePaymentsRow struct {
SettleIndex int32
AmountPaidMsat int64
SettleDate time.Time
ID int32
Hash []byte
Preimage []byte
Memo sql.NullString
AmountMsat int64
CltvDelta sql.NullInt32
Expiry int32
PaymentAddr []byte
PaymentRequest sql.NullString
State int16
AmountPaidMsat_2 int64
IsAmp bool
IsHodl bool
IsKeysend bool
CreatedAt time.Time
}
func (q *Queries) FilterInvoicePayments(ctx context.Context, arg FilterInvoicePaymentsParams) ([]FilterInvoicePaymentsRow, error) {
rows, err := q.db.QueryContext(ctx, filterInvoicePayments,
arg.SettleIndexGet,
arg.SettledAfter,
arg.Reverse,
arg.NumOffset,
arg.NumLimit,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []FilterInvoicePaymentsRow
for rows.Next() {
var i FilterInvoicePaymentsRow
if err := rows.Scan(
&i.SettleIndex,
&i.AmountPaidMsat,
&i.SettleDate,
&i.ID,
&i.Hash,
&i.Preimage,
&i.Memo,
&i.AmountMsat,
&i.CltvDelta,
&i.Expiry,
&i.PaymentAddr,
&i.PaymentRequest,
&i.State,
&i.AmountPaidMsat_2,
&i.IsAmp,
&i.IsHodl,
&i.IsKeysend,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
} }
const filterInvoices = `-- name: FilterInvoices :many const filterInvoices = `-- name: FilterInvoices :many
SELECT id, hash, preimage, memo, amount_msat, cltv_delta, expiry, payment_addr, payment_request, state, amount_paid_msat, is_amp, is_hodl, is_keysend, created_at SELECT
invoices.id, invoices.hash, invoices.preimage, invoices.settle_index, invoices.settled_at, invoices.memo, invoices.amount_msat, invoices.cltv_delta, invoices.expiry, invoices.payment_addr, invoices.payment_request, invoices.payment_request_hash, invoices.state, invoices.amount_paid_msat, invoices.is_amp, invoices.is_hodl, invoices.is_keysend, invoices.created_at
FROM invoices FROM invoices
WHERE ( WHERE (
id >= $1 OR id >= $1 OR
@@ -206,48 +66,58 @@ WHERE (
id <= $2 OR id <= $2 OR
$2 IS NULL $2 IS NULL
) AND ( ) AND (
state = $3 OR settle_index >= $3 OR
$3 IS NULL $3 IS NULL
) AND ( ) AND (
created_at >= $4 OR settle_index <= $4 OR
$4 IS NULL $4 IS NULL
) AND ( ) AND (
created_at <= $5 OR state = $5 OR
$5 IS NULL $5 IS NULL
) AND (
created_at >= $6 OR
$6 IS NULL
) AND (
created_at <= $7 OR
$7 IS NULL
) AND ( ) AND (
CASE CASE
WHEN $6=TRUE THEN (state = 0 OR state = 3) WHEN $8=TRUE THEN (state = 0 OR state = 3)
ELSE TRUE ELSE TRUE
END END
) )
ORDER BY ORDER BY
CASE CASE
WHEN $7 = FALSE THEN id WHEN $9 = FALSE THEN id
ELSE NULL ELSE NULL
END ASC, END ASC,
CASE CASE
WHEN $7 = TRUE THEN id WHEN $9 = TRUE THEN id
ELSE NULL ELSE NULL
END DESC END DESC
LIMIT $9 OFFSET $8 LIMIT $11 OFFSET $10
` `
type FilterInvoicesParams struct { type FilterInvoicesParams struct {
AddIndexGet sql.NullInt32 AddIndexGet sql.NullInt64
AddIndexLet sql.NullInt32 AddIndexLet sql.NullInt64
State sql.NullInt16 SettleIndexGet sql.NullInt64
CreatedAfter sql.NullTime SettleIndexLet sql.NullInt64
CreatedBefore sql.NullTime State sql.NullInt16
PendingOnly interface{} CreatedAfter sql.NullTime
Reverse interface{} CreatedBefore sql.NullTime
NumOffset int32 PendingOnly interface{}
NumLimit int32 Reverse interface{}
NumOffset int32
NumLimit int32
} }
func (q *Queries) FilterInvoices(ctx context.Context, arg FilterInvoicesParams) ([]Invoice, error) { func (q *Queries) FilterInvoices(ctx context.Context, arg FilterInvoicesParams) ([]Invoice, error) {
rows, err := q.db.QueryContext(ctx, filterInvoices, rows, err := q.db.QueryContext(ctx, filterInvoices,
arg.AddIndexGet, arg.AddIndexGet,
arg.AddIndexLet, arg.AddIndexLet,
arg.SettleIndexGet,
arg.SettleIndexLet,
arg.State, arg.State,
arg.CreatedAfter, arg.CreatedAfter,
arg.CreatedBefore, arg.CreatedBefore,
@@ -267,12 +137,15 @@ func (q *Queries) FilterInvoices(ctx context.Context, arg FilterInvoicesParams)
&i.ID, &i.ID,
&i.Hash, &i.Hash,
&i.Preimage, &i.Preimage,
&i.SettleIndex,
&i.SettledAt,
&i.Memo, &i.Memo,
&i.AmountMsat, &i.AmountMsat,
&i.CltvDelta, &i.CltvDelta,
&i.Expiry, &i.Expiry,
&i.PaymentAddr, &i.PaymentAddr,
&i.PaymentRequest, &i.PaymentRequest,
&i.PaymentRequestHash,
&i.State, &i.State,
&i.AmountPaidMsat, &i.AmountPaidMsat,
&i.IsAmp, &i.IsAmp,
@@ -295,29 +168,35 @@ func (q *Queries) FilterInvoices(ctx context.Context, arg FilterInvoicesParams)
const getInvoice = `-- name: GetInvoice :many const getInvoice = `-- name: GetInvoice :many
SELECT id, hash, preimage, memo, amount_msat, cltv_delta, expiry, payment_addr, payment_request, state, amount_paid_msat, is_amp, is_hodl, is_keysend, created_at SELECT i.id, i.hash, i.preimage, i.settle_index, i.settled_at, i.memo, i.amount_msat, i.cltv_delta, i.expiry, i.payment_addr, i.payment_request, i.payment_request_hash, i.state, i.amount_paid_msat, i.is_amp, i.is_hodl, i.is_keysend, i.created_at
FROM invoices FROM invoices i
LEFT JOIN amp_sub_invoices a on i.id = a.invoice_id
WHERE ( WHERE (
id = $1 OR i.id = $1 OR
$1 IS NULL $1 IS NULL
) AND ( ) AND (
hash = $2 OR i.hash = $2 OR
$2 IS NULL $2 IS NULL
) AND ( ) AND (
preimage = $3 OR i.preimage = $3 OR
$3 IS NULL $3 IS NULL
) AND ( ) AND (
payment_addr = $4 OR i.payment_addr = $4 OR
$4 IS NULL $4 IS NULL
) AND (
a.set_id = $5 OR
$5 IS NULL
) )
GROUP BY i.id
LIMIT 2 LIMIT 2
` `
type GetInvoiceParams struct { type GetInvoiceParams struct {
AddIndex sql.NullInt32 AddIndex sql.NullInt64
Hash []byte Hash []byte
Preimage []byte Preimage []byte
PaymentAddr []byte PaymentAddr []byte
SetID []byte
} }
// This method may return more than one invoice if filter using multiple fields // This method may return more than one invoice if filter using multiple fields
@@ -329,6 +208,7 @@ func (q *Queries) GetInvoice(ctx context.Context, arg GetInvoiceParams) ([]Invoi
arg.Hash, arg.Hash,
arg.Preimage, arg.Preimage,
arg.PaymentAddr, arg.PaymentAddr,
arg.SetID,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@@ -341,12 +221,15 @@ func (q *Queries) GetInvoice(ctx context.Context, arg GetInvoiceParams) ([]Invoi
&i.ID, &i.ID,
&i.Hash, &i.Hash,
&i.Preimage, &i.Preimage,
&i.SettleIndex,
&i.SettledAt,
&i.Memo, &i.Memo,
&i.AmountMsat, &i.AmountMsat,
&i.CltvDelta, &i.CltvDelta,
&i.Expiry, &i.Expiry,
&i.PaymentAddr, &i.PaymentAddr,
&i.PaymentRequest, &i.PaymentRequest,
&i.PaymentRequestHash,
&i.State, &i.State,
&i.AmountPaidMsat, &i.AmountPaidMsat,
&i.IsAmp, &i.IsAmp,
@@ -373,7 +256,7 @@ FROM invoice_features
WHERE invoice_id = $1 WHERE invoice_id = $1
` `
func (q *Queries) GetInvoiceFeatures(ctx context.Context, invoiceID int32) ([]InvoiceFeature, error) { func (q *Queries) GetInvoiceFeatures(ctx context.Context, invoiceID int64) ([]InvoiceFeature, error) {
rows, err := q.db.QueryContext(ctx, getInvoiceFeatures, invoiceID) rows, err := q.db.QueryContext(ctx, getInvoiceFeatures, invoiceID)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -408,7 +291,7 @@ type GetInvoiceHTLCCustomRecordsRow struct {
Value []byte Value []byte
} }
func (q *Queries) GetInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int32) ([]GetInvoiceHTLCCustomRecordsRow, error) { func (q *Queries) GetInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int64) ([]GetInvoiceHTLCCustomRecordsRow, error) {
rows, err := q.db.QueryContext(ctx, getInvoiceHTLCCustomRecords, invoiceID) rows, err := q.db.QueryContext(ctx, getInvoiceHTLCCustomRecords, invoiceID)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -432,12 +315,12 @@ func (q *Queries) GetInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int
} }
const getInvoiceHTLCs = `-- name: GetInvoiceHTLCs :many const getInvoiceHTLCs = `-- name: GetInvoiceHTLCs :many
SELECT id, htlc_id, chan_id, amount_msat, total_mpp_msat, accept_height, accept_time, expiry_height, state, resolve_time, invoice_id SELECT id, chan_id, htlc_id, amount_msat, total_mpp_msat, accept_height, accept_time, expiry_height, state, resolve_time, invoice_id
FROM invoice_htlcs FROM invoice_htlcs
WHERE invoice_id = $1 WHERE invoice_id = $1
` `
func (q *Queries) GetInvoiceHTLCs(ctx context.Context, invoiceID int32) ([]InvoiceHtlc, error) { func (q *Queries) GetInvoiceHTLCs(ctx context.Context, invoiceID int64) ([]InvoiceHtlc, error) {
rows, err := q.db.QueryContext(ctx, getInvoiceHTLCs, invoiceID) rows, err := q.db.QueryContext(ctx, getInvoiceHTLCs, invoiceID)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -448,8 +331,8 @@ func (q *Queries) GetInvoiceHTLCs(ctx context.Context, invoiceID int32) ([]Invoi
var i InvoiceHtlc var i InvoiceHtlc
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.HtlcID,
&i.ChanID, &i.ChanID,
&i.HtlcID,
&i.AmountMsat, &i.AmountMsat,
&i.TotalMppMsat, &i.TotalMppMsat,
&i.AcceptHeight, &i.AcceptHeight,
@@ -472,68 +355,35 @@ func (q *Queries) GetInvoiceHTLCs(ctx context.Context, invoiceID int32) ([]Invoi
return items, nil return items, nil
} }
const getInvoicePayments = `-- name: GetInvoicePayments :many
SELECT id, settled_at, amount_paid_msat, invoice_id
FROM invoice_payments
WHERE invoice_id = $1
`
func (q *Queries) GetInvoicePayments(ctx context.Context, invoiceID int32) ([]InvoicePayment, error) {
rows, err := q.db.QueryContext(ctx, getInvoicePayments, invoiceID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []InvoicePayment
for rows.Next() {
var i InvoicePayment
if err := rows.Scan(
&i.ID,
&i.SettledAt,
&i.AmountPaidMsat,
&i.InvoiceID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const insertInvoice = `-- name: InsertInvoice :one const insertInvoice = `-- name: InsertInvoice :one
INSERT INTO invoices ( INSERT INTO invoices (
hash, preimage, memo, amount_msat, cltv_delta, expiry, payment_addr, hash, preimage, memo, amount_msat, cltv_delta, expiry, payment_addr,
payment_request, state, amount_paid_msat, is_amp, is_hodl, is_keysend, payment_request, payment_request_hash, state, amount_paid_msat, is_amp,
created_at is_hodl, is_keysend, created_at
) VALUES ( ) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14 $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15
) RETURNING id ) RETURNING id
` `
type InsertInvoiceParams struct { type InsertInvoiceParams struct {
Hash []byte Hash []byte
Preimage []byte Preimage []byte
Memo sql.NullString Memo sql.NullString
AmountMsat int64 AmountMsat int64
CltvDelta sql.NullInt32 CltvDelta sql.NullInt32
Expiry int32 Expiry int32
PaymentAddr []byte PaymentAddr []byte
PaymentRequest sql.NullString PaymentRequest sql.NullString
State int16 PaymentRequestHash []byte
AmountPaidMsat int64 State int16
IsAmp bool AmountPaidMsat int64
IsHodl bool IsAmp bool
IsKeysend bool IsHodl bool
CreatedAt time.Time IsKeysend bool
CreatedAt time.Time
} }
func (q *Queries) InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (int32, error) { func (q *Queries) InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (int64, error) {
row := q.db.QueryRowContext(ctx, insertInvoice, row := q.db.QueryRowContext(ctx, insertInvoice,
arg.Hash, arg.Hash,
arg.Preimage, arg.Preimage,
@@ -543,6 +393,7 @@ func (q *Queries) InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (i
arg.Expiry, arg.Expiry,
arg.PaymentAddr, arg.PaymentAddr,
arg.PaymentRequest, arg.PaymentRequest,
arg.PaymentRequestHash,
arg.State, arg.State,
arg.AmountPaidMsat, arg.AmountPaidMsat,
arg.IsAmp, arg.IsAmp,
@@ -550,7 +401,7 @@ func (q *Queries) InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (i
arg.IsKeysend, arg.IsKeysend,
arg.CreatedAt, arg.CreatedAt,
) )
var id int32 var id int64
err := row.Scan(&id) err := row.Scan(&id)
return id, err return id, err
} }
@@ -564,7 +415,7 @@ INSERT INTO invoice_features (
` `
type InsertInvoiceFeatureParams struct { type InsertInvoiceFeatureParams struct {
InvoiceID int32 InvoiceID int64
Feature int32 Feature int32
} }
@@ -573,13 +424,13 @@ func (q *Queries) InsertInvoiceFeature(ctx context.Context, arg InsertInvoiceFea
return err return err
} }
const insertInvoiceHTLC = `-- name: InsertInvoiceHTLC :exec const insertInvoiceHTLC = `-- name: InsertInvoiceHTLC :one
INSERT INTO invoice_htlcs ( INSERT INTO invoice_htlcs (
htlc_id, chan_id, amount_msat, total_mpp_msat, accept_height, accept_time, htlc_id, chan_id, amount_msat, total_mpp_msat, accept_height, accept_time,
expiry_height, state, resolve_time, invoice_id expiry_height, state, resolve_time, invoice_id
) VALUES ( ) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10 $1, $2, $3, $4, $5, $6, $7, $8, $9, $10
) ) RETURNING id
` `
type InsertInvoiceHTLCParams struct { type InsertInvoiceHTLCParams struct {
@@ -592,11 +443,11 @@ type InsertInvoiceHTLCParams struct {
ExpiryHeight int32 ExpiryHeight int32
State int16 State int16
ResolveTime sql.NullTime ResolveTime sql.NullTime
InvoiceID int32 InvoiceID int64
} }
func (q *Queries) InsertInvoiceHTLC(ctx context.Context, arg InsertInvoiceHTLCParams) error { func (q *Queries) InsertInvoiceHTLC(ctx context.Context, arg InsertInvoiceHTLCParams) (int64, error) {
_, err := q.db.ExecContext(ctx, insertInvoiceHTLC, row := q.db.QueryRowContext(ctx, insertInvoiceHTLC,
arg.HtlcID, arg.HtlcID,
arg.ChanID, arg.ChanID,
arg.AmountMsat, arg.AmountMsat,
@@ -608,7 +459,9 @@ func (q *Queries) InsertInvoiceHTLC(ctx context.Context, arg InsertInvoiceHTLCPa
arg.ResolveTime, arg.ResolveTime,
arg.InvoiceID, arg.InvoiceID,
) )
return err var id int64
err := row.Scan(&id)
return id, err
} }
const insertInvoiceHTLCCustomRecord = `-- name: InsertInvoiceHTLCCustomRecord :exec const insertInvoiceHTLCCustomRecord = `-- name: InsertInvoiceHTLCCustomRecord :exec
@@ -630,64 +483,56 @@ func (q *Queries) InsertInvoiceHTLCCustomRecord(ctx context.Context, arg InsertI
return err return err
} }
const insertInvoicePayment = `-- name: InsertInvoicePayment :one const nextInvoiceSettleIndex = `-- name: NextInvoiceSettleIndex :one
INSERT INTO invoice_payments ( UPDATE invoice_sequences SET current_value = current_value + 1
invoice_id, amount_paid_msat, settled_at WHERE name = 'settle_index'
) VALUES ( RETURNING current_value
$1, $2, $3
) RETURNING id
` `
type InsertInvoicePaymentParams struct { func (q *Queries) NextInvoiceSettleIndex(ctx context.Context) (int64, error) {
InvoiceID int32 row := q.db.QueryRowContext(ctx, nextInvoiceSettleIndex)
AmountPaidMsat int64 var current_value int64
SettledAt time.Time err := row.Scan(&current_value)
return current_value, err
} }
func (q *Queries) InsertInvoicePayment(ctx context.Context, arg InsertInvoicePaymentParams) (int32, error) { const updateInvoiceAmountPaid = `-- name: UpdateInvoiceAmountPaid :execresult
row := q.db.QueryRowContext(ctx, insertInvoicePayment, arg.InvoiceID, arg.AmountPaidMsat, arg.SettledAt) UPDATE invoices
var id int32 SET amount_paid_msat = $2
err := row.Scan(&id) WHERE id = $1
return id, err
}
const updateInvoice = `-- name: UpdateInvoice :exec
UPDATE invoices
SET preimage=$2, state=$3, amount_paid_msat=$4
WHERE id=$1
` `
type UpdateInvoiceParams struct { type UpdateInvoiceAmountPaidParams struct {
ID int32 ID int64
Preimage []byte
State int16
AmountPaidMsat int64 AmountPaidMsat int64
} }
func (q *Queries) UpdateInvoice(ctx context.Context, arg UpdateInvoiceParams) error { func (q *Queries) UpdateInvoiceAmountPaid(ctx context.Context, arg UpdateInvoiceAmountPaidParams) (sql.Result, error) {
_, err := q.db.ExecContext(ctx, updateInvoice, return q.db.ExecContext(ctx, updateInvoiceAmountPaid, arg.ID, arg.AmountPaidMsat)
arg.ID,
arg.Preimage,
arg.State,
arg.AmountPaidMsat,
)
return err
} }
const updateInvoiceHTLC = `-- name: UpdateInvoiceHTLC :exec const updateInvoiceHTLC = `-- name: UpdateInvoiceHTLC :exec
UPDATE invoice_htlcs UPDATE invoice_htlcs
SET state=$2, resolve_time=$3 SET state=$4, resolve_time=$5
WHERE id = $1 WHERE htlc_id = $1 AND chan_id = $2 AND invoice_id = $3
` `
type UpdateInvoiceHTLCParams struct { type UpdateInvoiceHTLCParams struct {
ID int32 HtlcID int64
ChanID string
InvoiceID int64
State int16 State int16
ResolveTime sql.NullTime ResolveTime sql.NullTime
} }
func (q *Queries) UpdateInvoiceHTLC(ctx context.Context, arg UpdateInvoiceHTLCParams) error { func (q *Queries) UpdateInvoiceHTLC(ctx context.Context, arg UpdateInvoiceHTLCParams) error {
_, err := q.db.ExecContext(ctx, updateInvoiceHTLC, arg.ID, arg.State, arg.ResolveTime) _, err := q.db.ExecContext(ctx, updateInvoiceHTLC,
arg.HtlcID,
arg.ChanID,
arg.InvoiceID,
arg.State,
arg.ResolveTime,
)
return err return err
} }
@@ -698,7 +543,7 @@ WHERE invoice_id = $1 AND resolve_time IS NULL
` `
type UpdateInvoiceHTLCsParams struct { type UpdateInvoiceHTLCsParams struct {
InvoiceID int32 InvoiceID int64
State int16 State int16
ResolveTime sql.NullTime ResolveTime sql.NullTime
} }
@@ -707,3 +552,30 @@ func (q *Queries) UpdateInvoiceHTLCs(ctx context.Context, arg UpdateInvoiceHTLCs
_, err := q.db.ExecContext(ctx, updateInvoiceHTLCs, arg.InvoiceID, arg.State, arg.ResolveTime) _, err := q.db.ExecContext(ctx, updateInvoiceHTLCs, arg.InvoiceID, arg.State, arg.ResolveTime)
return err return err
} }
const updateInvoiceState = `-- name: UpdateInvoiceState :execresult
UPDATE invoices
SET state = $2,
preimage = COALESCE(preimage, $3),
settle_index = COALESCE(settle_index, $4),
settled_at = COALESCE(settled_at, $5)
WHERE id = $1
`
type UpdateInvoiceStateParams struct {
ID int64
State int16
Preimage []byte
SettleIndex sql.NullInt64
SettledAt sql.NullTime
}
func (q *Queries) UpdateInvoiceState(ctx context.Context, arg UpdateInvoiceStateParams) (sql.Result, error) {
return q.db.ExecContext(ctx, updateInvoiceState,
arg.ID,
arg.State,
arg.Preimage,
arg.SettleIndex,
arg.SettledAt,
)
}
+34 -28
View File
@@ -1,7 +1,17 @@
-- sequences contains all sequences used for invoices.
CREATE TABLE invoice_sequences (
name TEXT PRIMARY KEY,
current_value BIGINT NOT NULL
);
-- Initialize a sequence for the settled index used to track invoice settlement
-- to remain compatible with the legacy channeldb implementation.
INSERT INTO invoice_sequences(name, current_value) VALUES ('settle_index', 0);
-- invoices table contains all the information shared by all the invoice types. -- invoices table contains all the information shared by all the invoice types.
CREATE TABLE IF NOT EXISTS invoices ( CREATE TABLE IF NOT EXISTS invoices (
-- The id of the invoice. Translates to the AddIndex. -- The id of the invoice. Translates to the AddIndex.
id INTEGER PRIMARY KEY, id BIGINT PRIMARY KEY,
-- The hash for this invoice. The invoice hash will always identify that -- The hash for this invoice. The invoice hash will always identify that
-- invoice. -- invoice.
@@ -10,6 +20,14 @@ CREATE TABLE IF NOT EXISTS invoices (
-- The preimage for the hash in this invoice. Some invoices may have this -- The preimage for the hash in this invoice. Some invoices may have this
-- field empty, like unsettled hodl invoices or AMP invoices. -- field empty, like unsettled hodl invoices or AMP invoices.
preimage BLOB, preimage BLOB,
-- If settled, the index is set to the current_value+1 of the settle_index
-- seuqence in the invoice_sequences table. If not settled, then it is set
-- to NULL.
settle_index BIGINT,
-- When the invoice was settled.
settled_at TIMESTAMP,
-- An optional memo to attach along with the invoice. -- An optional memo to attach along with the invoice.
memo TEXT, memo TEXT,
@@ -31,7 +49,12 @@ CREATE TABLE IF NOT EXISTS invoices (
-- The encoded payment request for this invoice. Some invoice types may -- The encoded payment request for this invoice. Some invoice types may
-- not have leave this empty, like Keysends. -- not have leave this empty, like Keysends.
payment_request TEXT UNIQUE, payment_request TEXT,
-- Holds the hash of the payment request. This field is used to ensure that
-- there are no duplicates in the database. This trick is needed because
-- PostgreSQL has a limitiation of 2712 bytes for unique index rows.
payment_request_hash BLOB UNIQUE,
-- The invoice state. -- The invoice state.
state SMALLINT NOT NULL, state SMALLINT NOT NULL,
@@ -58,6 +81,7 @@ CREATE INDEX IF NOT EXISTS invoices_preimage_idx ON invoices(preimage);
CREATE INDEX IF NOT EXISTS invoices_payment_addr_idx ON invoices(payment_addr); CREATE INDEX IF NOT EXISTS invoices_payment_addr_idx ON invoices(payment_addr);
CREATE INDEX IF NOT EXISTS invoices_state_idx ON invoices(state); CREATE INDEX IF NOT EXISTS invoices_state_idx ON invoices(state);
CREATE INDEX IF NOT EXISTS invoices_created_at_idx ON invoices(created_at); CREATE INDEX IF NOT EXISTS invoices_created_at_idx ON invoices(created_at);
CREATE INDEX IF NOT EXISTS invoices_settled_at_idx ON invoices(settled_at);
-- invoice_features contains the feature bits of an invoice. -- invoice_features contains the feature bits of an invoice.
CREATE TABLE IF NOT EXISTS invoice_features ( CREATE TABLE IF NOT EXISTS invoice_features (
@@ -65,7 +89,7 @@ CREATE TABLE IF NOT EXISTS invoice_features (
feature INTEGER NOT NULL, feature INTEGER NOT NULL,
-- The invoice id this feature belongs to. -- The invoice id this feature belongs to.
invoice_id INTEGER NOT NULL REFERENCES invoices(id), invoice_id BIGINT NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
-- The feature bit is unique per invoice. -- The feature bit is unique per invoice.
UNIQUE (feature, invoice_id) UNIQUE (feature, invoice_id)
@@ -78,15 +102,15 @@ CREATE INDEX IF NOT EXISTS invoice_feature_invoice_id_idx ON invoice_features(in
CREATE TABLE IF NOT EXISTS invoice_htlcs ( CREATE TABLE IF NOT EXISTS invoice_htlcs (
-- The id for this htlc. Used in foreign keys instead of the -- The id for this htlc. Used in foreign keys instead of the
-- htlc_id/chan_id combination. -- htlc_id/chan_id combination.
id INTEGER PRIMARY KEY, id BIGINT PRIMARY KEY,
-- Short chan id indicating the htlc's origin. uint64 stored as text.
chan_id TEXT NOT NULL,
-- The uint64 htlc id. This field is a counter so it is safe to store it as -- The uint64 htlc id. This field is a counter so it is safe to store it as
-- int64 in the database. The application layer must check that there is no -- int64 in the database. The application layer must check that there is no
-- overflow when storing/loading this column. -- overflow when storing/loading this column.
htlc_id BIGINT NOT NULL, htlc_id BIGINT NOT NULL,
-- Short chan id indicating the htlc's origin. uint64 stored as text.
chan_id TEXT NOT NULL,
-- The htlc's amount in millisatoshis. -- The htlc's amount in millisatoshis.
amount_msat BIGINT NOT NULL, amount_msat BIGINT NOT NULL,
@@ -110,10 +134,10 @@ CREATE TABLE IF NOT EXISTS invoice_htlcs (
resolve_time TIMESTAMP, resolve_time TIMESTAMP,
-- The id of the invoice this htlc belongs to. -- The id of the invoice this htlc belongs to.
invoice_id INTEGER NOT NULL REFERENCES invoices(id), invoice_id BIGINT NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
-- The htlc_id and chan_id identify the htlc. -- The htlc_id and chan_id identify the htlc.
UNIQUE (htlc_id, chan_id) UNIQUE (chan_id, htlc_id)
); );
CREATE INDEX IF NOT EXISTS invoice_htlc_invoice_id_idx ON invoice_htlcs(invoice_id); CREATE INDEX IF NOT EXISTS invoice_htlc_invoice_id_idx ON invoice_htlcs(invoice_id);
@@ -129,26 +153,8 @@ CREATE TABLE IF NOT EXISTS invoice_htlc_custom_records (
value BLOB NOT NULL, value BLOB NOT NULL,
-- The htlc id this record belongs to. -- The htlc id this record belongs to.
htlc_id BIGINT NOT NULL REFERENCES invoice_htlcs(id) htlc_id BIGINT NOT NULL REFERENCES invoice_htlcs(id) ON DELETE CASCADE
); );
CREATE INDEX IF NOT EXISTS invoice_htlc_custom_records_htlc_id_idx ON invoice_htlc_custom_records(htlc_id); CREATE INDEX IF NOT EXISTS invoice_htlc_custom_records_htlc_id_idx ON invoice_htlc_custom_records(htlc_id);
-- invoice_payments contains the information of a settled invoice payment.
CREATE TABLE IF NOT EXISTS invoice_payments (
-- The id for this invoice payment. Translates to SettleIndex.
id INTEGER PRIMARY KEY,
-- When the payment was settled.
settled_at TIMESTAMP NOT NULL,
-- The amount of the payment in millisatoshis. This is the sum of all the
-- the htlcs settled for this payment.
amount_paid_msat BIGINT NOT NULL,
-- The invoice id this payment is for.
invoice_id INTEGER NOT NULL REFERENCES invoices(id)
);
CREATE INDEX IF NOT EXISTS invoice_payments_settled_at_idx ON invoice_payments(settled_at);
CREATE INDEX IF NOT EXISTS invoice_payments_invoice_id_idx ON invoice_payments(invoice_id);
@@ -1,5 +1,6 @@
-- amp_invoices_payments -- amp_sub_invoices holds all AMP sub-invoices that belong to a given parent
CREATE TABLE IF NOT EXISTS amp_invoice_payments ( -- invoice.
CREATE TABLE IF NOT EXISTS amp_sub_invoices (
-- The set id identifying the payment. -- The set id identifying the payment.
set_id BLOB PRIMARY KEY, set_id BLOB PRIMARY KEY,
@@ -10,26 +11,35 @@ CREATE TABLE IF NOT EXISTS amp_invoice_payments (
-- Timestamp of when the first htlc for this payment was accepted. -- Timestamp of when the first htlc for this payment was accepted.
created_at TIMESTAMP NOT NULL, created_at TIMESTAMP NOT NULL,
-- If settled, the invoice payment related to this set id. -- When the invoice was settled.
settled_index INTEGER REFERENCES invoice_payments(id), settled_at TIMESTAMP,
-- The invoice id this set id is related to. -- If settled, the index is set to the current_value+1 of the settle_index
invoice_id INTEGER NOT NULL REFERENCES invoices(id) -- seuqence in the invoice_sequences table. If not settled, then it is set
-- to NULL.
settle_index BIGINT,
-- The id of the parent invoice this AMP invoice belongs to.
invoice_id BIGINT NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
-- A unique constraint on the set_id and invoice_id is needed to ensure that
-- we don't have two AMP invoices for the same invoice.
UNIQUE (set_id, invoice_id)
); );
CREATE INDEX IF NOT EXISTS amp_invoice_payments_invoice_id_idx ON amp_invoice_payments(invoice_id); CREATE INDEX IF NOT EXISTS amp_sub_invoices_invoice_id_idx ON amp_sub_invoices(invoice_id);
-- amp_invoice_htlcs contains the complementary information for an htlc related -- amp_invoice_htlcs contains the complementary information for an htlc related
-- to an AMP invoice. -- to an AMP invoice.
CREATE TABLE IF NOT EXISTS amp_invoice_htlcs ( CREATE TABLE IF NOT EXISTS amp_sub_invoice_htlcs (
-- The invoice id this entry is related to.
invoice_id BIGINT NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
-- The set id identifying the payment this htlc belongs to. -- The set id identifying the payment this htlc belongs to.
set_id BLOB NOT NULL REFERENCES amp_invoice_payments(set_id), set_id BLOB NOT NULL REFERENCES amp_sub_invoices(set_id) ON DELETE CASCADE,
-- The id of the htlc this entry blongs to. -- The id of the htlc this entry blongs to.
htlc_id BIGINT NOT NULL REFERENCES invoice_htlcs(id), htlc_id BIGINT NOT NULL REFERENCES invoice_htlcs(id) ON DELETE CASCADE,
-- The invoice id this entry is related to.
invoice_id INTEGER NOT NULL REFERENCES invoices(id),
-- The root share for this amp htlc. -- The root share for this amp htlc.
root_share BLOB NOT NULL, root_share BLOB NOT NULL,
@@ -48,7 +58,7 @@ CREATE TABLE IF NOT EXISTS amp_invoice_htlcs (
preimage BLOB preimage BLOB
); );
CREATE INDEX IF NOT EXISTS amp_htlcs_set_id_idx ON amp_invoice_htlcs(set_id); CREATE INDEX IF NOT EXISTS amp_htlcs_invoice_id_idx ON amp_sub_invoice_htlcs(invoice_id);
CREATE INDEX IF NOT EXISTS amp_htlcs_invoice_id_idx ON amp_invoice_htlcs(invoice_id); CREATE INDEX IF NOT EXISTS amp_htlcs_set_id_idx ON amp_sub_invoice_htlcs(set_id);
CREATE INDEX IF NOT EXISTS amp_htlcs_htlc_id_idx ON amp_invoice_htlcs(htlc_id); CREATE INDEX IF NOT EXISTS amp_htlcs_htlc_id_idx ON amp_sub_invoice_htlcs(htlc_id);
@@ -1,9 +1,6 @@
DROP TABLE IF EXISTS invoice_events; DROP TABLE IF EXISTS invoice_events;
DROP INDEX IF EXISTS invoice_events_added_at_idx;
DROP INDEX IF EXISTS invoice_events_created_at_idx;
DROP INDEX IF EXISTS invoice_events_invoice_id_idx;
DROP INDEX IF EXISTS invoice_events_htlc_id_idx;
DROP INDEX IF EXISTS invoice_events_set_id_idx;
DROP INDEX IF EXISTS invoice_events_event_type_idx; DROP INDEX IF EXISTS invoice_events_event_type_idx;
DROP INDEX IF EXISTS invoice_events_invoice_id_idx;
DROP INDEX IF EXISTS invoice_events_set_id_idx;
DROP TABLE IF EXISTS invoice_event_types; DROP TABLE IF EXISTS invoice_event_types;
@@ -1,59 +1,51 @@
-- invoice_event_types stores the different types of events that can be emitted -- invoice_event_types stores the different types of invoice events.
-- for invoices.
CREATE TABLE IF NOT EXISTS invoice_event_types( CREATE TABLE IF NOT EXISTS invoice_event_types(
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
description TEXT NOT NULL description TEXT NOT NULL
); );
-- invoice_events stores all events related to the node invoices. -- invoice_event_types defines the different types of invoice events.
CREATE TABLE IF NOT EXISTS invoice_events (
id INTEGER PRIMARY KEY,
-- created_at is the creation time of this event.
created_at TIMESTAMP NOT NULL,
-- invoice_id is the reference to the invoice this event was emitted for.
invoice_id INTEGER NOT NULL REFERENCES invoices(id),
-- htlc_id is the reference to the htlc this event was emitted for, may be
-- null.
htlc_id BIGINT REFERENCES invoice_htlcs(htlc_id),
-- set_id is the reference to the set_id this event was emitted for, may be
-- null.
set_id BLOB NOT NULL REFERENCES amp_invoice_payments(set_id),
-- event_type is the type of this event.
event_type INTEGER NOT NULL REFERENCES invoice_event_types(id),
-- event_metadata is a TLV encoding any relevant information for this kind
-- of events.
event_metadata BLOB
);
CREATE INDEX IF NOT EXISTS invoice_events_created_at_idx ON invoice_events(created_at);
CREATE INDEX IF NOT EXISTS invoice_events_invoice_id_idx ON invoice_events(invoice_id);
CREATE INDEX IF NOT EXISTS invoice_events_htlc_id_idx ON invoice_events(htlc_id);
CREATE INDEX IF NOT EXISTS invoice_events_set_id_idx ON invoice_events(set_id);
CREATE INDEX IF NOT EXISTS invoice_events_event_type_idx ON invoice_events(event_type);
-- invoice_event_types defines the different types of events that can be emitted
-- for an invoice.
INSERT INTO invoice_event_types (id, description) INSERT INTO invoice_event_types (id, description)
VALUES VALUES
-- invoice_created is the event emitted when an invoice is created. -- invoice_created is the event type used when an invoice is created.
(0, 'invoice_created'), (0, 'invoice_created'),
-- invoice_canceled is the event emitted when an invoice is canceled. -- invoice_canceled is the event type used when an invoice is canceled.
(1, "invoice_canceled"), (1, 'invoice_canceled'),
-- invoice_settled is the event emitted when an invoice is settled. -- invoice_settled is the event type used when an invoice is settled.
(2, "invoice_settled"), (2, 'invoice_settled'),
-- setid_created is the event emitted when the first htlc for the set_id is -- setid_created is the event type used when an AMP sub invoice
-- received. -- corresponding to the set_id is created.
(3, "setid_created"), (3, 'setid_created'),
-- setid_canceled is the event emitted when the set_id is canceled. -- setid_canceled is the event type used when an AMP sub invoice
(4, "setid_canceled"), -- corresponding to the set_id is canceled.
-- setid_settled is the event emitted when the set_id is settled. (4, 'setid_canceled'),
(5, "setid_settled"); -- setid_settled is the event type used when an AMP sub invoice
-- corresponding to the set_id is settled.
(5, 'setid_settled');
-- invoice_events stores all major events related to the node's invoices and
-- AMP sub invoices. This table can be used to create a historical view of what
-- happened to the node's invoices.
CREATE TABLE IF NOT EXISTS invoice_events (
id BIGINT PRIMARY KEY,
-- added_at is the timestamp when this event was added.
added_at TIMESTAMP NOT NULL,
-- event_type is the type of this event.
event_type INTEGER NOT NULL REFERENCES invoice_event_types(id) ON DELETE CASCADE,
-- invoice_id is the reference to the invoice this event was added for.
invoice_id BIGINT NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
-- set_id is the reference to the AMP sub invoice this event was added for.
-- May be NULL if the event is not related to an AMP sub invoice.
set_id BLOB REFERENCES amp_sub_invoices(set_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS invoice_events_added_at_idx ON invoice_events(added_at);
CREATE INDEX IF NOT EXISTS invoice_events_event_type_idx ON invoice_events(event_type);
CREATE INDEX IF NOT EXISTS invoice_events_invoice_id_idx ON invoice_events(invoice_id);
CREATE INDEX IF NOT EXISTS invoice_events_set_id_idx ON invoice_events(set_id);
+42 -42
View File
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.18.0 // sqlc v1.25.0
package sqlc package sqlc
@@ -9,50 +9,52 @@ import (
"time" "time"
) )
type AmpInvoiceHtlc struct { type AmpSubInvoice struct {
SetID []byte
State int16
CreatedAt time.Time
SettledAt sql.NullTime
SettleIndex sql.NullInt64
InvoiceID int64
}
type AmpSubInvoiceHtlc struct {
InvoiceID int64
SetID []byte SetID []byte
HtlcID int64 HtlcID int64
InvoiceID int32
RootShare []byte RootShare []byte
ChildIndex int64 ChildIndex int64
Hash []byte Hash []byte
Preimage []byte Preimage []byte
} }
type AmpInvoicePayment struct {
SetID []byte
State int16
CreatedAt time.Time
SettledIndex sql.NullInt32
InvoiceID int32
}
type Invoice struct { type Invoice struct {
ID int32 ID int64
Hash []byte Hash []byte
Preimage []byte Preimage []byte
Memo sql.NullString SettleIndex sql.NullInt64
AmountMsat int64 SettledAt sql.NullTime
CltvDelta sql.NullInt32 Memo sql.NullString
Expiry int32 AmountMsat int64
PaymentAddr []byte CltvDelta sql.NullInt32
PaymentRequest sql.NullString Expiry int32
State int16 PaymentAddr []byte
AmountPaidMsat int64 PaymentRequest sql.NullString
IsAmp bool PaymentRequestHash []byte
IsHodl bool State int16
IsKeysend bool AmountPaidMsat int64
CreatedAt time.Time IsAmp bool
IsHodl bool
IsKeysend bool
CreatedAt time.Time
} }
type InvoiceEvent struct { type InvoiceEvent struct {
ID int32 ID int64
CreatedAt time.Time AddedAt time.Time
InvoiceID int32 EventType int32
HtlcID sql.NullInt64 InvoiceID int64
SetID []byte SetID []byte
EventType int32
EventMetadata []byte
} }
type InvoiceEventType struct { type InvoiceEventType struct {
@@ -62,13 +64,13 @@ type InvoiceEventType struct {
type InvoiceFeature struct { type InvoiceFeature struct {
Feature int32 Feature int32
InvoiceID int32 InvoiceID int64
} }
type InvoiceHtlc struct { type InvoiceHtlc struct {
ID int32 ID int64
HtlcID int64
ChanID string ChanID string
HtlcID int64
AmountMsat int64 AmountMsat int64
TotalMppMsat sql.NullInt64 TotalMppMsat sql.NullInt64
AcceptHeight int32 AcceptHeight int32
@@ -76,7 +78,7 @@ type InvoiceHtlc struct {
ExpiryHeight int32 ExpiryHeight int32
State int16 State int16
ResolveTime sql.NullTime ResolveTime sql.NullTime
InvoiceID int32 InvoiceID int64
} }
type InvoiceHtlcCustomRecord struct { type InvoiceHtlcCustomRecord struct {
@@ -85,9 +87,7 @@ type InvoiceHtlcCustomRecord struct {
HtlcID int64 HtlcID int64
} }
type InvoicePayment struct { type InvoiceSequence struct {
ID int32 Name string
SettledAt time.Time CurrentValue int64
AmountPaidMsat int64
InvoiceID int32
} }
+26 -29
View File
@@ -1,51 +1,48 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.18.0 // sqlc v1.25.0
package sqlc package sqlc
import ( import (
"context" "context"
"database/sql"
) )
type Querier interface { type Querier interface {
DeleteAMPHTLCCustomRecords(ctx context.Context, invoiceID int32) error DeleteCanceledInvoices(ctx context.Context) (sql.Result, error)
DeleteAMPHTLCs(ctx context.Context, invoiceID int32) error DeleteInvoice(ctx context.Context, arg DeleteInvoiceParams) (sql.Result, error)
DeleteAMPInvoiceHTLC(ctx context.Context, setID []byte) error FetchAMPSubInvoiceHTLCs(ctx context.Context, arg FetchAMPSubInvoiceHTLCsParams) ([]FetchAMPSubInvoiceHTLCsRow, error)
DeleteInvoice(ctx context.Context, arg DeleteInvoiceParams) error FetchAMPSubInvoices(ctx context.Context, arg FetchAMPSubInvoicesParams) ([]AmpSubInvoice, error)
DeleteInvoiceEvents(ctx context.Context, invoiceID int32) error FetchSettledAMPSubInvoices(ctx context.Context, arg FetchSettledAMPSubInvoicesParams) ([]FetchSettledAMPSubInvoicesRow, error)
DeleteInvoiceFeatures(ctx context.Context, invoiceID int32) error
DeleteInvoiceHTLC(ctx context.Context, htlcID int64) error
DeleteInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int32) error
DeleteInvoiceHTLCs(ctx context.Context, invoiceID int32) error
FilterInvoicePayments(ctx context.Context, arg FilterInvoicePaymentsParams) ([]FilterInvoicePaymentsRow, error)
FilterInvoices(ctx context.Context, arg FilterInvoicesParams) ([]Invoice, error) FilterInvoices(ctx context.Context, arg FilterInvoicesParams) ([]Invoice, error)
GetAMPInvoiceHTLCsByInvoiceID(ctx context.Context, invoiceID int32) ([]AmpInvoiceHtlc, error) GetAMPInvoiceID(ctx context.Context, setID []byte) (int64, error)
GetAMPInvoiceHTLCsBySetID(ctx context.Context, setID []byte) ([]AmpInvoiceHtlc, error)
// This method may return more than one invoice if filter using multiple fields // This method may return more than one invoice if filter using multiple fields
// from different invoices. It is the caller's responsibility to ensure that // from different invoices. It is the caller's responsibility to ensure that
// we bubble up an error in those cases. // we bubble up an error in those cases.
GetInvoice(ctx context.Context, arg GetInvoiceParams) ([]Invoice, error) GetInvoice(ctx context.Context, arg GetInvoiceParams) ([]Invoice, error)
GetInvoiceFeatures(ctx context.Context, invoiceID int32) ([]InvoiceFeature, error) GetInvoiceFeatures(ctx context.Context, invoiceID int64) ([]InvoiceFeature, error)
GetInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int32) ([]GetInvoiceHTLCCustomRecordsRow, error) GetInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int64) ([]GetInvoiceHTLCCustomRecordsRow, error)
GetInvoiceHTLCs(ctx context.Context, invoiceID int32) ([]InvoiceHtlc, error) GetInvoiceHTLCs(ctx context.Context, invoiceID int64) ([]InvoiceHtlc, error)
GetInvoicePayments(ctx context.Context, invoiceID int32) ([]InvoicePayment, error) InsertAMPSubInvoiceHTLC(ctx context.Context, arg InsertAMPSubInvoiceHTLCParams) error
GetSetIDHTLCsCustomRecords(ctx context.Context, setID []byte) ([]GetSetIDHTLCsCustomRecordsRow, error) InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (int64, error)
InsertAMPInvoiceHTLC(ctx context.Context, arg InsertAMPInvoiceHTLCParams) error
InsertAMPInvoicePayment(ctx context.Context, arg InsertAMPInvoicePaymentParams) error
InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (int32, error)
InsertInvoiceEvent(ctx context.Context, arg InsertInvoiceEventParams) error
InsertInvoiceFeature(ctx context.Context, arg InsertInvoiceFeatureParams) error InsertInvoiceFeature(ctx context.Context, arg InsertInvoiceFeatureParams) error
InsertInvoiceHTLC(ctx context.Context, arg InsertInvoiceHTLCParams) error InsertInvoiceHTLC(ctx context.Context, arg InsertInvoiceHTLCParams) (int64, error)
InsertInvoiceHTLCCustomRecord(ctx context.Context, arg InsertInvoiceHTLCCustomRecordParams) error InsertInvoiceHTLCCustomRecord(ctx context.Context, arg InsertInvoiceHTLCCustomRecordParams) error
InsertInvoicePayment(ctx context.Context, arg InsertInvoicePaymentParams) (int32, error) NextInvoiceSettleIndex(ctx context.Context) (int64, error)
SelectAMPInvoicePayments(ctx context.Context, arg SelectAMPInvoicePaymentsParams) ([]SelectAMPInvoicePaymentsRow, error) OnAMPSubInvoiceCanceled(ctx context.Context, arg OnAMPSubInvoiceCanceledParams) error
SelectInvoiceEvents(ctx context.Context, arg SelectInvoiceEventsParams) ([]InvoiceEvent, error) OnAMPSubInvoiceCreated(ctx context.Context, arg OnAMPSubInvoiceCreatedParams) error
UpdateAMPInvoiceHTLC(ctx context.Context, arg UpdateAMPInvoiceHTLCParams) error OnAMPSubInvoiceSettled(ctx context.Context, arg OnAMPSubInvoiceSettledParams) error
UpdateAMPPayment(ctx context.Context, arg UpdateAMPPaymentParams) error OnInvoiceCanceled(ctx context.Context, arg OnInvoiceCanceledParams) error
UpdateInvoice(ctx context.Context, arg UpdateInvoiceParams) error OnInvoiceCreated(ctx context.Context, arg OnInvoiceCreatedParams) error
OnInvoiceSettled(ctx context.Context, arg OnInvoiceSettledParams) error
UpdateAMPSubInvoiceHTLCPreimage(ctx context.Context, arg UpdateAMPSubInvoiceHTLCPreimageParams) (sql.Result, error)
UpdateAMPSubInvoiceState(ctx context.Context, arg UpdateAMPSubInvoiceStateParams) error
UpdateInvoiceAmountPaid(ctx context.Context, arg UpdateInvoiceAmountPaidParams) (sql.Result, error)
UpdateInvoiceHTLC(ctx context.Context, arg UpdateInvoiceHTLCParams) error UpdateInvoiceHTLC(ctx context.Context, arg UpdateInvoiceHTLCParams) error
UpdateInvoiceHTLCs(ctx context.Context, arg UpdateInvoiceHTLCsParams) error UpdateInvoiceHTLCs(ctx context.Context, arg UpdateInvoiceHTLCsParams) error
UpdateInvoiceState(ctx context.Context, arg UpdateInvoiceStateParams) (sql.Result, error)
UpsertAMPSubInvoice(ctx context.Context, arg UpsertAMPSubInvoiceParams) (sql.Result, error)
} }
var _ Querier = (*Queries)(nil) var _ Querier = (*Queries)(nil)
+56 -68
View File
@@ -1,79 +1,67 @@
-- name: InsertAMPInvoicePayment :exec -- name: UpsertAMPSubInvoice :execresult
INSERT INTO amp_invoice_payments ( INSERT INTO amp_sub_invoices (
set_id, state, created_at, settled_index, invoice_id set_id, state, created_at, invoice_id
) VALUES ( ) VALUES (
$1, $2, $3, $4, $5 $1, $2, $3, $4
); ) ON CONFLICT (set_id, invoice_id) DO NOTHING;
-- name: SelectAMPInvoicePayments :many -- name: GetAMPInvoiceID :one
SELECT aip.*, ip.* SELECT invoice_id FROM amp_sub_invoices WHERE set_id = $1;
FROM amp_invoice_payments aip LEFT JOIN invoice_payments ip ON aip.settled_index = ip.id
WHERE (
set_id = sqlc.narg('set_id') OR
sqlc.narg('set_id') IS NULL
) AND (
aip.settled_index = sqlc.narg('settled_index') OR
sqlc.narg('settled_index') IS NULL
) AND (
aip.invoice_id = sqlc.narg('invoice_id') OR
sqlc.narg('invoice_id') IS NULL
);
-- name: UpdateAMPPayment :exec -- name: UpdateAMPSubInvoiceState :exec
UPDATE amp_invoice_payments UPDATE amp_sub_invoices
SET state = $1, settled_index = $2 SET state = $2,
WHERE state = 0 AND ( settle_index = COALESCE(settle_index, $3),
set_id = sqlc.narg('set_id') OR settled_at = COALESCE(settled_at, $4)
sqlc.narg('set_id') IS NULL WHERE set_id = $1;
) AND (
invoice_id = sqlc.narg('invoice_id') OR
sqlc.narg('invoice_id') IS NULL
);
-- name: InsertAMPInvoiceHTLC :exec -- name: InsertAMPSubInvoiceHTLC :exec
INSERT INTO amp_invoice_htlcs ( INSERT INTO amp_sub_invoice_htlcs (
set_id, htlc_id, root_share, child_index, hash, preimage invoice_id, set_id, htlc_id, root_share, child_index, hash, preimage
) VALUES ( ) VALUES (
$1, $2, $3, $4, $5, $6 $1, $2, $3, $4, $5, $6, $7
); );
-- name: GetAMPInvoiceHTLCsBySetID :many -- name: FetchAMPSubInvoices :many
SELECT * SELECT *
FROM amp_invoice_htlcs FROM amp_sub_invoices
WHERE set_id = $1; WHERE invoice_id = $1
AND (
set_id = sqlc.narg('set_id') OR
sqlc.narg('set_id') IS NULL
);
-- name: GetAMPInvoiceHTLCsByInvoiceID :many -- name: FetchAMPSubInvoiceHTLCs :many
SELECT * SELECT
FROM amp_invoice_htlcs amp.set_id, amp.root_share, amp.child_index, amp.hash, amp.preimage,
WHERE invoice_id = $1; invoice_htlcs.*
FROM amp_sub_invoice_htlcs amp
INNER JOIN invoice_htlcs ON amp.htlc_id = invoice_htlcs.id
WHERE amp.invoice_id = $1
AND (
set_id = sqlc.narg('set_id') OR
sqlc.narg('set_id') IS NULL
);
-- name: GetSetIDHTLCsCustomRecords :many -- name: FetchSettledAMPSubInvoices :many
SELECT ihcr.htlc_id, key, value SELECT
FROM amp_invoice_htlcs aih JOIN invoice_htlc_custom_records ihcr ON aih.id=ihcr.htlc_id a.set_id,
WHERE aih.set_id = $1; a.settle_index as amp_settle_index,
a.settled_at as amp_settled_at,
-- name: UpdateAMPInvoiceHTLC :exec i.*
UPDATE amp_invoice_htlcs FROM amp_sub_invoices a
SET preimage = $1 INNER JOIN invoices i ON a.invoice_id = i.id
WHERE htlc_id = $2; WHERE (
a.settle_index >= sqlc.narg('settle_index_get') OR
-- name: DeleteAMPHTLCCustomRecords :exec sqlc.narg('settle_index_get') IS NULL
WITH htlc_ids AS ( ) AND (
SELECT htlc_id a.settle_index <= sqlc.narg('settle_index_let') OR
FROM amp_invoice_htlcs sqlc.narg('settle_index_let') IS NULL
WHERE invoice_id = $1 );
)
DELETE
FROM invoice_htlc_custom_records
WHERE htlc_id IN (SELECT id FROM htlc_ids);
-- name: DeleteAMPHTLCs :exec
DELETE
FROM amp_invoice_htlcs
WHERE invoice_id = $1;
-- name: DeleteAMPInvoiceHTLC :exec
DELETE
FROM amp_invoice_htlcs
WHERE set_id = $1;
-- name: UpdateAMPSubInvoiceHTLCPreimage :execresult
UPDATE amp_sub_invoice_htlcs AS a
SET preimage = $4
WHERE a.invoice_id = $1 AND a.set_id = $2 AND a.htlc_id = (
SELECT id FROM invoice_htlcs AS i WHERE i.htlc_id = $3
);
+36 -30
View File
@@ -1,35 +1,41 @@
-- name: InsertInvoiceEvent :exec -- name: OnInvoiceCreated :exec
INSERT INTO invoice_events ( INSERT INTO invoice_events (
created_at, invoice_id, htlc_id, set_id, event_type, event_metadata added_at, event_type, invoice_id
) VALUES ( ) VALUES (
$1, $2, $3, $4, $5, $6 $1, 0, $2
); );
-- name: SelectInvoiceEvents :many -- name: OnInvoiceCanceled :exec
SELECT * INSERT INTO invoice_events (
FROM invoice_events added_at, event_type, invoice_id
WHERE ( ) VALUES (
invoice_id = sqlc.narg('invoice_id') OR $1, 1, $2
sqlc.narg('invoice_id') IS NULL );
) AND (
htlc_id = sqlc.narg('htlc_id') OR
sqlc.narg('htlc_id') IS NULL
) AND (
set_id = sqlc.narg('set_id') OR
sqlc.narg('set_id') IS NULL
) AND (
event_type = sqlc.narg('event_type') OR
sqlc.narg('event_type') IS NULL
) AND (
created_at >= sqlc.narg('created_after') OR
sqlc.narg('created_after') IS NULL
) AND (
created_at <= sqlc.narg('created_before') OR
sqlc.narg('created_before') IS NULL
)
LIMIT @num_limit OFFSET @num_offset;
-- name: DeleteInvoiceEvents :exec -- name: OnInvoiceSettled :exec
DELETE INSERT INTO invoice_events (
FROM invoice_events added_at, event_type, invoice_id
WHERE invoice_id = $1; ) VALUES (
$1, 2, $2
);
-- name: OnAMPSubInvoiceCreated :exec
INSERT INTO invoice_events (
added_at, event_type, invoice_id, set_id
) VALUES (
$1, 3, $2, $3
);
-- name: OnAMPSubInvoiceCanceled :exec
INSERT INTO invoice_events (
added_at, event_type, invoice_id, set_id
) VALUES (
$1, 4, $2, $3
);
-- name: OnAMPSubInvoiceSettled :exec
INSERT INTO invoice_events (
added_at, event_type, invoice_id, set_id
) VALUES (
$1, 5, $2, $3
);
+51 -83
View File
@@ -1,10 +1,10 @@
-- name: InsertInvoice :one -- name: InsertInvoice :one
INSERT INTO invoices ( INSERT INTO invoices (
hash, preimage, memo, amount_msat, cltv_delta, expiry, payment_addr, hash, preimage, memo, amount_msat, cltv_delta, expiry, payment_addr,
payment_request, state, amount_paid_msat, is_amp, is_hodl, is_keysend, payment_request, payment_request_hash, state, amount_paid_msat, is_amp,
created_at is_hodl, is_keysend, created_at
) VALUES ( ) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14 $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15
) RETURNING id; ) RETURNING id;
-- name: InsertInvoiceFeature :exec -- name: InsertInvoiceFeature :exec
@@ -19,35 +19,36 @@ SELECT *
FROM invoice_features FROM invoice_features
WHERE invoice_id = $1; WHERE invoice_id = $1;
-- name: DeleteInvoiceFeatures :exec
DELETE
FROM invoice_features
WHERE invoice_id = $1;
-- This method may return more than one invoice if filter using multiple fields -- This method may return more than one invoice if filter using multiple fields
-- from different invoices. It is the caller's responsibility to ensure that -- from different invoices. It is the caller's responsibility to ensure that
-- we bubble up an error in those cases. -- we bubble up an error in those cases.
-- name: GetInvoice :many -- name: GetInvoice :many
SELECT * SELECT i.*
FROM invoices FROM invoices i
LEFT JOIN amp_sub_invoices a on i.id = a.invoice_id
WHERE ( WHERE (
id = sqlc.narg('add_index') OR i.id = sqlc.narg('add_index') OR
sqlc.narg('add_index') IS NULL sqlc.narg('add_index') IS NULL
) AND ( ) AND (
hash = sqlc.narg('hash') OR i.hash = sqlc.narg('hash') OR
sqlc.narg('hash') IS NULL sqlc.narg('hash') IS NULL
) AND ( ) AND (
preimage = sqlc.narg('preimage') OR i.preimage = sqlc.narg('preimage') OR
sqlc.narg('preimage') IS NULL sqlc.narg('preimage') IS NULL
) AND ( ) AND (
payment_addr = sqlc.narg('payment_addr') OR i.payment_addr = sqlc.narg('payment_addr') OR
sqlc.narg('payment_addr') IS NULL sqlc.narg('payment_addr') IS NULL
) AND (
a.set_id = sqlc.narg('set_id') OR
sqlc.narg('set_id') IS NULL
) )
GROUP BY i.id
LIMIT 2; LIMIT 2;
-- name: FilterInvoices :many -- name: FilterInvoices :many
SELECT * SELECT
invoices.*
FROM invoices FROM invoices
WHERE ( WHERE (
id >= sqlc.narg('add_index_get') OR id >= sqlc.narg('add_index_get') OR
@@ -55,6 +56,12 @@ WHERE (
) AND ( ) AND (
id <= sqlc.narg('add_index_let') OR id <= sqlc.narg('add_index_let') OR
sqlc.narg('add_index_let') IS NULL sqlc.narg('add_index_let') IS NULL
) AND (
settle_index >= sqlc.narg('settle_index_get') OR
sqlc.narg('settle_index_get') IS NULL
) AND (
settle_index <= sqlc.narg('settle_index_let') OR
sqlc.narg('settle_index_let') IS NULL
) AND ( ) AND (
state = sqlc.narg('state') OR state = sqlc.narg('state') OR
sqlc.narg('state') IS NULL sqlc.narg('state') IS NULL
@@ -81,12 +88,25 @@ ORDER BY
END DESC END DESC
LIMIT @num_limit OFFSET @num_offset; LIMIT @num_limit OFFSET @num_offset;
-- name: UpdateInvoice :exec -- name: UpdateInvoiceState :execresult
UPDATE invoices UPDATE invoices
SET preimage=$2, state=$3, amount_paid_msat=$4 SET state = $2,
WHERE id=$1; preimage = COALESCE(preimage, $3),
settle_index = COALESCE(settle_index, $4),
settled_at = COALESCE(settled_at, $5)
WHERE id = $1;
-- name: DeleteInvoice :exec -- name: UpdateInvoiceAmountPaid :execresult
UPDATE invoices
SET amount_paid_msat = $2
WHERE id = $1;
-- name: NextInvoiceSettleIndex :one
UPDATE invoice_sequences SET current_value = current_value + 1
WHERE name = 'settle_index'
RETURNING current_value;
-- name: DeleteInvoice :execresult
DELETE DELETE
FROM invoices FROM invoices
WHERE ( WHERE (
@@ -96,20 +116,25 @@ WHERE (
hash = sqlc.narg('hash') OR hash = sqlc.narg('hash') OR
sqlc.narg('hash') IS NULL sqlc.narg('hash') IS NULL
) AND ( ) AND (
preimage = sqlc.narg('preimage') OR settle_index = sqlc.narg('settle_index') OR
sqlc.narg('preimage') IS NULL sqlc.narg('settle_index') IS NULL
) AND ( ) AND (
payment_addr = sqlc.narg('payment_addr') OR payment_addr = sqlc.narg('payment_addr') OR
sqlc.narg('payment_addr') IS NULL sqlc.narg('payment_addr') IS NULL
); );
-- name: InsertInvoiceHTLC :exec -- name: DeleteCanceledInvoices :execresult
DELETE
FROM invoices
WHERE state = 2;
-- name: InsertInvoiceHTLC :one
INSERT INTO invoice_htlcs ( INSERT INTO invoice_htlcs (
htlc_id, chan_id, amount_msat, total_mpp_msat, accept_height, accept_time, htlc_id, chan_id, amount_msat, total_mpp_msat, accept_height, accept_time,
expiry_height, state, resolve_time, invoice_id expiry_height, state, resolve_time, invoice_id
) VALUES ( ) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10 $1, $2, $3, $4, $5, $6, $7, $8, $9, $10
); ) RETURNING id;
-- name: GetInvoiceHTLCs :many -- name: GetInvoiceHTLCs :many
SELECT * SELECT *
@@ -118,25 +143,14 @@ WHERE invoice_id = $1;
-- name: UpdateInvoiceHTLC :exec -- name: UpdateInvoiceHTLC :exec
UPDATE invoice_htlcs UPDATE invoice_htlcs
SET state=$2, resolve_time=$3 SET state=$4, resolve_time=$5
WHERE id = $1; WHERE htlc_id = $1 AND chan_id = $2 AND invoice_id = $3;
-- name: UpdateInvoiceHTLCs :exec -- name: UpdateInvoiceHTLCs :exec
UPDATE invoice_htlcs UPDATE invoice_htlcs
SET state=$2, resolve_time=$3 SET state=$2, resolve_time=$3
WHERE invoice_id = $1 AND resolve_time IS NULL; WHERE invoice_id = $1 AND resolve_time IS NULL;
-- name: DeleteInvoiceHTLC :exec
DELETE
FROM invoice_htlcs
WHERE htlc_id = $1;
-- name: DeleteInvoiceHTLCs :exec
DELETE
FROM invoice_htlcs
WHERE invoice_id = $1;
-- name: InsertInvoiceHTLCCustomRecord :exec -- name: InsertInvoiceHTLCCustomRecord :exec
INSERT INTO invoice_htlc_custom_records ( INSERT INTO invoice_htlc_custom_records (
key, value, htlc_id key, value, htlc_id
@@ -148,49 +162,3 @@ INSERT INTO invoice_htlc_custom_records (
SELECT ihcr.htlc_id, key, value SELECT ihcr.htlc_id, key, value
FROM invoice_htlcs ih JOIN invoice_htlc_custom_records ihcr ON ih.id=ihcr.htlc_id FROM invoice_htlcs ih JOIN invoice_htlc_custom_records ihcr ON ih.id=ihcr.htlc_id
WHERE ih.invoice_id = $1; WHERE ih.invoice_id = $1;
-- name: DeleteInvoiceHTLCCustomRecords :exec
WITH htlc_ids AS (
SELECT ih.id
FROM invoice_htlcs ih JOIN invoice_htlc_custom_records ihcr ON ih.id=ihcr.htlc_id
WHERE ih.invoice_id = $1
)
DELETE
FROM invoice_htlc_custom_records
WHERE htlc_id IN (SELECT id FROM htlc_ids);
-- name: InsertInvoicePayment :one
INSERT INTO invoice_payments (
invoice_id, amount_paid_msat, settled_at
) VALUES (
$1, $2, $3
) RETURNING id;
-- name: GetInvoicePayments :many
SELECT *
FROM invoice_payments
WHERE invoice_id = $1;
-- name: FilterInvoicePayments :many
SELECT
ip.id AS settle_index, ip.amount_paid_msat, ip.settled_at AS settle_date,
i.*
FROM invoice_payments ip JOIN invoices i ON ip.invoice_id = i.id
WHERE (
ip.id >= sqlc.narg('settle_index_get') OR
sqlc.narg('settle_index_get') IS NULL
) AND (
ip.settled_at >= sqlc.narg('settled_after') OR
sqlc.narg('settled_after') IS NULL
)
ORDER BY
CASE
WHEN sqlc.narg('reverse') = FALSE THEN ip.id
ELSE NULL
END ASC,
CASE
WHEN sqlc.narg('reverse') = TRUE THEN ip.id
ELSE NULL
END DESC
LIMIT @num_limit OFFSET @num_offset;
+7
View File
@@ -1,3 +1,5 @@
//go:build !js && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64))
package sqldb package sqldb
import ( import (
@@ -46,6 +48,11 @@ func parseSqliteError(sqliteErr *sqlite.Error) error {
DBError: sqliteErr, DBError: sqliteErr,
} }
case sqlite3.SQLITE_CONSTRAINT_PRIMARYKEY:
return &ErrSQLUniqueConstraintViolation{
DBError: sqliteErr,
}
// Database is currently busy, so we'll need to try again. // Database is currently busy, so we'll need to try again.
case sqlite3.SQLITE_BUSY: case sqlite3.SQLITE_BUSY:
return &ErrSerializationError{ return &ErrSerializationError{
+86
View File
@@ -0,0 +1,86 @@
//go:build js || (windows && (arm || 386)) || (linux && (ppc64 || mips || mipsle || mips64))
package sqldb
import (
"errors"
"fmt"
"github.com/jackc/pgconn"
"github.com/jackc/pgerrcode"
)
var (
// ErrRetriesExceeded is returned when a transaction is retried more
// than the max allowed valued without a success.
ErrRetriesExceeded = errors.New("db tx retries exceeded")
)
// MapSQLError attempts to interpret a given error as a database agnostic SQL
// error.
func MapSQLError(err error) error {
// Attempt to interpret the error as a postgres error.
var pqErr *pgconn.PgError
if errors.As(err, &pqErr) {
return parsePostgresError(pqErr)
}
// Return original error if it could not be classified as a database
// specific error.
return err
}
// parsePostgresError attempts to parse a postgres error as a database agnostic
// SQL error.
func parsePostgresError(pqErr *pgconn.PgError) error {
switch pqErr.Code {
// Handle unique constraint violation error.
case pgerrcode.UniqueViolation:
return &ErrSQLUniqueConstraintViolation{
DBError: pqErr,
}
// Unable to serialize the transaction, so we'll need to try again.
case pgerrcode.SerializationFailure:
return &ErrSerializationError{
DBError: pqErr,
}
default:
return fmt.Errorf("unknown postgres error: %w", pqErr)
}
}
// ErrSQLUniqueConstraintViolation is an error type which represents a database
// agnostic SQL unique constraint violation.
type ErrSQLUniqueConstraintViolation struct {
DBError error
}
func (e ErrSQLUniqueConstraintViolation) Error() string {
return fmt.Sprintf("sql unique constraint violation: %v", e.DBError)
}
// ErrSerializationError is an error type which represents a database agnostic
// error that a transaction couldn't be serialized with other concurrent db
// transactions.
type ErrSerializationError struct {
DBError error
}
// Unwrap returns the wrapped error.
func (e ErrSerializationError) Unwrap() error {
return e.DBError
}
// Error returns the error message.
func (e ErrSerializationError) Error() string {
return e.DBError.Error()
}
// IsSerializationError returns true if the given error is a serialization
// error.
func IsSerializationError(err error) bool {
var serializationError *ErrSerializationError
return errors.As(err, &serializationError)
}
+15 -31
View File
@@ -1,3 +1,5 @@
//go:build !js && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64))
package sqldb package sqldb
import ( import (
@@ -6,7 +8,6 @@ import (
"net/url" "net/url"
"path/filepath" "path/filepath"
"testing" "testing"
"time"
sqlite_migrate "github.com/golang-migrate/migrate/v4/database/sqlite" sqlite_migrate "github.com/golang-migrate/migrate/v4/database/sqlite"
"github.com/lightningnetwork/lnd/sqldb/sqlc" "github.com/lightningnetwork/lnd/sqldb/sqlc"
@@ -23,32 +24,8 @@ const (
// sqliteTxLockImmediate is a dsn option used to ensure that write // sqliteTxLockImmediate is a dsn option used to ensure that write
// transactions are started immediately. // transactions are started immediately.
sqliteTxLockImmediate = "_txlock=immediate" sqliteTxLockImmediate = "_txlock=immediate"
// defaultMaxConns is the number of permitted active and idle
// connections. We want to limit this so it isn't unlimited. We use the
// same value for the number of idle connections as, this can speed up
// queries given a new connection doesn't need to be established each
// time.
defaultMaxConns = 25
// connIdleLifetime is the amount of time a connection can be idle.
connIdleLifetime = 5 * time.Minute
) )
// SqliteConfig holds all the config arguments needed to interact with our
// sqlite DB.
//
//nolint:lll
type SqliteConfig struct {
// SkipMigrations if true, then all the tables will be created on start
// up if they don't already exist.
SkipMigrations bool `long:"skipmigrations" description:"Skip applying migrations on startup."`
// DatabaseFileName is the full file path where the database file can be
// found.
DatabaseFileName string `long:"dbfile" description:"The full path to the database."`
}
// SqliteStore is a database store implementation that uses a sqlite backend. // SqliteStore is a database store implementation that uses a sqlite backend.
type SqliteStore struct { type SqliteStore struct {
cfg *SqliteConfig cfg *SqliteConfig
@@ -58,7 +35,7 @@ type SqliteStore struct {
// NewSqliteStore attempts to open a new sqlite database based on the passed // NewSqliteStore attempts to open a new sqlite database based on the passed
// config. // config.
func NewSqliteStore(cfg *SqliteConfig) (*SqliteStore, error) { func NewSqliteStore(cfg *SqliteConfig, dbPath string) (*SqliteStore, error) {
// The set of pragma options are accepted using query options. For now // The set of pragma options are accepted using query options. For now
// we only want to ensure that foreign key constraints are properly // we only want to ensure that foreign key constraints are properly
// enforced. // enforced.
@@ -107,7 +84,7 @@ func NewSqliteStore(cfg *SqliteConfig) (*SqliteStore, error) {
// details on the formatting here, see the modernc.org/sqlite docs: // details on the formatting here, see the modernc.org/sqlite docs:
// https://pkg.go.dev/modernc.org/sqlite#Driver.Open. // https://pkg.go.dev/modernc.org/sqlite#Driver.Open.
dsn := fmt.Sprintf( dsn := fmt.Sprintf(
"%v?%v&%v", cfg.DatabaseFileName, sqliteOptions.Encode(), "%v?%v&%v", dbPath, sqliteOptions.Encode(),
sqliteTxLockImmediate, sqliteTxLockImmediate,
) )
db, err := sql.Open("sqlite", dsn) db, err := sql.Open("sqlite", dsn)
@@ -133,8 +110,16 @@ func NewSqliteStore(cfg *SqliteConfig) (*SqliteStore, error) {
return nil, err return nil, err
} }
// We use INTEGER PRIMARY KEY for sqlite, because it acts as a
// ROWID alias which is 8 bytes big and also autoincrements.
// It's important to use the ROWID as a primary key because the
// key look ups are almost twice as fast
sqliteFS := newReplacerFS(sqlSchemas, map[string]string{
"BIGINT PRIMARY KEY": "INTEGER PRIMARY KEY",
})
err = applyMigrations( err = applyMigrations(
sqlSchemas, driver, "sqlc/migrations", "sqlc", sqliteFS, driver, "sqlc/migrations", "sqlc",
) )
if err != nil { if err != nil {
return nil, err return nil, err
@@ -163,9 +148,8 @@ func NewTestSqliteDB(t *testing.T) *SqliteStore {
// an in mem version to speed up tests // an in mem version to speed up tests
dbFileName := filepath.Join(t.TempDir(), "tmp.db") dbFileName := filepath.Join(t.TempDir(), "tmp.db")
sqlDB, err := NewSqliteStore(&SqliteConfig{ sqlDB, err := NewSqliteStore(&SqliteConfig{
DatabaseFileName: dbFileName, SkipMigrations: false,
SkipMigrations: false, }, dbFileName)
})
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(func() { t.Cleanup(func() {
+54
View File
@@ -0,0 +1,54 @@
package sqldb
import (
"database/sql"
"time"
"golang.org/x/exp/constraints"
)
// sqlInt32 turns a numerical integer type into the NullInt32 that sql/sqlc
// uses when an integer field can be permitted to be NULL.
//
// We use this constraints.Integer constraint here which maps to all signed and
// unsigned integer types.
func sqlInt32[T constraints.Integer](num T) sql.NullInt32 {
return sql.NullInt32{
Int32: int32(num),
Valid: true,
}
}
// sqlInt64 turns a numerical integer type into the NullInt64 that sql/sqlc
// uses when an integer field can be permitted to be NULL.
//
// We use this constraints.Integer constraint here which maps to all signed and
// unsigned integer types.
func sqlInt64[T constraints.Integer](num T) sql.NullInt64 {
return sql.NullInt64{
Int64: int64(num),
Valid: true,
}
}
// sqlStr turns a string into the NullString that sql/sqlc uses when a string
// can be permitted to be NULL.
func sqlStr(s string) sql.NullString {
if s == "" {
return sql.NullString{}
}
return sql.NullString{
String: s,
Valid: true,
}
}
// sqlTime turns a time.Time into the NullTime that sql/sqlc uses when a time
// can be permitted to be NULL.
func sqlTime(t time.Time) sql.NullTime {
return sql.NullTime{
Time: t,
Valid: true,
}
}