db/sqlc: add account related tables and queries

This commit also contains the sqlc.yaml file, the `make sqlc` command
and the script for generating sqlc code. This must be done in this
commit as the script only works if there are queries to generate from.
This commit is contained in:
Elle Mouton 2024-12-21 19:46:06 +02:00
parent 09c1c16fb7
commit caec0742db
No known key found for this signature in database
GPG key ID: D7D916376026F177
10 changed files with 729 additions and 0 deletions

View file

@ -300,6 +300,14 @@ clean: clean-itest
$(RM) ./litd-debug
$(RM) coverage.txt
sqlc:
@$(call print, "Generating sql models and queries in Go")
./scripts/gen_sqlc_docker.sh
sqlc-check: sqlc
@$(call print, "Verifying sql code generation.")
if test -n "$$(git status --porcelain '*.go')"; then echo "SQL models not properly generated!"; git status --porcelain '*.go'; exit 1; fi
# Prevent make from interpreting any of the defined goals as folders or files to
# include in the build process.
.PHONY: default all yarn-install build install go-build go-build-noui \

394
db/sqlc/accounts.sql.go Normal file
View file

@ -0,0 +1,394 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
// source: accounts.sql
package sqlc
import (
"context"
"database/sql"
"time"
)
const addAccountInvoice = `-- name: AddAccountInvoice :exec
INSERT INTO account_invoices (account_id, hash)
VALUES ($1, $2)
`
type AddAccountInvoiceParams struct {
AccountID int64
Hash []byte
}
func (q *Queries) AddAccountInvoice(ctx context.Context, arg AddAccountInvoiceParams) error {
_, err := q.db.ExecContext(ctx, addAccountInvoice, arg.AccountID, arg.Hash)
return err
}
const deleteAccount = `-- name: DeleteAccount :exec
DELETE FROM accounts
WHERE id = $1
`
func (q *Queries) DeleteAccount(ctx context.Context, id int64) error {
_, err := q.db.ExecContext(ctx, deleteAccount, id)
return err
}
const deleteAccountPayment = `-- name: DeleteAccountPayment :exec
DELETE FROM account_payments
WHERE hash = $1
AND account_id = $2
`
type DeleteAccountPaymentParams struct {
Hash []byte
AccountID int64
}
func (q *Queries) DeleteAccountPayment(ctx context.Context, arg DeleteAccountPaymentParams) error {
_, err := q.db.ExecContext(ctx, deleteAccountPayment, arg.Hash, arg.AccountID)
return err
}
const getAccount = `-- name: GetAccount :one
SELECT id, alias, label, type, initial_balance_msat, current_balance_msat, last_updated, expiration
FROM accounts
WHERE id = $1
`
func (q *Queries) GetAccount(ctx context.Context, id int64) (Account, error) {
row := q.db.QueryRowContext(ctx, getAccount, id)
var i Account
err := row.Scan(
&i.ID,
&i.Alias,
&i.Label,
&i.Type,
&i.InitialBalanceMsat,
&i.CurrentBalanceMsat,
&i.LastUpdated,
&i.Expiration,
)
return i, err
}
const getAccountByLabel = `-- name: GetAccountByLabel :one
SELECT id, alias, label, type, initial_balance_msat, current_balance_msat, last_updated, expiration
FROM accounts
WHERE label = $1
`
func (q *Queries) GetAccountByLabel(ctx context.Context, label sql.NullString) (Account, error) {
row := q.db.QueryRowContext(ctx, getAccountByLabel, label)
var i Account
err := row.Scan(
&i.ID,
&i.Alias,
&i.Label,
&i.Type,
&i.InitialBalanceMsat,
&i.CurrentBalanceMsat,
&i.LastUpdated,
&i.Expiration,
)
return i, err
}
const getAccountIDByAlias = `-- name: GetAccountIDByAlias :one
SELECT id
FROM accounts
WHERE alias = $1
`
func (q *Queries) GetAccountIDByAlias(ctx context.Context, alias int64) (int64, error) {
row := q.db.QueryRowContext(ctx, getAccountIDByAlias, alias)
var id int64
err := row.Scan(&id)
return id, err
}
const getAccountIndex = `-- name: GetAccountIndex :one
SELECT value
FROM account_indices
WHERE name = $1
`
func (q *Queries) GetAccountIndex(ctx context.Context, name string) (int64, error) {
row := q.db.QueryRowContext(ctx, getAccountIndex, name)
var value int64
err := row.Scan(&value)
return value, err
}
const getAccountInvoice = `-- name: GetAccountInvoice :one
SELECT account_id, hash
FROM account_invoices
WHERE account_id = $1
AND hash = $2
`
type GetAccountInvoiceParams struct {
AccountID int64
Hash []byte
}
func (q *Queries) GetAccountInvoice(ctx context.Context, arg GetAccountInvoiceParams) (AccountInvoice, error) {
row := q.db.QueryRowContext(ctx, getAccountInvoice, arg.AccountID, arg.Hash)
var i AccountInvoice
err := row.Scan(&i.AccountID, &i.Hash)
return i, err
}
const getAccountPayment = `-- name: GetAccountPayment :one
SELECT account_id, hash, status, full_amount_msat FROM account_payments
WHERE hash = $1
AND account_id = $2
`
type GetAccountPaymentParams struct {
Hash []byte
AccountID int64
}
func (q *Queries) GetAccountPayment(ctx context.Context, arg GetAccountPaymentParams) (AccountPayment, error) {
row := q.db.QueryRowContext(ctx, getAccountPayment, arg.Hash, arg.AccountID)
var i AccountPayment
err := row.Scan(
&i.AccountID,
&i.Hash,
&i.Status,
&i.FullAmountMsat,
)
return i, err
}
const insertAccount = `-- name: InsertAccount :one
INSERT INTO accounts (type, initial_balance_msat, current_balance_msat, last_updated, label, alias, expiration)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id
`
type InsertAccountParams struct {
Type int16
InitialBalanceMsat int64
CurrentBalanceMsat int64
LastUpdated time.Time
Label sql.NullString
Alias int64
Expiration time.Time
}
func (q *Queries) InsertAccount(ctx context.Context, arg InsertAccountParams) (int64, error) {
row := q.db.QueryRowContext(ctx, insertAccount,
arg.Type,
arg.InitialBalanceMsat,
arg.CurrentBalanceMsat,
arg.LastUpdated,
arg.Label,
arg.Alias,
arg.Expiration,
)
var id int64
err := row.Scan(&id)
return id, err
}
const listAccountInvoices = `-- name: ListAccountInvoices :many
SELECT account_id, hash
FROM account_invoices
WHERE account_id = $1
`
func (q *Queries) ListAccountInvoices(ctx context.Context, accountID int64) ([]AccountInvoice, error) {
rows, err := q.db.QueryContext(ctx, listAccountInvoices, accountID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AccountInvoice
for rows.Next() {
var i AccountInvoice
if err := rows.Scan(&i.AccountID, &i.Hash); 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 listAccountPayments = `-- name: ListAccountPayments :many
SELECT account_id, hash, status, full_amount_msat
FROM account_payments
WHERE account_id = $1
`
func (q *Queries) ListAccountPayments(ctx context.Context, accountID int64) ([]AccountPayment, error) {
rows, err := q.db.QueryContext(ctx, listAccountPayments, accountID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AccountPayment
for rows.Next() {
var i AccountPayment
if err := rows.Scan(
&i.AccountID,
&i.Hash,
&i.Status,
&i.FullAmountMsat,
); 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 listAllAccounts = `-- name: ListAllAccounts :many
SELECT id, alias, label, type, initial_balance_msat, current_balance_msat, last_updated, expiration
FROM accounts
`
func (q *Queries) ListAllAccounts(ctx context.Context) ([]Account, error) {
rows, err := q.db.QueryContext(ctx, listAllAccounts)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Account
for rows.Next() {
var i Account
if err := rows.Scan(
&i.ID,
&i.Alias,
&i.Label,
&i.Type,
&i.InitialBalanceMsat,
&i.CurrentBalanceMsat,
&i.LastUpdated,
&i.Expiration,
); 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 setAccountIndex = `-- name: SetAccountIndex :exec
INSERT INTO account_indices (name, value)
VALUES ($1, $2)
ON CONFLICT (name)
DO UPDATE SET value = $2
`
type SetAccountIndexParams struct {
Name string
Value int64
}
func (q *Queries) SetAccountIndex(ctx context.Context, arg SetAccountIndexParams) error {
_, err := q.db.ExecContext(ctx, setAccountIndex, arg.Name, arg.Value)
return err
}
const updateAccountBalance = `-- name: UpdateAccountBalance :one
UPDATE accounts
SET current_balance_msat = $1
WHERE id = $2
RETURNING id
`
type UpdateAccountBalanceParams struct {
CurrentBalanceMsat int64
ID int64
}
func (q *Queries) UpdateAccountBalance(ctx context.Context, arg UpdateAccountBalanceParams) (int64, error) {
row := q.db.QueryRowContext(ctx, updateAccountBalance, arg.CurrentBalanceMsat, arg.ID)
var id int64
err := row.Scan(&id)
return id, err
}
const updateAccountExpiry = `-- name: UpdateAccountExpiry :one
UPDATE accounts
SET expiration = $1
WHERE id = $2
RETURNING id
`
type UpdateAccountExpiryParams struct {
Expiration time.Time
ID int64
}
func (q *Queries) UpdateAccountExpiry(ctx context.Context, arg UpdateAccountExpiryParams) (int64, error) {
row := q.db.QueryRowContext(ctx, updateAccountExpiry, arg.Expiration, arg.ID)
var id int64
err := row.Scan(&id)
return id, err
}
const updateAccountLastUpdate = `-- name: UpdateAccountLastUpdate :one
UPDATE accounts
SET last_updated = $1
WHERE id = $2
RETURNING id
`
type UpdateAccountLastUpdateParams struct {
LastUpdated time.Time
ID int64
}
func (q *Queries) UpdateAccountLastUpdate(ctx context.Context, arg UpdateAccountLastUpdateParams) (int64, error) {
row := q.db.QueryRowContext(ctx, updateAccountLastUpdate, arg.LastUpdated, arg.ID)
var id int64
err := row.Scan(&id)
return id, err
}
const upsertAccountPayment = `-- name: UpsertAccountPayment :exec
INSERT INTO account_payments (account_id, hash, status, full_amount_msat)
VALUES ($1, $2, $3, $4)
ON CONFLICT (account_id, hash)
DO UPDATE SET status = $3, full_amount_msat = $4
`
type UpsertAccountPaymentParams struct {
AccountID int64
Hash []byte
Status int16
FullAmountMsat int64
}
func (q *Queries) UpsertAccountPayment(ctx context.Context, arg UpsertAccountPaymentParams) error {
_, err := q.db.ExecContext(ctx, upsertAccountPayment,
arg.AccountID,
arg.Hash,
arg.Status,
arg.FullAmountMsat,
)
return err
}

31
db/sqlc/db.go Normal file
View file

@ -0,0 +1,31 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
package sqlc
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}

View file

@ -0,0 +1,4 @@
DROP TABLE IF EXISTS account_payments;
DROP TABLE IF EXISTS account_invoices;
DROP TABLE IF EXISTS account_indices;
DROP TABLE IF EXISTS accounts;

View file

@ -0,0 +1,71 @@
CREATE TABLE IF NOT EXISTS accounts (
-- The auto incrementing primary key.
id INTEGER PRIMARY KEY,
-- The ID that was used to identify the account in the legacy KVDB store.
-- In order to avoid breaking the API, we keep this field here so that
-- we can still look up accounts by this ID for the time being.
alias BIGINT NOT NULL UNIQUE,
-- An optional label to use for the account. If it is set, it must be
-- unique.
label TEXT UNIQUE,
-- The account type.
type SMALLINT NOT NULL,
-- The accounts initial balance. This is never updated.
initial_balance_msat BIGINT NOT NULL,
-- The accounts current balance. This is updated as the account is used.
current_balance_msat BIGINT NOT NULL,
-- The last time the account was updated.
last_updated TIMESTAMP NOT NULL,
-- The time that the account will expire.
expiration TIMESTAMP NOT NULL
);
-- The account_payments table stores all the payment hashes of outgoing
-- payments that are associated with a particular account. These are used to
-- when an account should be debited.
CREATE TABLE IF NOT EXISTS account_payments (
-- The account that this payment is linked to.
account_id BIGINT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
-- The payment hash of the payment.
hash BLOB NOT NULL,
-- The LND RPC status of the payment.
status SMALLINT NOT NULL,
-- The total amount of the payment in millisatoshis.
-- This includes the payment amount and estimated routing fee.
full_amount_msat BIGINT NOT NULL,
UNIQUE(account_id, hash)
);
-- The account_invoices table stores all the invoice payment hashes that
-- are associated with a particular account. These are used to determine
-- when an account should be credited.
CREATE TABLE IF NOT EXISTS account_invoices (
-- The account that this invoice is linked to.
account_id BIGINT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
-- The payment hash of the invoice.
hash BLOB NOT NULL,
UNIQUE(account_id, hash)
);
-- The account_indices table stores any string-to-integer mappings that are
-- used by the accounts system.
CREATE TABLE IF NOT EXISTS account_indices (
-- The unique name of the index.
name TEXT NOT NULL UNIQUE,
-- The current value of the index.
value BIGINT NOT NULL
);

38
db/sqlc/models.go Normal file
View file

@ -0,0 +1,38 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
package sqlc
import (
"database/sql"
"time"
)
type Account struct {
ID int64
Alias int64
Label sql.NullString
Type int16
InitialBalanceMsat int64
CurrentBalanceMsat int64
LastUpdated time.Time
Expiration time.Time
}
type AccountIndex struct {
Name string
Value int64
}
type AccountInvoice struct {
AccountID int64
Hash []byte
}
type AccountPayment struct {
AccountID int64
Hash []byte
Status int16
FullAmountMsat int64
}

33
db/sqlc/querier.go Normal file
View file

@ -0,0 +1,33 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
package sqlc
import (
"context"
"database/sql"
)
type Querier interface {
AddAccountInvoice(ctx context.Context, arg AddAccountInvoiceParams) error
DeleteAccount(ctx context.Context, id int64) error
DeleteAccountPayment(ctx context.Context, arg DeleteAccountPaymentParams) error
GetAccount(ctx context.Context, id int64) (Account, error)
GetAccountByLabel(ctx context.Context, label sql.NullString) (Account, error)
GetAccountIDByAlias(ctx context.Context, alias int64) (int64, error)
GetAccountIndex(ctx context.Context, name string) (int64, error)
GetAccountInvoice(ctx context.Context, arg GetAccountInvoiceParams) (AccountInvoice, error)
GetAccountPayment(ctx context.Context, arg GetAccountPaymentParams) (AccountPayment, error)
InsertAccount(ctx context.Context, arg InsertAccountParams) (int64, error)
ListAccountInvoices(ctx context.Context, accountID int64) ([]AccountInvoice, error)
ListAccountPayments(ctx context.Context, accountID int64) ([]AccountPayment, error)
ListAllAccounts(ctx context.Context) ([]Account, error)
SetAccountIndex(ctx context.Context, arg SetAccountIndexParams) error
UpdateAccountBalance(ctx context.Context, arg UpdateAccountBalanceParams) (int64, error)
UpdateAccountExpiry(ctx context.Context, arg UpdateAccountExpiryParams) (int64, error)
UpdateAccountLastUpdate(ctx context.Context, arg UpdateAccountLastUpdateParams) (int64, error)
UpsertAccountPayment(ctx context.Context, arg UpsertAccountPaymentParams) error
}
var _ Querier = (*Queries)(nil)

View file

@ -0,0 +1,92 @@
-- name: InsertAccount :one
INSERT INTO accounts (type, initial_balance_msat, current_balance_msat, last_updated, label, alias, expiration)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id;
-- name: UpdateAccountBalance :one
UPDATE accounts
SET current_balance_msat = $1
WHERE id = $2
RETURNING id;
-- name: UpdateAccountExpiry :one
UPDATE accounts
SET expiration = $1
WHERE id = $2
RETURNING id;
-- name: UpdateAccountLastUpdate :one
UPDATE accounts
SET last_updated = $1
WHERE id = $2
RETURNING id;
-- name: AddAccountInvoice :exec
INSERT INTO account_invoices (account_id, hash)
VALUES ($1, $2);
-- name: DeleteAccountPayment :exec
DELETE FROM account_payments
WHERE hash = $1
AND account_id = $2;
-- name: UpsertAccountPayment :exec
INSERT INTO account_payments (account_id, hash, status, full_amount_msat)
VALUES ($1, $2, $3, $4)
ON CONFLICT (account_id, hash)
DO UPDATE SET status = $3, full_amount_msat = $4;
-- name: GetAccountPayment :one
SELECT * FROM account_payments
WHERE hash = $1
AND account_id = $2;
-- name: GetAccount :one
SELECT *
FROM accounts
WHERE id = $1;
-- name: GetAccountIDByAlias :one
SELECT id
FROM accounts
WHERE alias = $1;
-- name: GetAccountByLabel :one
SELECT *
FROM accounts
WHERE label = $1;
-- name: DeleteAccount :exec
DELETE FROM accounts
WHERE id = $1;
-- name: ListAllAccounts :many
SELECT *
FROM accounts;
-- name: ListAccountPayments :many
SELECT *
FROM account_payments
WHERE account_id = $1;
-- name: ListAccountInvoices :many
SELECT *
FROM account_invoices
WHERE account_id = $1;
-- name: GetAccountInvoice :one
SELECT *
FROM account_invoices
WHERE account_id = $1
AND hash = $2;
-- name: SetAccountIndex :exec
INSERT INTO account_indices (name, value)
VALUES ($1, $2)
ON CONFLICT (name)
DO UPDATE SET value = $2;
-- name: GetAccountIndex :one
SELECT value
FROM account_indices
WHERE name = $1;

48
scripts/gen_sqlc_docker.sh Executable file
View file

@ -0,0 +1,48 @@
#!/bin/bash
set -e
# restore_files is a function to restore original schema files.
restore_files() {
echo "Restoring SQLite bigint patch..."
for file in db/sqlc/migrations/*.up.sql.bak; do
mv "$file" "${file%.bak}"
done
}
# Set trap to call restore_files on script exit. This makes sure the old files
# are always restored.
trap restore_files EXIT
# Directory of the script file, independent of where it's called from.
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Use the user's cache directories
GOCACHE=$(go env GOCACHE)
GOMODCACHE=$(go env GOMODCACHE)
# SQLite doesn't support "BIGINT PRIMARY KEY" for auto-incrementing primary
# keys, only "INTEGER PRIMARY KEY". Internally it uses 64-bit integers for
# numbers anyway, independent of the column type. So we can just use
# "INTEGER PRIMARY KEY" and it will work the same under the hood, giving us
# auto incrementing 64-bit integers.
# _BUT_, sqlc will generate Go code with int32 if we use "INTEGER PRIMARY KEY",
# even though we want int64. So before we run sqlc, we need to patch the
# source schema SQL files to use "BIGINT PRIMARY KEY" instead of "INTEGER
# PRIMARY KEY".
echo "Applying SQLite bigint patch..."
for file in db/sqlc/migrations/*.up.sql; do
echo "Patching $file"
sed -i.bak -E 's/INTEGER PRIMARY KEY/BIGINT PRIMARY KEY/g' "$file"
done
echo "Generating sql models and queries in go..."
# Run the script to generate the new generated code. Once the script exits, we
# use `trap` to make sure all files are restored.
docker run \
--rm \
--user "$UID:$(id -g)" \
-e UID=$UID \
-v "$DIR/../:/build" \
-w /build \
sqlc/sqlc:1.25.0 generate

10
sqlc.yaml Normal file
View file

@ -0,0 +1,10 @@
version: "2"
sql:
- engine: "postgresql"
schema: "db/sqlc/migrations"
queries: "db/sqlc/queries"
gen:
go:
out: db/sqlc
package: sqlc
emit_interface: true