mirror of
https://github.com/lightningnetwork/lnd.git
synced 2026-08-13 12:32:48 +02:00
488 lines
12 KiB
SQL
488 lines
12 KiB
SQL
/* ─────────────────────────────────────────────
|
|
fetch queries
|
|
─────────────────────────────────────────────
|
|
*/
|
|
|
|
-- name: FilterPayments :many
|
|
SELECT
|
|
sqlc.embed(p),
|
|
i.intent_type AS "intent_type",
|
|
i.intent_payload AS "intent_payload"
|
|
FROM payments p
|
|
LEFT JOIN payment_intents i ON i.payment_id = p.id
|
|
WHERE p.id > COALESCE(sqlc.narg('index_offset_get'), -1)
|
|
AND p.id < COALESCE(sqlc.narg('index_offset_let'), 9223372036854775807)
|
|
-- NOTE: We use non-nullable time params with Go-side defaults instead of
|
|
-- COALESCE, because COALESCE with text fallback causes type mismatch on
|
|
-- Postgres (timestamp vs text), and OR-based optional filters can prevent
|
|
-- the planner from using the created_at index.
|
|
AND p.created_at >= @created_after
|
|
AND p.created_at <= @created_before
|
|
AND (
|
|
i.intent_type = sqlc.narg('intent_type') OR
|
|
sqlc.narg('intent_type') IS NULL OR i.intent_type IS NULL
|
|
)
|
|
ORDER BY p.id ASC
|
|
LIMIT @num_limit;
|
|
|
|
-- name: FilterPaymentsDesc :many
|
|
SELECT
|
|
sqlc.embed(p),
|
|
i.intent_type AS "intent_type",
|
|
i.intent_payload AS "intent_payload"
|
|
FROM payments p
|
|
LEFT JOIN payment_intents i ON i.payment_id = p.id
|
|
WHERE p.id > COALESCE(sqlc.narg('index_offset_get'), -1)
|
|
AND p.id < COALESCE(sqlc.narg('index_offset_let'), 9223372036854775807)
|
|
-- NOTE: We use non-nullable time params with Go-side defaults instead of
|
|
-- COALESCE, because COALESCE with text fallback causes type mismatch on
|
|
-- Postgres (timestamp vs text), and OR-based optional filters can prevent
|
|
-- the planner from using the created_at index.
|
|
AND p.created_at >= @created_after
|
|
AND p.created_at <= @created_before
|
|
AND (
|
|
i.intent_type = sqlc.narg('intent_type') OR
|
|
sqlc.narg('intent_type') IS NULL OR i.intent_type IS NULL
|
|
)
|
|
ORDER BY p.id DESC
|
|
LIMIT @num_limit;
|
|
|
|
-- name: FetchPayment :one
|
|
SELECT
|
|
sqlc.embed(p),
|
|
i.intent_type AS "intent_type",
|
|
i.intent_payload AS "intent_payload"
|
|
FROM payments p
|
|
LEFT JOIN payment_intents i ON i.payment_id = p.id
|
|
WHERE p.payment_identifier = $1;
|
|
|
|
-- name: FetchPaymentDuplicates :many
|
|
-- Fetch all duplicate payment records from the payment_duplicates table for
|
|
-- a given payment ID.
|
|
SELECT
|
|
id,
|
|
payment_id,
|
|
amount_msat,
|
|
created_at,
|
|
fail_reason,
|
|
settle_preimage,
|
|
settle_time
|
|
FROM payment_duplicates
|
|
WHERE payment_id = $1
|
|
ORDER BY id ASC;
|
|
|
|
-- name: CountPayments :one
|
|
SELECT COUNT(*) FROM payments;
|
|
|
|
-- name: FetchHtlcAttemptsForPayments :many
|
|
SELECT
|
|
ha.id,
|
|
ha.attempt_index,
|
|
ha.payment_id,
|
|
ha.session_key,
|
|
ha.attempt_time,
|
|
ha.payment_hash,
|
|
ha.first_hop_amount_msat,
|
|
ha.route_total_time_lock,
|
|
ha.route_total_amount,
|
|
ha.route_source_key,
|
|
hr.resolution_type,
|
|
hr.resolution_time,
|
|
hr.failure_source_index,
|
|
hr.htlc_fail_reason,
|
|
hr.failure_msg,
|
|
hr.settle_preimage
|
|
FROM payment_htlc_attempts ha
|
|
LEFT JOIN payment_htlc_attempt_resolutions hr ON hr.attempt_index = ha.attempt_index
|
|
WHERE ha.payment_id IN (sqlc.slice('payment_ids')/*SLICE:payment_ids*/)
|
|
ORDER BY ha.payment_id ASC, ha.attempt_time ASC;
|
|
|
|
-- name: FetchHtlcAttemptResolutionsForPayments :many
|
|
-- Batch query to fetch only HTLC resolution status for multiple payments.
|
|
-- We don't need to order by payment_id and attempt_time because we will
|
|
-- group the resolutions by payment_id in the background.
|
|
SELECT
|
|
ha.payment_id,
|
|
hr.resolution_type
|
|
FROM payment_htlc_attempts ha
|
|
LEFT JOIN payment_htlc_attempt_resolutions hr ON hr.attempt_index = ha.attempt_index
|
|
WHERE ha.payment_id IN (sqlc.slice('payment_ids')/*SLICE:payment_ids*/);
|
|
|
|
-- name: FetchPaymentsByIDs :many
|
|
-- Batch fetch payment and intent data for a set of payment IDs.
|
|
-- Used to avoid fetching redundant payment data when processing multiple
|
|
-- attempts for the same payment.
|
|
SELECT
|
|
p.id,
|
|
p.amount_msat,
|
|
p.created_at,
|
|
p.payment_identifier,
|
|
p.fail_reason,
|
|
pi.intent_type,
|
|
pi.intent_payload
|
|
FROM payments p
|
|
LEFT JOIN payment_intents pi ON pi.payment_id = p.id
|
|
WHERE p.id IN (sqlc.slice('payment_ids')/*SLICE:payment_ids*/)
|
|
ORDER BY p.id ASC;
|
|
|
|
-- name: FetchNonTerminalPayments :many
|
|
-- Fetch all non-terminal payments using pagination. A payment is
|
|
-- non-terminal if it has an unresolved attempt, or if it has not been
|
|
-- permanently failed and has no settled attempt yet.
|
|
SELECT
|
|
p.id,
|
|
p.amount_msat,
|
|
p.created_at,
|
|
p.payment_identifier,
|
|
p.fail_reason,
|
|
pi.intent_type,
|
|
pi.intent_payload
|
|
FROM payments p
|
|
LEFT JOIN payment_intents pi
|
|
ON pi.payment_id = p.id
|
|
WHERE p.id > $1
|
|
AND (
|
|
(
|
|
p.fail_reason IS NULL
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM payment_htlc_attempts ha
|
|
JOIN payment_htlc_attempt_resolutions hr
|
|
ON hr.attempt_index = ha.attempt_index
|
|
WHERE ha.payment_id = p.id
|
|
AND hr.resolution_type = 1
|
|
)
|
|
)
|
|
OR EXISTS (
|
|
SELECT 1
|
|
FROM payment_htlc_attempts ha
|
|
WHERE ha.payment_id = p.id
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM payment_htlc_attempt_resolutions hr
|
|
WHERE hr.attempt_index = ha.attempt_index
|
|
)
|
|
)
|
|
)
|
|
ORDER BY p.id ASC
|
|
LIMIT $2;
|
|
|
|
-- name: FetchHopsForAttempts :many
|
|
SELECT
|
|
h.id,
|
|
h.htlc_attempt_index,
|
|
h.hop_index,
|
|
h.pub_key,
|
|
h.scid,
|
|
h.outgoing_time_lock,
|
|
h.amt_to_forward,
|
|
h.meta_data,
|
|
m.payment_addr AS mpp_payment_addr,
|
|
m.total_msat AS mpp_total_msat,
|
|
a.root_share AS amp_root_share,
|
|
a.set_id AS amp_set_id,
|
|
a.child_index AS amp_child_index,
|
|
b.encrypted_data,
|
|
b.blinding_point,
|
|
b.blinded_path_total_amt
|
|
FROM payment_route_hops h
|
|
LEFT JOIN payment_route_hop_mpp m ON m.hop_id = h.id
|
|
LEFT JOIN payment_route_hop_amp a ON a.hop_id = h.id
|
|
LEFT JOIN payment_route_hop_blinded b ON b.hop_id = h.id
|
|
WHERE h.htlc_attempt_index IN (sqlc.slice('htlc_attempt_indices')/*SLICE:htlc_attempt_indices*/)
|
|
ORDER BY h.htlc_attempt_index ASC, h.hop_index ASC;
|
|
|
|
|
|
-- name: FetchPaymentLevelFirstHopCustomRecords :many
|
|
SELECT
|
|
l.id,
|
|
l.payment_id,
|
|
l.key,
|
|
l.value
|
|
FROM payment_first_hop_custom_records l
|
|
WHERE l.payment_id IN (sqlc.slice('payment_ids')/*SLICE:payment_ids*/)
|
|
ORDER BY l.payment_id ASC, l.key ASC;
|
|
|
|
-- name: FetchRouteLevelFirstHopCustomRecords :many
|
|
SELECT
|
|
l.id,
|
|
l.htlc_attempt_index,
|
|
l.key,
|
|
l.value
|
|
FROM payment_attempt_first_hop_custom_records l
|
|
WHERE l.htlc_attempt_index IN (sqlc.slice('htlc_attempt_indices')/*SLICE:htlc_attempt_indices*/)
|
|
ORDER BY l.htlc_attempt_index ASC, l.key ASC;
|
|
|
|
-- name: FetchHopLevelCustomRecords :many
|
|
SELECT
|
|
l.id,
|
|
l.hop_id,
|
|
l.key,
|
|
l.value
|
|
FROM payment_hop_custom_records l
|
|
WHERE l.hop_id IN (sqlc.slice('hop_ids')/*SLICE:hop_ids*/)
|
|
ORDER BY l.hop_id ASC, l.key ASC;
|
|
|
|
|
|
-- name: DeletePayment :exec
|
|
DELETE FROM payments WHERE id = $1;
|
|
|
|
-- name: DeleteFailedAttempts :exec
|
|
-- Delete all failed HTLC attempts for the given payment. Resolution type 2
|
|
-- indicates a failed attempt. Uses EXISTS to scope the resolution lookup to
|
|
-- only this payment's attempts, avoiding an O(N) scan of all failed
|
|
-- resolutions across all payments.
|
|
DELETE FROM payment_htlc_attempts
|
|
WHERE payment_id = $1
|
|
AND EXISTS (
|
|
SELECT 1 FROM payment_htlc_attempt_resolutions hr
|
|
WHERE hr.attempt_index = payment_htlc_attempts.attempt_index
|
|
AND hr.resolution_type = 2
|
|
);
|
|
|
|
-- name: InsertPaymentIntent :one
|
|
-- Insert a payment intent for a given payment and return its ID.
|
|
INSERT INTO payment_intents (
|
|
payment_id,
|
|
intent_type,
|
|
intent_payload)
|
|
VALUES (
|
|
@payment_id,
|
|
@intent_type,
|
|
@intent_payload
|
|
)
|
|
RETURNING id;
|
|
|
|
-- name: InsertPayment :one
|
|
-- Insert a new payment and return its ID.
|
|
-- When creating a payment we don't have a fail reason because we start the
|
|
-- payment process.
|
|
INSERT INTO payments (
|
|
amount_msat,
|
|
created_at,
|
|
payment_identifier,
|
|
fail_reason)
|
|
VALUES (
|
|
@amount_msat,
|
|
@created_at,
|
|
@payment_identifier,
|
|
NULL
|
|
)
|
|
RETURNING id;
|
|
|
|
-- name: InsertPaymentFirstHopCustomRecord :exec
|
|
INSERT INTO payment_first_hop_custom_records (
|
|
payment_id,
|
|
key,
|
|
value
|
|
)
|
|
VALUES (
|
|
@payment_id,
|
|
@key,
|
|
@value
|
|
);
|
|
|
|
-- name: InsertHtlcAttempt :one
|
|
INSERT INTO payment_htlc_attempts (
|
|
payment_id,
|
|
attempt_index,
|
|
session_key,
|
|
attempt_time,
|
|
payment_hash,
|
|
first_hop_amount_msat,
|
|
route_total_time_lock,
|
|
route_total_amount,
|
|
route_source_key)
|
|
VALUES (
|
|
@payment_id,
|
|
@attempt_index,
|
|
@session_key,
|
|
@attempt_time,
|
|
@payment_hash,
|
|
@first_hop_amount_msat,
|
|
@route_total_time_lock,
|
|
@route_total_amount,
|
|
@route_source_key)
|
|
RETURNING id;
|
|
|
|
-- name: InsertPaymentAttemptFirstHopCustomRecord :exec
|
|
INSERT INTO payment_attempt_first_hop_custom_records (
|
|
htlc_attempt_index,
|
|
key,
|
|
value
|
|
)
|
|
VALUES (
|
|
@htlc_attempt_index,
|
|
@key,
|
|
@value
|
|
);
|
|
|
|
-- name: InsertRouteHop :one
|
|
INSERT INTO payment_route_hops (
|
|
htlc_attempt_index,
|
|
hop_index,
|
|
pub_key,
|
|
scid,
|
|
outgoing_time_lock,
|
|
amt_to_forward,
|
|
meta_data
|
|
)
|
|
VALUES (
|
|
@htlc_attempt_index,
|
|
@hop_index,
|
|
@pub_key,
|
|
@scid,
|
|
@outgoing_time_lock,
|
|
@amt_to_forward,
|
|
@meta_data
|
|
)
|
|
RETURNING id;
|
|
|
|
-- name: InsertRouteHopMpp :exec
|
|
INSERT INTO payment_route_hop_mpp (
|
|
hop_id,
|
|
payment_addr,
|
|
total_msat
|
|
)
|
|
VALUES (
|
|
@hop_id,
|
|
@payment_addr,
|
|
@total_msat
|
|
);
|
|
|
|
-- name: InsertRouteHopAmp :exec
|
|
INSERT INTO payment_route_hop_amp (
|
|
hop_id,
|
|
root_share,
|
|
set_id,
|
|
child_index
|
|
)
|
|
VALUES (
|
|
@hop_id,
|
|
@root_share,
|
|
@set_id,
|
|
@child_index
|
|
);
|
|
|
|
-- name: InsertRouteHopBlinded :exec
|
|
INSERT INTO payment_route_hop_blinded (
|
|
hop_id,
|
|
encrypted_data,
|
|
blinding_point,
|
|
blinded_path_total_amt
|
|
)
|
|
VALUES (
|
|
@hop_id,
|
|
@encrypted_data,
|
|
@blinding_point,
|
|
@blinded_path_total_amt
|
|
);
|
|
|
|
-- name: InsertPaymentHopCustomRecord :exec
|
|
INSERT INTO payment_hop_custom_records (
|
|
hop_id,
|
|
key,
|
|
value
|
|
)
|
|
VALUES (
|
|
@hop_id,
|
|
@key,
|
|
@value
|
|
);
|
|
|
|
-- name: SettleAttempt :exec
|
|
INSERT INTO payment_htlc_attempt_resolutions (
|
|
attempt_index,
|
|
resolution_time,
|
|
resolution_type,
|
|
settle_preimage
|
|
)
|
|
VALUES (
|
|
@attempt_index,
|
|
@resolution_time,
|
|
@resolution_type,
|
|
@settle_preimage
|
|
);
|
|
|
|
-- name: FailAttempt :exec
|
|
INSERT INTO payment_htlc_attempt_resolutions (
|
|
attempt_index,
|
|
resolution_time,
|
|
resolution_type,
|
|
failure_source_index,
|
|
htlc_fail_reason,
|
|
failure_msg
|
|
)
|
|
VALUES (
|
|
@attempt_index,
|
|
@resolution_time,
|
|
@resolution_type,
|
|
@failure_source_index,
|
|
@htlc_fail_reason,
|
|
@failure_msg
|
|
);
|
|
|
|
-- name: FailPayment :execresult
|
|
UPDATE payments SET fail_reason = $1 WHERE payment_identifier = $2;
|
|
|
|
/* ─────────────────────────────────────────────
|
|
Migration-specific queries
|
|
|
|
These queries are used ONLY for the one-time migration from KV to SQL.
|
|
─────────────────────────────────────────────
|
|
*/
|
|
|
|
-- name: InsertPaymentMig :one
|
|
-- Migration-specific payment insert that allows setting fail_reason.
|
|
-- Normal InsertPayment forces fail_reason to NULL since new payments
|
|
-- aren't failed yet. During migration, we're inserting historical data
|
|
-- that may already be failed.
|
|
INSERT INTO payments (
|
|
amount_msat,
|
|
created_at,
|
|
payment_identifier,
|
|
fail_reason)
|
|
VALUES (
|
|
@amount_msat,
|
|
@created_at,
|
|
@payment_identifier,
|
|
@fail_reason
|
|
)
|
|
RETURNING id;
|
|
|
|
-- name: FetchPaymentsByIDsMig :many
|
|
-- Migration-specific batch fetch that returns payment data along with HTLC
|
|
-- attempt counts for structural validation during KV to SQL migration.
|
|
SELECT
|
|
p.id,
|
|
p.amount_msat,
|
|
p.created_at,
|
|
p.payment_identifier,
|
|
p.fail_reason,
|
|
COUNT(ha.id) AS htlc_attempt_count
|
|
FROM payments p
|
|
LEFT JOIN payment_htlc_attempts ha ON ha.payment_id = p.id
|
|
WHERE p.id IN (sqlc.slice('payment_ids')/*SLICE:payment_ids*/)
|
|
GROUP BY p.id, p.amount_msat, p.created_at, p.payment_identifier, p.fail_reason
|
|
ORDER BY p.id ASC;
|
|
|
|
-- name: InsertPaymentDuplicateMig :one
|
|
-- Insert a duplicate payment record into the payment_duplicates table and
|
|
-- return its ID.
|
|
INSERT INTO payment_duplicates (
|
|
payment_id,
|
|
amount_msat,
|
|
created_at,
|
|
fail_reason,
|
|
settle_preimage,
|
|
settle_time
|
|
)
|
|
VALUES (
|
|
@payment_id,
|
|
@amount_msat,
|
|
@created_at,
|
|
@fail_reason,
|
|
@settle_preimage,
|
|
@settle_time
|
|
)
|
|
RETURNING id;
|