// Code generated by sqlc. DO NOT EDIT.
// versions:
//   sqlc v1.29.0
// source: graph.sql

package sqlc

import (
	"context"
	"database/sql"
	"strings"
)

const addSourceNode = `-- name: AddSourceNode :exec
/* ─────────────────────────────────────────────
   graph_source_nodes table queries
   ─────────────────────────────────────────────
*/

INSERT INTO graph_source_nodes (node_id)
VALUES ($1)
ON CONFLICT (node_id) DO NOTHING
`

func (q *Queries) AddSourceNode(ctx context.Context, nodeID int64) error {
	_, err := q.db.ExecContext(ctx, addSourceNode, nodeID)
	return err
}

const addV1ChannelProof = `-- name: AddV1ChannelProof :execresult
UPDATE graph_channels
SET node_1_signature = $2,
    node_2_signature = $3,
    bitcoin_1_signature = $4,
    bitcoin_2_signature = $5
WHERE scid = $1
  AND version = 1
`

type AddV1ChannelProofParams struct {
	Scid              []byte
	Node1Signature    []byte
	Node2Signature    []byte
	Bitcoin1Signature []byte
	Bitcoin2Signature []byte
}

func (q *Queries) AddV1ChannelProof(ctx context.Context, arg AddV1ChannelProofParams) (sql.Result, error) {
	return q.db.ExecContext(ctx, addV1ChannelProof,
		arg.Scid,
		arg.Node1Signature,
		arg.Node2Signature,
		arg.Bitcoin1Signature,
		arg.Bitcoin2Signature,
	)
}

const countZombieChannels = `-- name: CountZombieChannels :one
SELECT COUNT(*)
FROM graph_zombie_channels
WHERE version = $1
`

func (q *Queries) CountZombieChannels(ctx context.Context, version int16) (int64, error) {
	row := q.db.QueryRowContext(ctx, countZombieChannels, version)
	var count int64
	err := row.Scan(&count)
	return count, err
}

const createChannel = `-- name: CreateChannel :one
/* ─────────────────────────────────────────────
   graph_channels table queries
   ─────────────────────────────────────────────
*/

INSERT INTO graph_channels (
    version, scid, node_id_1, node_id_2,
    outpoint, capacity, bitcoin_key_1, bitcoin_key_2,
    node_1_signature, node_2_signature, bitcoin_1_signature,
    bitcoin_2_signature
) VALUES (
    $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
)
RETURNING id
`

type CreateChannelParams struct {
	Version           int16
	Scid              []byte
	NodeID1           int64
	NodeID2           int64
	Outpoint          string
	Capacity          sql.NullInt64
	BitcoinKey1       []byte
	BitcoinKey2       []byte
	Node1Signature    []byte
	Node2Signature    []byte
	Bitcoin1Signature []byte
	Bitcoin2Signature []byte
}

func (q *Queries) CreateChannel(ctx context.Context, arg CreateChannelParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, createChannel,
		arg.Version,
		arg.Scid,
		arg.NodeID1,
		arg.NodeID2,
		arg.Outpoint,
		arg.Capacity,
		arg.BitcoinKey1,
		arg.BitcoinKey2,
		arg.Node1Signature,
		arg.Node2Signature,
		arg.Bitcoin1Signature,
		arg.Bitcoin2Signature,
	)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const deleteChannelPolicyExtraTypes = `-- name: DeleteChannelPolicyExtraTypes :exec
DELETE FROM graph_channel_policy_extra_types
WHERE channel_policy_id = $1
`

func (q *Queries) DeleteChannelPolicyExtraTypes(ctx context.Context, channelPolicyID int64) error {
	_, err := q.db.ExecContext(ctx, deleteChannelPolicyExtraTypes, channelPolicyID)
	return err
}

const deleteChannels = `-- name: DeleteChannels :exec
DELETE FROM graph_channels
WHERE id IN (/*SLICE:ids*/?)
`

func (q *Queries) DeleteChannels(ctx context.Context, ids []int64) error {
	query := deleteChannels
	var queryParams []interface{}
	if len(ids) > 0 {
		for _, v := range ids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:ids*/?", makeQueryParams(len(queryParams), len(ids)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:ids*/?", "NULL", 1)
	}
	_, err := q.db.ExecContext(ctx, query, queryParams...)
	return err
}

const deleteExtraNodeType = `-- name: DeleteExtraNodeType :exec
DELETE FROM graph_node_extra_types
WHERE node_id = $1
  AND type = $2
`

type DeleteExtraNodeTypeParams struct {
	NodeID int64
	Type   int64
}

func (q *Queries) DeleteExtraNodeType(ctx context.Context, arg DeleteExtraNodeTypeParams) error {
	_, err := q.db.ExecContext(ctx, deleteExtraNodeType, arg.NodeID, arg.Type)
	return err
}

const deleteNode = `-- name: DeleteNode :exec
DELETE FROM graph_nodes
WHERE id = $1
`

func (q *Queries) DeleteNode(ctx context.Context, id int64) error {
	_, err := q.db.ExecContext(ctx, deleteNode, id)
	return err
}

const deleteNodeAddresses = `-- name: DeleteNodeAddresses :exec
DELETE FROM graph_node_addresses
WHERE node_id = $1
`

func (q *Queries) DeleteNodeAddresses(ctx context.Context, nodeID int64) error {
	_, err := q.db.ExecContext(ctx, deleteNodeAddresses, nodeID)
	return err
}

const deleteNodeByPubKey = `-- name: DeleteNodeByPubKey :execresult
DELETE FROM graph_nodes
WHERE pub_key = $1
  AND version = $2
`

type DeleteNodeByPubKeyParams struct {
	PubKey  []byte
	Version int16
}

func (q *Queries) DeleteNodeByPubKey(ctx context.Context, arg DeleteNodeByPubKeyParams) (sql.Result, error) {
	return q.db.ExecContext(ctx, deleteNodeByPubKey, arg.PubKey, arg.Version)
}

const deleteNodeFeature = `-- name: DeleteNodeFeature :exec
DELETE FROM graph_node_features
WHERE node_id = $1
  AND feature_bit = $2
`

type DeleteNodeFeatureParams struct {
	NodeID     int64
	FeatureBit int32
}

func (q *Queries) DeleteNodeFeature(ctx context.Context, arg DeleteNodeFeatureParams) error {
	_, err := q.db.ExecContext(ctx, deleteNodeFeature, arg.NodeID, arg.FeatureBit)
	return err
}

const deletePruneLogEntriesInRange = `-- name: DeletePruneLogEntriesInRange :exec
DELETE FROM graph_prune_log
WHERE block_height >= $1
  AND block_height <= $2
`

type DeletePruneLogEntriesInRangeParams struct {
	StartHeight int64
	EndHeight   int64
}

func (q *Queries) DeletePruneLogEntriesInRange(ctx context.Context, arg DeletePruneLogEntriesInRangeParams) error {
	_, err := q.db.ExecContext(ctx, deletePruneLogEntriesInRange, arg.StartHeight, arg.EndHeight)
	return err
}

const deleteUnconnectedNodes = `-- name: DeleteUnconnectedNodes :many
DELETE FROM graph_nodes
WHERE
    -- Ignore any of our source nodes.
    NOT EXISTS (
        SELECT 1
        FROM graph_source_nodes sn
        WHERE sn.node_id = graph_nodes.id
    )
    -- Select all nodes that do not have any channels.
    AND NOT EXISTS (
        SELECT 1
        FROM graph_channels c
        WHERE c.node_id_1 = graph_nodes.id OR c.node_id_2 = graph_nodes.id
) RETURNING pub_key
`

func (q *Queries) DeleteUnconnectedNodes(ctx context.Context) ([][]byte, error) {
	rows, err := q.db.QueryContext(ctx, deleteUnconnectedNodes)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items [][]byte
	for rows.Next() {
		var pub_key []byte
		if err := rows.Scan(&pub_key); err != nil {
			return nil, err
		}
		items = append(items, pub_key)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const deleteZombieChannel = `-- name: DeleteZombieChannel :execresult
DELETE FROM graph_zombie_channels
WHERE scid = $1
AND version = $2
`

type DeleteZombieChannelParams struct {
	Scid    []byte
	Version int16
}

func (q *Queries) DeleteZombieChannel(ctx context.Context, arg DeleteZombieChannelParams) (sql.Result, error) {
	return q.db.ExecContext(ctx, deleteZombieChannel, arg.Scid, arg.Version)
}

const getChannelAndNodesBySCID = `-- name: GetChannelAndNodesBySCID :one
SELECT
    c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity, c.bitcoin_key_1, c.bitcoin_key_2, c.node_1_signature, c.node_2_signature, c.bitcoin_1_signature, c.bitcoin_2_signature,
    n1.pub_key AS node1_pub_key,
    n2.pub_key AS node2_pub_key
FROM graph_channels c
    JOIN graph_nodes n1 ON c.node_id_1 = n1.id
    JOIN graph_nodes n2 ON c.node_id_2 = n2.id
WHERE c.scid = $1
  AND c.version = $2
`

type GetChannelAndNodesBySCIDParams struct {
	Scid    []byte
	Version int16
}

type GetChannelAndNodesBySCIDRow struct {
	ID                int64
	Version           int16
	Scid              []byte
	NodeID1           int64
	NodeID2           int64
	Outpoint          string
	Capacity          sql.NullInt64
	BitcoinKey1       []byte
	BitcoinKey2       []byte
	Node1Signature    []byte
	Node2Signature    []byte
	Bitcoin1Signature []byte
	Bitcoin2Signature []byte
	Node1PubKey       []byte
	Node2PubKey       []byte
}

func (q *Queries) GetChannelAndNodesBySCID(ctx context.Context, arg GetChannelAndNodesBySCIDParams) (GetChannelAndNodesBySCIDRow, error) {
	row := q.db.QueryRowContext(ctx, getChannelAndNodesBySCID, arg.Scid, arg.Version)
	var i GetChannelAndNodesBySCIDRow
	err := row.Scan(
		&i.ID,
		&i.Version,
		&i.Scid,
		&i.NodeID1,
		&i.NodeID2,
		&i.Outpoint,
		&i.Capacity,
		&i.BitcoinKey1,
		&i.BitcoinKey2,
		&i.Node1Signature,
		&i.Node2Signature,
		&i.Bitcoin1Signature,
		&i.Bitcoin2Signature,
		&i.Node1PubKey,
		&i.Node2PubKey,
	)
	return i, err
}

const getChannelByOutpointWithPolicies = `-- name: GetChannelByOutpointWithPolicies :one
SELECT
    c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity, c.bitcoin_key_1, c.bitcoin_key_2, c.node_1_signature, c.node_2_signature, c.bitcoin_1_signature, c.bitcoin_2_signature,

    n1.pub_key AS node1_pubkey,
    n2.pub_key AS node2_pubkey,

    -- Node 1 policy
    cp1.id AS policy_1_id,
    cp1.node_id AS policy_1_node_id,
    cp1.version AS policy_1_version,
    cp1.timelock AS policy_1_timelock,
    cp1.fee_ppm AS policy_1_fee_ppm,
    cp1.base_fee_msat AS policy_1_base_fee_msat,
    cp1.min_htlc_msat AS policy_1_min_htlc_msat,
    cp1.max_htlc_msat AS policy_1_max_htlc_msat,
    cp1.last_update AS policy_1_last_update,
    cp1.disabled AS policy_1_disabled,
    cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat,
    cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat,
    cp1.message_flags AS policy_1_message_flags,
    cp1.channel_flags AS policy_1_channel_flags,
    cp1.signature AS policy_1_signature,

    -- Node 2 policy
    cp2.id AS policy_2_id,
    cp2.node_id AS policy_2_node_id,
    cp2.version AS policy_2_version,
    cp2.timelock AS policy_2_timelock,
    cp2.fee_ppm AS policy_2_fee_ppm,
    cp2.base_fee_msat AS policy_2_base_fee_msat,
    cp2.min_htlc_msat AS policy_2_min_htlc_msat,
    cp2.max_htlc_msat AS policy_2_max_htlc_msat,
    cp2.last_update AS policy_2_last_update,
    cp2.disabled AS policy_2_disabled,
    cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat,
    cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
    cp2.message_flags AS policy_2_message_flags,
    cp2.channel_flags AS policy_2_channel_flags,
    cp2.signature AS policy_2_signature
FROM graph_channels c
    JOIN graph_nodes n1 ON c.node_id_1 = n1.id
    JOIN graph_nodes n2 ON c.node_id_2 = n2.id
    LEFT JOIN graph_channel_policies cp1
        ON cp1.channel_id = c.id AND cp1.node_id = c.node_id_1 AND cp1.version = c.version
    LEFT JOIN graph_channel_policies cp2
        ON cp2.channel_id = c.id AND cp2.node_id = c.node_id_2 AND cp2.version = c.version
WHERE c.outpoint = $1 AND c.version = $2
`

type GetChannelByOutpointWithPoliciesParams struct {
	Outpoint string
	Version  int16
}

type GetChannelByOutpointWithPoliciesRow struct {
	GraphChannel                   GraphChannel
	Node1Pubkey                    []byte
	Node2Pubkey                    []byte
	Policy1ID                      sql.NullInt64
	Policy1NodeID                  sql.NullInt64
	Policy1Version                 sql.NullInt16
	Policy1Timelock                sql.NullInt32
	Policy1FeePpm                  sql.NullInt64
	Policy1BaseFeeMsat             sql.NullInt64
	Policy1MinHtlcMsat             sql.NullInt64
	Policy1MaxHtlcMsat             sql.NullInt64
	Policy1LastUpdate              sql.NullInt64
	Policy1Disabled                sql.NullBool
	Policy1InboundBaseFeeMsat      sql.NullInt64
	Policy1InboundFeeRateMilliMsat sql.NullInt64
	Policy1MessageFlags            sql.NullInt16
	Policy1ChannelFlags            sql.NullInt16
	Policy1Signature               []byte
	Policy2ID                      sql.NullInt64
	Policy2NodeID                  sql.NullInt64
	Policy2Version                 sql.NullInt16
	Policy2Timelock                sql.NullInt32
	Policy2FeePpm                  sql.NullInt64
	Policy2BaseFeeMsat             sql.NullInt64
	Policy2MinHtlcMsat             sql.NullInt64
	Policy2MaxHtlcMsat             sql.NullInt64
	Policy2LastUpdate              sql.NullInt64
	Policy2Disabled                sql.NullBool
	Policy2InboundBaseFeeMsat      sql.NullInt64
	Policy2InboundFeeRateMilliMsat sql.NullInt64
	Policy2MessageFlags            sql.NullInt16
	Policy2ChannelFlags            sql.NullInt16
	Policy2Signature               []byte
}

func (q *Queries) GetChannelByOutpointWithPolicies(ctx context.Context, arg GetChannelByOutpointWithPoliciesParams) (GetChannelByOutpointWithPoliciesRow, error) {
	row := q.db.QueryRowContext(ctx, getChannelByOutpointWithPolicies, arg.Outpoint, arg.Version)
	var i GetChannelByOutpointWithPoliciesRow
	err := row.Scan(
		&i.GraphChannel.ID,
		&i.GraphChannel.Version,
		&i.GraphChannel.Scid,
		&i.GraphChannel.NodeID1,
		&i.GraphChannel.NodeID2,
		&i.GraphChannel.Outpoint,
		&i.GraphChannel.Capacity,
		&i.GraphChannel.BitcoinKey1,
		&i.GraphChannel.BitcoinKey2,
		&i.GraphChannel.Node1Signature,
		&i.GraphChannel.Node2Signature,
		&i.GraphChannel.Bitcoin1Signature,
		&i.GraphChannel.Bitcoin2Signature,
		&i.Node1Pubkey,
		&i.Node2Pubkey,
		&i.Policy1ID,
		&i.Policy1NodeID,
		&i.Policy1Version,
		&i.Policy1Timelock,
		&i.Policy1FeePpm,
		&i.Policy1BaseFeeMsat,
		&i.Policy1MinHtlcMsat,
		&i.Policy1MaxHtlcMsat,
		&i.Policy1LastUpdate,
		&i.Policy1Disabled,
		&i.Policy1InboundBaseFeeMsat,
		&i.Policy1InboundFeeRateMilliMsat,
		&i.Policy1MessageFlags,
		&i.Policy1ChannelFlags,
		&i.Policy1Signature,
		&i.Policy2ID,
		&i.Policy2NodeID,
		&i.Policy2Version,
		&i.Policy2Timelock,
		&i.Policy2FeePpm,
		&i.Policy2BaseFeeMsat,
		&i.Policy2MinHtlcMsat,
		&i.Policy2MaxHtlcMsat,
		&i.Policy2LastUpdate,
		&i.Policy2Disabled,
		&i.Policy2InboundBaseFeeMsat,
		&i.Policy2InboundFeeRateMilliMsat,
		&i.Policy2MessageFlags,
		&i.Policy2ChannelFlags,
		&i.Policy2Signature,
	)
	return i, err
}

const getChannelBySCID = `-- name: GetChannelBySCID :one
SELECT id, version, scid, node_id_1, node_id_2, outpoint, capacity, bitcoin_key_1, bitcoin_key_2, node_1_signature, node_2_signature, bitcoin_1_signature, bitcoin_2_signature FROM graph_channels
WHERE scid = $1 AND version = $2
`

type GetChannelBySCIDParams struct {
	Scid    []byte
	Version int16
}

func (q *Queries) GetChannelBySCID(ctx context.Context, arg GetChannelBySCIDParams) (GraphChannel, error) {
	row := q.db.QueryRowContext(ctx, getChannelBySCID, arg.Scid, arg.Version)
	var i GraphChannel
	err := row.Scan(
		&i.ID,
		&i.Version,
		&i.Scid,
		&i.NodeID1,
		&i.NodeID2,
		&i.Outpoint,
		&i.Capacity,
		&i.BitcoinKey1,
		&i.BitcoinKey2,
		&i.Node1Signature,
		&i.Node2Signature,
		&i.Bitcoin1Signature,
		&i.Bitcoin2Signature,
	)
	return i, err
}

const getChannelBySCIDWithPolicies = `-- name: GetChannelBySCIDWithPolicies :one
SELECT
    c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity, c.bitcoin_key_1, c.bitcoin_key_2, c.node_1_signature, c.node_2_signature, c.bitcoin_1_signature, c.bitcoin_2_signature,
    n1.id, n1.version, n1.pub_key, n1.alias, n1.last_update, n1.color, n1.signature,
    n2.id, n2.version, n2.pub_key, n2.alias, n2.last_update, n2.color, n2.signature,

    -- Policy 1
    cp1.id AS policy1_id,
    cp1.node_id AS policy1_node_id,
    cp1.version AS policy1_version,
    cp1.timelock AS policy1_timelock,
    cp1.fee_ppm AS policy1_fee_ppm,
    cp1.base_fee_msat AS policy1_base_fee_msat,
    cp1.min_htlc_msat AS policy1_min_htlc_msat,
    cp1.max_htlc_msat AS policy1_max_htlc_msat,
    cp1.last_update AS policy1_last_update,
    cp1.disabled AS policy1_disabled,
    cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat,
    cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat,
    cp1.message_flags AS policy1_message_flags,
    cp1.channel_flags AS policy1_channel_flags,
    cp1.signature AS policy1_signature,

    -- Policy 2
    cp2.id AS policy2_id,
    cp2.node_id AS policy2_node_id,
    cp2.version AS policy2_version,
    cp2.timelock AS policy2_timelock,
    cp2.fee_ppm AS policy2_fee_ppm,
    cp2.base_fee_msat AS policy2_base_fee_msat,
    cp2.min_htlc_msat AS policy2_min_htlc_msat,
    cp2.max_htlc_msat AS policy2_max_htlc_msat,
    cp2.last_update AS policy2_last_update,
    cp2.disabled AS policy2_disabled,
    cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat,
    cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
    cp2.message_flags AS policy_2_message_flags,
    cp2.channel_flags AS policy_2_channel_flags,
    cp2.signature AS policy2_signature

FROM graph_channels c
    JOIN graph_nodes n1 ON c.node_id_1 = n1.id
    JOIN graph_nodes n2 ON c.node_id_2 = n2.id
    LEFT JOIN graph_channel_policies cp1
        ON cp1.channel_id = c.id AND cp1.node_id = c.node_id_1 AND cp1.version = c.version
    LEFT JOIN graph_channel_policies cp2
        ON cp2.channel_id = c.id AND cp2.node_id = c.node_id_2 AND cp2.version = c.version
WHERE c.scid = $1
  AND c.version = $2
`

type GetChannelBySCIDWithPoliciesParams struct {
	Scid    []byte
	Version int16
}

type GetChannelBySCIDWithPoliciesRow struct {
	GraphChannel                   GraphChannel
	GraphNode                      GraphNode
	GraphNode_2                    GraphNode
	Policy1ID                      sql.NullInt64
	Policy1NodeID                  sql.NullInt64
	Policy1Version                 sql.NullInt16
	Policy1Timelock                sql.NullInt32
	Policy1FeePpm                  sql.NullInt64
	Policy1BaseFeeMsat             sql.NullInt64
	Policy1MinHtlcMsat             sql.NullInt64
	Policy1MaxHtlcMsat             sql.NullInt64
	Policy1LastUpdate              sql.NullInt64
	Policy1Disabled                sql.NullBool
	Policy1InboundBaseFeeMsat      sql.NullInt64
	Policy1InboundFeeRateMilliMsat sql.NullInt64
	Policy1MessageFlags            sql.NullInt16
	Policy1ChannelFlags            sql.NullInt16
	Policy1Signature               []byte
	Policy2ID                      sql.NullInt64
	Policy2NodeID                  sql.NullInt64
	Policy2Version                 sql.NullInt16
	Policy2Timelock                sql.NullInt32
	Policy2FeePpm                  sql.NullInt64
	Policy2BaseFeeMsat             sql.NullInt64
	Policy2MinHtlcMsat             sql.NullInt64
	Policy2MaxHtlcMsat             sql.NullInt64
	Policy2LastUpdate              sql.NullInt64
	Policy2Disabled                sql.NullBool
	Policy2InboundBaseFeeMsat      sql.NullInt64
	Policy2InboundFeeRateMilliMsat sql.NullInt64
	Policy2MessageFlags            sql.NullInt16
	Policy2ChannelFlags            sql.NullInt16
	Policy2Signature               []byte
}

func (q *Queries) GetChannelBySCIDWithPolicies(ctx context.Context, arg GetChannelBySCIDWithPoliciesParams) (GetChannelBySCIDWithPoliciesRow, error) {
	row := q.db.QueryRowContext(ctx, getChannelBySCIDWithPolicies, arg.Scid, arg.Version)
	var i GetChannelBySCIDWithPoliciesRow
	err := row.Scan(
		&i.GraphChannel.ID,
		&i.GraphChannel.Version,
		&i.GraphChannel.Scid,
		&i.GraphChannel.NodeID1,
		&i.GraphChannel.NodeID2,
		&i.GraphChannel.Outpoint,
		&i.GraphChannel.Capacity,
		&i.GraphChannel.BitcoinKey1,
		&i.GraphChannel.BitcoinKey2,
		&i.GraphChannel.Node1Signature,
		&i.GraphChannel.Node2Signature,
		&i.GraphChannel.Bitcoin1Signature,
		&i.GraphChannel.Bitcoin2Signature,
		&i.GraphNode.ID,
		&i.GraphNode.Version,
		&i.GraphNode.PubKey,
		&i.GraphNode.Alias,
		&i.GraphNode.LastUpdate,
		&i.GraphNode.Color,
		&i.GraphNode.Signature,
		&i.GraphNode_2.ID,
		&i.GraphNode_2.Version,
		&i.GraphNode_2.PubKey,
		&i.GraphNode_2.Alias,
		&i.GraphNode_2.LastUpdate,
		&i.GraphNode_2.Color,
		&i.GraphNode_2.Signature,
		&i.Policy1ID,
		&i.Policy1NodeID,
		&i.Policy1Version,
		&i.Policy1Timelock,
		&i.Policy1FeePpm,
		&i.Policy1BaseFeeMsat,
		&i.Policy1MinHtlcMsat,
		&i.Policy1MaxHtlcMsat,
		&i.Policy1LastUpdate,
		&i.Policy1Disabled,
		&i.Policy1InboundBaseFeeMsat,
		&i.Policy1InboundFeeRateMilliMsat,
		&i.Policy1MessageFlags,
		&i.Policy1ChannelFlags,
		&i.Policy1Signature,
		&i.Policy2ID,
		&i.Policy2NodeID,
		&i.Policy2Version,
		&i.Policy2Timelock,
		&i.Policy2FeePpm,
		&i.Policy2BaseFeeMsat,
		&i.Policy2MinHtlcMsat,
		&i.Policy2MaxHtlcMsat,
		&i.Policy2LastUpdate,
		&i.Policy2Disabled,
		&i.Policy2InboundBaseFeeMsat,
		&i.Policy2InboundFeeRateMilliMsat,
		&i.Policy2MessageFlags,
		&i.Policy2ChannelFlags,
		&i.Policy2Signature,
	)
	return i, err
}

const getChannelExtrasBatch = `-- name: GetChannelExtrasBatch :many
SELECT
    channel_id,
    type,
    value
FROM graph_channel_extra_types
WHERE channel_id IN (/*SLICE:chan_ids*/?)
ORDER BY channel_id, type
`

func (q *Queries) GetChannelExtrasBatch(ctx context.Context, chanIds []int64) ([]GraphChannelExtraType, error) {
	query := getChannelExtrasBatch
	var queryParams []interface{}
	if len(chanIds) > 0 {
		for _, v := range chanIds {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:chan_ids*/?", makeQueryParams(len(queryParams), len(chanIds)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:chan_ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphChannelExtraType
	for rows.Next() {
		var i GraphChannelExtraType
		if err := rows.Scan(&i.ChannelID, &i.Type, &i.Value); 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 getChannelFeaturesBatch = `-- name: GetChannelFeaturesBatch :many
SELECT
    channel_id,
    feature_bit
FROM graph_channel_features
WHERE channel_id IN (/*SLICE:chan_ids*/?)
ORDER BY channel_id, feature_bit
`

func (q *Queries) GetChannelFeaturesBatch(ctx context.Context, chanIds []int64) ([]GraphChannelFeature, error) {
	query := getChannelFeaturesBatch
	var queryParams []interface{}
	if len(chanIds) > 0 {
		for _, v := range chanIds {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:chan_ids*/?", makeQueryParams(len(queryParams), len(chanIds)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:chan_ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphChannelFeature
	for rows.Next() {
		var i GraphChannelFeature
		if err := rows.Scan(&i.ChannelID, &i.FeatureBit); 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 getChannelPolicyByChannelAndNode = `-- name: GetChannelPolicyByChannelAndNode :one
SELECT id, version, channel_id, node_id, timelock, fee_ppm, base_fee_msat, min_htlc_msat, max_htlc_msat, last_update, disabled, inbound_base_fee_msat, inbound_fee_rate_milli_msat, message_flags, channel_flags, signature
FROM graph_channel_policies
WHERE channel_id = $1
  AND node_id = $2
  AND version = $3
`

type GetChannelPolicyByChannelAndNodeParams struct {
	ChannelID int64
	NodeID    int64
	Version   int16
}

func (q *Queries) GetChannelPolicyByChannelAndNode(ctx context.Context, arg GetChannelPolicyByChannelAndNodeParams) (GraphChannelPolicy, error) {
	row := q.db.QueryRowContext(ctx, getChannelPolicyByChannelAndNode, arg.ChannelID, arg.NodeID, arg.Version)
	var i GraphChannelPolicy
	err := row.Scan(
		&i.ID,
		&i.Version,
		&i.ChannelID,
		&i.NodeID,
		&i.Timelock,
		&i.FeePpm,
		&i.BaseFeeMsat,
		&i.MinHtlcMsat,
		&i.MaxHtlcMsat,
		&i.LastUpdate,
		&i.Disabled,
		&i.InboundBaseFeeMsat,
		&i.InboundFeeRateMilliMsat,
		&i.MessageFlags,
		&i.ChannelFlags,
		&i.Signature,
	)
	return i, err
}

const getChannelPolicyExtraTypesBatch = `-- name: GetChannelPolicyExtraTypesBatch :many
SELECT
    channel_policy_id as policy_id,
    type,
    value
FROM graph_channel_policy_extra_types
WHERE channel_policy_id IN (/*SLICE:policy_ids*/?)
ORDER BY channel_policy_id, type
`

type GetChannelPolicyExtraTypesBatchRow struct {
	PolicyID int64
	Type     int64
	Value    []byte
}

func (q *Queries) GetChannelPolicyExtraTypesBatch(ctx context.Context, policyIds []int64) ([]GetChannelPolicyExtraTypesBatchRow, error) {
	query := getChannelPolicyExtraTypesBatch
	var queryParams []interface{}
	if len(policyIds) > 0 {
		for _, v := range policyIds {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:policy_ids*/?", makeQueryParams(len(queryParams), len(policyIds)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:policy_ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetChannelPolicyExtraTypesBatchRow
	for rows.Next() {
		var i GetChannelPolicyExtraTypesBatchRow
		if err := rows.Scan(&i.PolicyID, &i.Type, &i.Value); 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 getChannelsByIDs = `-- name: GetChannelsByIDs :many
SELECT
    c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity, c.bitcoin_key_1, c.bitcoin_key_2, c.node_1_signature, c.node_2_signature, c.bitcoin_1_signature, c.bitcoin_2_signature,

    -- Minimal node data.
    n1.id AS node1_id,
    n1.pub_key AS node1_pub_key,
    n2.id AS node2_id,
    n2.pub_key AS node2_pub_key,

    -- Policy 1
    cp1.id AS policy1_id,
    cp1.node_id AS policy1_node_id,
    cp1.version AS policy1_version,
    cp1.timelock AS policy1_timelock,
    cp1.fee_ppm AS policy1_fee_ppm,
    cp1.base_fee_msat AS policy1_base_fee_msat,
    cp1.min_htlc_msat AS policy1_min_htlc_msat,
    cp1.max_htlc_msat AS policy1_max_htlc_msat,
    cp1.last_update AS policy1_last_update,
    cp1.disabled AS policy1_disabled,
    cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat,
    cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat,
    cp1.message_flags AS policy1_message_flags,
    cp1.channel_flags AS policy1_channel_flags,
    cp1.signature AS policy1_signature,

    -- Policy 2
    cp2.id AS policy2_id,
    cp2.node_id AS policy2_node_id,
    cp2.version AS policy2_version,
    cp2.timelock AS policy2_timelock,
    cp2.fee_ppm AS policy2_fee_ppm,
    cp2.base_fee_msat AS policy2_base_fee_msat,
    cp2.min_htlc_msat AS policy2_min_htlc_msat,
    cp2.max_htlc_msat AS policy2_max_htlc_msat,
    cp2.last_update AS policy2_last_update,
    cp2.disabled AS policy2_disabled,
    cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat,
    cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
    cp2.message_flags AS policy2_message_flags,
    cp2.channel_flags AS policy2_channel_flags,
    cp2.signature AS policy2_signature

FROM graph_channels c
    JOIN graph_nodes n1 ON c.node_id_1 = n1.id
    JOIN graph_nodes n2 ON c.node_id_2 = n2.id
    LEFT JOIN graph_channel_policies cp1
        ON cp1.channel_id = c.id AND cp1.node_id = c.node_id_1 AND cp1.version = c.version
    LEFT JOIN graph_channel_policies cp2
        ON cp2.channel_id = c.id AND cp2.node_id = c.node_id_2 AND cp2.version = c.version
WHERE c.id IN (/*SLICE:ids*/?)
`

type GetChannelsByIDsRow struct {
	GraphChannel                   GraphChannel
	Node1ID                        int64
	Node1PubKey                    []byte
	Node2ID                        int64
	Node2PubKey                    []byte
	Policy1ID                      sql.NullInt64
	Policy1NodeID                  sql.NullInt64
	Policy1Version                 sql.NullInt16
	Policy1Timelock                sql.NullInt32
	Policy1FeePpm                  sql.NullInt64
	Policy1BaseFeeMsat             sql.NullInt64
	Policy1MinHtlcMsat             sql.NullInt64
	Policy1MaxHtlcMsat             sql.NullInt64
	Policy1LastUpdate              sql.NullInt64
	Policy1Disabled                sql.NullBool
	Policy1InboundBaseFeeMsat      sql.NullInt64
	Policy1InboundFeeRateMilliMsat sql.NullInt64
	Policy1MessageFlags            sql.NullInt16
	Policy1ChannelFlags            sql.NullInt16
	Policy1Signature               []byte
	Policy2ID                      sql.NullInt64
	Policy2NodeID                  sql.NullInt64
	Policy2Version                 sql.NullInt16
	Policy2Timelock                sql.NullInt32
	Policy2FeePpm                  sql.NullInt64
	Policy2BaseFeeMsat             sql.NullInt64
	Policy2MinHtlcMsat             sql.NullInt64
	Policy2MaxHtlcMsat             sql.NullInt64
	Policy2LastUpdate              sql.NullInt64
	Policy2Disabled                sql.NullBool
	Policy2InboundBaseFeeMsat      sql.NullInt64
	Policy2InboundFeeRateMilliMsat sql.NullInt64
	Policy2MessageFlags            sql.NullInt16
	Policy2ChannelFlags            sql.NullInt16
	Policy2Signature               []byte
}

func (q *Queries) GetChannelsByIDs(ctx context.Context, ids []int64) ([]GetChannelsByIDsRow, error) {
	query := getChannelsByIDs
	var queryParams []interface{}
	if len(ids) > 0 {
		for _, v := range ids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:ids*/?", makeQueryParams(len(queryParams), len(ids)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetChannelsByIDsRow
	for rows.Next() {
		var i GetChannelsByIDsRow
		if err := rows.Scan(
			&i.GraphChannel.ID,
			&i.GraphChannel.Version,
			&i.GraphChannel.Scid,
			&i.GraphChannel.NodeID1,
			&i.GraphChannel.NodeID2,
			&i.GraphChannel.Outpoint,
			&i.GraphChannel.Capacity,
			&i.GraphChannel.BitcoinKey1,
			&i.GraphChannel.BitcoinKey2,
			&i.GraphChannel.Node1Signature,
			&i.GraphChannel.Node2Signature,
			&i.GraphChannel.Bitcoin1Signature,
			&i.GraphChannel.Bitcoin2Signature,
			&i.Node1ID,
			&i.Node1PubKey,
			&i.Node2ID,
			&i.Node2PubKey,
			&i.Policy1ID,
			&i.Policy1NodeID,
			&i.Policy1Version,
			&i.Policy1Timelock,
			&i.Policy1FeePpm,
			&i.Policy1BaseFeeMsat,
			&i.Policy1MinHtlcMsat,
			&i.Policy1MaxHtlcMsat,
			&i.Policy1LastUpdate,
			&i.Policy1Disabled,
			&i.Policy1InboundBaseFeeMsat,
			&i.Policy1InboundFeeRateMilliMsat,
			&i.Policy1MessageFlags,
			&i.Policy1ChannelFlags,
			&i.Policy1Signature,
			&i.Policy2ID,
			&i.Policy2NodeID,
			&i.Policy2Version,
			&i.Policy2Timelock,
			&i.Policy2FeePpm,
			&i.Policy2BaseFeeMsat,
			&i.Policy2MinHtlcMsat,
			&i.Policy2MaxHtlcMsat,
			&i.Policy2LastUpdate,
			&i.Policy2Disabled,
			&i.Policy2InboundBaseFeeMsat,
			&i.Policy2InboundFeeRateMilliMsat,
			&i.Policy2MessageFlags,
			&i.Policy2ChannelFlags,
			&i.Policy2Signature,
		); 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 getChannelsByOutpoints = `-- name: GetChannelsByOutpoints :many
SELECT
    c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity, c.bitcoin_key_1, c.bitcoin_key_2, c.node_1_signature, c.node_2_signature, c.bitcoin_1_signature, c.bitcoin_2_signature,
    n1.pub_key AS node1_pubkey,
    n2.pub_key AS node2_pubkey
FROM graph_channels c
    JOIN graph_nodes n1 ON c.node_id_1 = n1.id
    JOIN graph_nodes n2 ON c.node_id_2 = n2.id
WHERE c.outpoint IN
    (/*SLICE:outpoints*/?)
`

type GetChannelsByOutpointsRow struct {
	GraphChannel GraphChannel
	Node1Pubkey  []byte
	Node2Pubkey  []byte
}

func (q *Queries) GetChannelsByOutpoints(ctx context.Context, outpoints []string) ([]GetChannelsByOutpointsRow, error) {
	query := getChannelsByOutpoints
	var queryParams []interface{}
	if len(outpoints) > 0 {
		for _, v := range outpoints {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:outpoints*/?", makeQueryParams(len(queryParams), len(outpoints)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:outpoints*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetChannelsByOutpointsRow
	for rows.Next() {
		var i GetChannelsByOutpointsRow
		if err := rows.Scan(
			&i.GraphChannel.ID,
			&i.GraphChannel.Version,
			&i.GraphChannel.Scid,
			&i.GraphChannel.NodeID1,
			&i.GraphChannel.NodeID2,
			&i.GraphChannel.Outpoint,
			&i.GraphChannel.Capacity,
			&i.GraphChannel.BitcoinKey1,
			&i.GraphChannel.BitcoinKey2,
			&i.GraphChannel.Node1Signature,
			&i.GraphChannel.Node2Signature,
			&i.GraphChannel.Bitcoin1Signature,
			&i.GraphChannel.Bitcoin2Signature,
			&i.Node1Pubkey,
			&i.Node2Pubkey,
		); 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 getChannelsByPolicyLastUpdateRange = `-- name: GetChannelsByPolicyLastUpdateRange :many
SELECT
    c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity, c.bitcoin_key_1, c.bitcoin_key_2, c.node_1_signature, c.node_2_signature, c.bitcoin_1_signature, c.bitcoin_2_signature,
    n1.id, n1.version, n1.pub_key, n1.alias, n1.last_update, n1.color, n1.signature,
    n2.id, n2.version, n2.pub_key, n2.alias, n2.last_update, n2.color, n2.signature,

    -- Policy 1 (node_id_1)
    cp1.id AS policy1_id,
    cp1.node_id AS policy1_node_id,
    cp1.version AS policy1_version,
    cp1.timelock AS policy1_timelock,
    cp1.fee_ppm AS policy1_fee_ppm,
    cp1.base_fee_msat AS policy1_base_fee_msat,
    cp1.min_htlc_msat AS policy1_min_htlc_msat,
    cp1.max_htlc_msat AS policy1_max_htlc_msat,
    cp1.last_update AS policy1_last_update,
    cp1.disabled AS policy1_disabled,
    cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat,
    cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat,
    cp1.message_flags AS policy1_message_flags,
    cp1.channel_flags AS policy1_channel_flags,
    cp1.signature AS policy1_signature,

    -- Policy 2 (node_id_2)
    cp2.id AS policy2_id,
    cp2.node_id AS policy2_node_id,
    cp2.version AS policy2_version,
    cp2.timelock AS policy2_timelock,
    cp2.fee_ppm AS policy2_fee_ppm,
    cp2.base_fee_msat AS policy2_base_fee_msat,
    cp2.min_htlc_msat AS policy2_min_htlc_msat,
    cp2.max_htlc_msat AS policy2_max_htlc_msat,
    cp2.last_update AS policy2_last_update,
    cp2.disabled AS policy2_disabled,
    cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat,
    cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
    cp2.message_flags AS policy2_message_flags,
    cp2.channel_flags AS policy2_channel_flags,
    cp2.signature AS policy2_signature

FROM graph_channels c
    JOIN graph_nodes n1 ON c.node_id_1 = n1.id
    JOIN graph_nodes n2 ON c.node_id_2 = n2.id
    LEFT JOIN graph_channel_policies cp1
        ON cp1.channel_id = c.id AND cp1.node_id = c.node_id_1 AND cp1.version = c.version
    LEFT JOIN graph_channel_policies cp2
        ON cp2.channel_id = c.id AND cp2.node_id = c.node_id_2 AND cp2.version = c.version
WHERE c.version = $1
  AND (
       (cp1.last_update >= $2 AND cp1.last_update < $3)
       OR
       (cp2.last_update >= $2 AND cp2.last_update < $3)
  )
  -- Pagination using compound cursor (max_update_time, id).
  -- We use COALESCE with -1 as sentinel since timestamps are always positive.
  AND (
       (CASE
           WHEN COALESCE(cp1.last_update, 0) >= COALESCE(cp2.last_update, 0)
               THEN COALESCE(cp1.last_update, 0)
           ELSE COALESCE(cp2.last_update, 0)
       END > COALESCE($4, -1))
       OR 
       (CASE
           WHEN COALESCE(cp1.last_update, 0) >= COALESCE(cp2.last_update, 0)
               THEN COALESCE(cp1.last_update, 0)
           ELSE COALESCE(cp2.last_update, 0)
       END = COALESCE($4, -1) 
       AND c.id > COALESCE($5, -1))
  )
ORDER BY
    CASE
        WHEN COALESCE(cp1.last_update, 0) >= COALESCE(cp2.last_update, 0)
            THEN COALESCE(cp1.last_update, 0)
        ELSE COALESCE(cp2.last_update, 0)
    END ASC,
    c.id ASC
LIMIT COALESCE($6, 999999999)
`

type GetChannelsByPolicyLastUpdateRangeParams struct {
	Version        int16
	StartTime      sql.NullInt64
	EndTime        sql.NullInt64
	LastUpdateTime sql.NullInt64
	LastID         sql.NullInt64
	MaxResults     interface{}
}

type GetChannelsByPolicyLastUpdateRangeRow struct {
	GraphChannel                   GraphChannel
	GraphNode                      GraphNode
	GraphNode_2                    GraphNode
	Policy1ID                      sql.NullInt64
	Policy1NodeID                  sql.NullInt64
	Policy1Version                 sql.NullInt16
	Policy1Timelock                sql.NullInt32
	Policy1FeePpm                  sql.NullInt64
	Policy1BaseFeeMsat             sql.NullInt64
	Policy1MinHtlcMsat             sql.NullInt64
	Policy1MaxHtlcMsat             sql.NullInt64
	Policy1LastUpdate              sql.NullInt64
	Policy1Disabled                sql.NullBool
	Policy1InboundBaseFeeMsat      sql.NullInt64
	Policy1InboundFeeRateMilliMsat sql.NullInt64
	Policy1MessageFlags            sql.NullInt16
	Policy1ChannelFlags            sql.NullInt16
	Policy1Signature               []byte
	Policy2ID                      sql.NullInt64
	Policy2NodeID                  sql.NullInt64
	Policy2Version                 sql.NullInt16
	Policy2Timelock                sql.NullInt32
	Policy2FeePpm                  sql.NullInt64
	Policy2BaseFeeMsat             sql.NullInt64
	Policy2MinHtlcMsat             sql.NullInt64
	Policy2MaxHtlcMsat             sql.NullInt64
	Policy2LastUpdate              sql.NullInt64
	Policy2Disabled                sql.NullBool
	Policy2InboundBaseFeeMsat      sql.NullInt64
	Policy2InboundFeeRateMilliMsat sql.NullInt64
	Policy2MessageFlags            sql.NullInt16
	Policy2ChannelFlags            sql.NullInt16
	Policy2Signature               []byte
}

func (q *Queries) GetChannelsByPolicyLastUpdateRange(ctx context.Context, arg GetChannelsByPolicyLastUpdateRangeParams) ([]GetChannelsByPolicyLastUpdateRangeRow, error) {
	rows, err := q.db.QueryContext(ctx, getChannelsByPolicyLastUpdateRange,
		arg.Version,
		arg.StartTime,
		arg.EndTime,
		arg.LastUpdateTime,
		arg.LastID,
		arg.MaxResults,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetChannelsByPolicyLastUpdateRangeRow
	for rows.Next() {
		var i GetChannelsByPolicyLastUpdateRangeRow
		if err := rows.Scan(
			&i.GraphChannel.ID,
			&i.GraphChannel.Version,
			&i.GraphChannel.Scid,
			&i.GraphChannel.NodeID1,
			&i.GraphChannel.NodeID2,
			&i.GraphChannel.Outpoint,
			&i.GraphChannel.Capacity,
			&i.GraphChannel.BitcoinKey1,
			&i.GraphChannel.BitcoinKey2,
			&i.GraphChannel.Node1Signature,
			&i.GraphChannel.Node2Signature,
			&i.GraphChannel.Bitcoin1Signature,
			&i.GraphChannel.Bitcoin2Signature,
			&i.GraphNode.ID,
			&i.GraphNode.Version,
			&i.GraphNode.PubKey,
			&i.GraphNode.Alias,
			&i.GraphNode.LastUpdate,
			&i.GraphNode.Color,
			&i.GraphNode.Signature,
			&i.GraphNode_2.ID,
			&i.GraphNode_2.Version,
			&i.GraphNode_2.PubKey,
			&i.GraphNode_2.Alias,
			&i.GraphNode_2.LastUpdate,
			&i.GraphNode_2.Color,
			&i.GraphNode_2.Signature,
			&i.Policy1ID,
			&i.Policy1NodeID,
			&i.Policy1Version,
			&i.Policy1Timelock,
			&i.Policy1FeePpm,
			&i.Policy1BaseFeeMsat,
			&i.Policy1MinHtlcMsat,
			&i.Policy1MaxHtlcMsat,
			&i.Policy1LastUpdate,
			&i.Policy1Disabled,
			&i.Policy1InboundBaseFeeMsat,
			&i.Policy1InboundFeeRateMilliMsat,
			&i.Policy1MessageFlags,
			&i.Policy1ChannelFlags,
			&i.Policy1Signature,
			&i.Policy2ID,
			&i.Policy2NodeID,
			&i.Policy2Version,
			&i.Policy2Timelock,
			&i.Policy2FeePpm,
			&i.Policy2BaseFeeMsat,
			&i.Policy2MinHtlcMsat,
			&i.Policy2MaxHtlcMsat,
			&i.Policy2LastUpdate,
			&i.Policy2Disabled,
			&i.Policy2InboundBaseFeeMsat,
			&i.Policy2InboundFeeRateMilliMsat,
			&i.Policy2MessageFlags,
			&i.Policy2ChannelFlags,
			&i.Policy2Signature,
		); 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 getChannelsBySCIDRange = `-- name: GetChannelsBySCIDRange :many
SELECT c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity, c.bitcoin_key_1, c.bitcoin_key_2, c.node_1_signature, c.node_2_signature, c.bitcoin_1_signature, c.bitcoin_2_signature,
    n1.pub_key AS node1_pub_key,
    n2.pub_key AS node2_pub_key
FROM graph_channels c
    JOIN graph_nodes n1 ON c.node_id_1 = n1.id
    JOIN graph_nodes n2 ON c.node_id_2 = n2.id
WHERE scid >= $1
  AND scid < $2
`

type GetChannelsBySCIDRangeParams struct {
	StartScid []byte
	EndScid   []byte
}

type GetChannelsBySCIDRangeRow struct {
	GraphChannel GraphChannel
	Node1PubKey  []byte
	Node2PubKey  []byte
}

func (q *Queries) GetChannelsBySCIDRange(ctx context.Context, arg GetChannelsBySCIDRangeParams) ([]GetChannelsBySCIDRangeRow, error) {
	rows, err := q.db.QueryContext(ctx, getChannelsBySCIDRange, arg.StartScid, arg.EndScid)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetChannelsBySCIDRangeRow
	for rows.Next() {
		var i GetChannelsBySCIDRangeRow
		if err := rows.Scan(
			&i.GraphChannel.ID,
			&i.GraphChannel.Version,
			&i.GraphChannel.Scid,
			&i.GraphChannel.NodeID1,
			&i.GraphChannel.NodeID2,
			&i.GraphChannel.Outpoint,
			&i.GraphChannel.Capacity,
			&i.GraphChannel.BitcoinKey1,
			&i.GraphChannel.BitcoinKey2,
			&i.GraphChannel.Node1Signature,
			&i.GraphChannel.Node2Signature,
			&i.GraphChannel.Bitcoin1Signature,
			&i.GraphChannel.Bitcoin2Signature,
			&i.Node1PubKey,
			&i.Node2PubKey,
		); 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 getChannelsBySCIDWithPolicies = `-- name: GetChannelsBySCIDWithPolicies :many
SELECT
    c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity, c.bitcoin_key_1, c.bitcoin_key_2, c.node_1_signature, c.node_2_signature, c.bitcoin_1_signature, c.bitcoin_2_signature,
    n1.id, n1.version, n1.pub_key, n1.alias, n1.last_update, n1.color, n1.signature,
    n2.id, n2.version, n2.pub_key, n2.alias, n2.last_update, n2.color, n2.signature,

    -- Policy 1
    cp1.id AS policy1_id,
    cp1.node_id AS policy1_node_id,
    cp1.version AS policy1_version,
    cp1.timelock AS policy1_timelock,
    cp1.fee_ppm AS policy1_fee_ppm,
    cp1.base_fee_msat AS policy1_base_fee_msat,
    cp1.min_htlc_msat AS policy1_min_htlc_msat,
    cp1.max_htlc_msat AS policy1_max_htlc_msat,
    cp1.last_update AS policy1_last_update,
    cp1.disabled AS policy1_disabled,
    cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat,
    cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat,
    cp1.message_flags AS policy1_message_flags,
    cp1.channel_flags AS policy1_channel_flags,
    cp1.signature AS policy1_signature,

    -- Policy 2
    cp2.id AS policy2_id,
    cp2.node_id AS policy2_node_id,
    cp2.version AS policy2_version,
    cp2.timelock AS policy2_timelock,
    cp2.fee_ppm AS policy2_fee_ppm,
    cp2.base_fee_msat AS policy2_base_fee_msat,
    cp2.min_htlc_msat AS policy2_min_htlc_msat,
    cp2.max_htlc_msat AS policy2_max_htlc_msat,
    cp2.last_update AS policy2_last_update,
    cp2.disabled AS policy2_disabled,
    cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat,
    cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
    cp2.message_flags AS policy_2_message_flags,
    cp2.channel_flags AS policy_2_channel_flags,
    cp2.signature AS policy2_signature

FROM graph_channels c
    JOIN graph_nodes n1 ON c.node_id_1 = n1.id
    JOIN graph_nodes n2 ON c.node_id_2 = n2.id
    LEFT JOIN graph_channel_policies cp1
        ON cp1.channel_id = c.id AND cp1.node_id = c.node_id_1 AND cp1.version = c.version
    LEFT JOIN graph_channel_policies cp2
        ON cp2.channel_id = c.id AND cp2.node_id = c.node_id_2 AND cp2.version = c.version
WHERE
    c.version = $1
  AND c.scid IN (/*SLICE:scids*/?)
`

type GetChannelsBySCIDWithPoliciesParams struct {
	Version int16
	Scids   [][]byte
}

type GetChannelsBySCIDWithPoliciesRow struct {
	GraphChannel                   GraphChannel
	GraphNode                      GraphNode
	GraphNode_2                    GraphNode
	Policy1ID                      sql.NullInt64
	Policy1NodeID                  sql.NullInt64
	Policy1Version                 sql.NullInt16
	Policy1Timelock                sql.NullInt32
	Policy1FeePpm                  sql.NullInt64
	Policy1BaseFeeMsat             sql.NullInt64
	Policy1MinHtlcMsat             sql.NullInt64
	Policy1MaxHtlcMsat             sql.NullInt64
	Policy1LastUpdate              sql.NullInt64
	Policy1Disabled                sql.NullBool
	Policy1InboundBaseFeeMsat      sql.NullInt64
	Policy1InboundFeeRateMilliMsat sql.NullInt64
	Policy1MessageFlags            sql.NullInt16
	Policy1ChannelFlags            sql.NullInt16
	Policy1Signature               []byte
	Policy2ID                      sql.NullInt64
	Policy2NodeID                  sql.NullInt64
	Policy2Version                 sql.NullInt16
	Policy2Timelock                sql.NullInt32
	Policy2FeePpm                  sql.NullInt64
	Policy2BaseFeeMsat             sql.NullInt64
	Policy2MinHtlcMsat             sql.NullInt64
	Policy2MaxHtlcMsat             sql.NullInt64
	Policy2LastUpdate              sql.NullInt64
	Policy2Disabled                sql.NullBool
	Policy2InboundBaseFeeMsat      sql.NullInt64
	Policy2InboundFeeRateMilliMsat sql.NullInt64
	Policy2MessageFlags            sql.NullInt16
	Policy2ChannelFlags            sql.NullInt16
	Policy2Signature               []byte
}

func (q *Queries) GetChannelsBySCIDWithPolicies(ctx context.Context, arg GetChannelsBySCIDWithPoliciesParams) ([]GetChannelsBySCIDWithPoliciesRow, error) {
	query := getChannelsBySCIDWithPolicies
	var queryParams []interface{}
	queryParams = append(queryParams, arg.Version)
	if len(arg.Scids) > 0 {
		for _, v := range arg.Scids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:scids*/?", makeQueryParams(len(queryParams), len(arg.Scids)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:scids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetChannelsBySCIDWithPoliciesRow
	for rows.Next() {
		var i GetChannelsBySCIDWithPoliciesRow
		if err := rows.Scan(
			&i.GraphChannel.ID,
			&i.GraphChannel.Version,
			&i.GraphChannel.Scid,
			&i.GraphChannel.NodeID1,
			&i.GraphChannel.NodeID2,
			&i.GraphChannel.Outpoint,
			&i.GraphChannel.Capacity,
			&i.GraphChannel.BitcoinKey1,
			&i.GraphChannel.BitcoinKey2,
			&i.GraphChannel.Node1Signature,
			&i.GraphChannel.Node2Signature,
			&i.GraphChannel.Bitcoin1Signature,
			&i.GraphChannel.Bitcoin2Signature,
			&i.GraphNode.ID,
			&i.GraphNode.Version,
			&i.GraphNode.PubKey,
			&i.GraphNode.Alias,
			&i.GraphNode.LastUpdate,
			&i.GraphNode.Color,
			&i.GraphNode.Signature,
			&i.GraphNode_2.ID,
			&i.GraphNode_2.Version,
			&i.GraphNode_2.PubKey,
			&i.GraphNode_2.Alias,
			&i.GraphNode_2.LastUpdate,
			&i.GraphNode_2.Color,
			&i.GraphNode_2.Signature,
			&i.Policy1ID,
			&i.Policy1NodeID,
			&i.Policy1Version,
			&i.Policy1Timelock,
			&i.Policy1FeePpm,
			&i.Policy1BaseFeeMsat,
			&i.Policy1MinHtlcMsat,
			&i.Policy1MaxHtlcMsat,
			&i.Policy1LastUpdate,
			&i.Policy1Disabled,
			&i.Policy1InboundBaseFeeMsat,
			&i.Policy1InboundFeeRateMilliMsat,
			&i.Policy1MessageFlags,
			&i.Policy1ChannelFlags,
			&i.Policy1Signature,
			&i.Policy2ID,
			&i.Policy2NodeID,
			&i.Policy2Version,
			&i.Policy2Timelock,
			&i.Policy2FeePpm,
			&i.Policy2BaseFeeMsat,
			&i.Policy2MinHtlcMsat,
			&i.Policy2MaxHtlcMsat,
			&i.Policy2LastUpdate,
			&i.Policy2Disabled,
			&i.Policy2InboundBaseFeeMsat,
			&i.Policy2InboundFeeRateMilliMsat,
			&i.Policy2MessageFlags,
			&i.Policy2ChannelFlags,
			&i.Policy2Signature,
		); 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 getChannelsBySCIDs = `-- name: GetChannelsBySCIDs :many
SELECT id, version, scid, node_id_1, node_id_2, outpoint, capacity, bitcoin_key_1, bitcoin_key_2, node_1_signature, node_2_signature, bitcoin_1_signature, bitcoin_2_signature FROM graph_channels
WHERE version = $1
  AND scid IN (/*SLICE:scids*/?)
`

type GetChannelsBySCIDsParams struct {
	Version int16
	Scids   [][]byte
}

func (q *Queries) GetChannelsBySCIDs(ctx context.Context, arg GetChannelsBySCIDsParams) ([]GraphChannel, error) {
	query := getChannelsBySCIDs
	var queryParams []interface{}
	queryParams = append(queryParams, arg.Version)
	if len(arg.Scids) > 0 {
		for _, v := range arg.Scids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:scids*/?", makeQueryParams(len(queryParams), len(arg.Scids)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:scids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphChannel
	for rows.Next() {
		var i GraphChannel
		if err := rows.Scan(
			&i.ID,
			&i.Version,
			&i.Scid,
			&i.NodeID1,
			&i.NodeID2,
			&i.Outpoint,
			&i.Capacity,
			&i.BitcoinKey1,
			&i.BitcoinKey2,
			&i.Node1Signature,
			&i.Node2Signature,
			&i.Bitcoin1Signature,
			&i.Bitcoin2Signature,
		); 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 getClosedChannelsSCIDs = `-- name: GetClosedChannelsSCIDs :many
SELECT scid
FROM graph_closed_scids
WHERE scid IN (/*SLICE:scids*/?)
`

func (q *Queries) GetClosedChannelsSCIDs(ctx context.Context, scids [][]byte) ([][]byte, error) {
	query := getClosedChannelsSCIDs
	var queryParams []interface{}
	if len(scids) > 0 {
		for _, v := range scids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:scids*/?", makeQueryParams(len(queryParams), len(scids)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:scids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items [][]byte
	for rows.Next() {
		var scid []byte
		if err := rows.Scan(&scid); err != nil {
			return nil, err
		}
		items = append(items, scid)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getExtraNodeTypes = `-- name: GetExtraNodeTypes :many
SELECT node_id, type, value
FROM graph_node_extra_types
WHERE node_id = $1
`

func (q *Queries) GetExtraNodeTypes(ctx context.Context, nodeID int64) ([]GraphNodeExtraType, error) {
	rows, err := q.db.QueryContext(ctx, getExtraNodeTypes, nodeID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphNodeExtraType
	for rows.Next() {
		var i GraphNodeExtraType
		if err := rows.Scan(&i.NodeID, &i.Type, &i.Value); 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 getNodeAddresses = `-- name: GetNodeAddresses :many
SELECT type, address
FROM graph_node_addresses
WHERE node_id = $1
ORDER BY type ASC, position ASC
`

type GetNodeAddressesRow struct {
	Type    int16
	Address string
}

func (q *Queries) GetNodeAddresses(ctx context.Context, nodeID int64) ([]GetNodeAddressesRow, error) {
	rows, err := q.db.QueryContext(ctx, getNodeAddresses, nodeID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetNodeAddressesRow
	for rows.Next() {
		var i GetNodeAddressesRow
		if err := rows.Scan(&i.Type, &i.Address); 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 getNodeAddressesBatch = `-- name: GetNodeAddressesBatch :many
SELECT node_id, type, position, address
FROM graph_node_addresses
WHERE node_id IN (/*SLICE:ids*/?)
ORDER BY node_id, type, position
`

func (q *Queries) GetNodeAddressesBatch(ctx context.Context, ids []int64) ([]GraphNodeAddress, error) {
	query := getNodeAddressesBatch
	var queryParams []interface{}
	if len(ids) > 0 {
		for _, v := range ids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:ids*/?", makeQueryParams(len(queryParams), len(ids)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphNodeAddress
	for rows.Next() {
		var i GraphNodeAddress
		if err := rows.Scan(
			&i.NodeID,
			&i.Type,
			&i.Position,
			&i.Address,
		); 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 getNodeByPubKey = `-- name: GetNodeByPubKey :one
SELECT id, version, pub_key, alias, last_update, color, signature
FROM graph_nodes
WHERE pub_key = $1
  AND version = $2
`

type GetNodeByPubKeyParams struct {
	PubKey  []byte
	Version int16
}

func (q *Queries) GetNodeByPubKey(ctx context.Context, arg GetNodeByPubKeyParams) (GraphNode, error) {
	row := q.db.QueryRowContext(ctx, getNodeByPubKey, arg.PubKey, arg.Version)
	var i GraphNode
	err := row.Scan(
		&i.ID,
		&i.Version,
		&i.PubKey,
		&i.Alias,
		&i.LastUpdate,
		&i.Color,
		&i.Signature,
	)
	return i, err
}

const getNodeExtraTypesBatch = `-- name: GetNodeExtraTypesBatch :many
SELECT node_id, type, value
FROM graph_node_extra_types
WHERE node_id IN (/*SLICE:ids*/?)
ORDER BY node_id, type
`

func (q *Queries) GetNodeExtraTypesBatch(ctx context.Context, ids []int64) ([]GraphNodeExtraType, error) {
	query := getNodeExtraTypesBatch
	var queryParams []interface{}
	if len(ids) > 0 {
		for _, v := range ids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:ids*/?", makeQueryParams(len(queryParams), len(ids)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphNodeExtraType
	for rows.Next() {
		var i GraphNodeExtraType
		if err := rows.Scan(&i.NodeID, &i.Type, &i.Value); 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 getNodeFeatures = `-- name: GetNodeFeatures :many
SELECT node_id, feature_bit
FROM graph_node_features
WHERE node_id = $1
`

func (q *Queries) GetNodeFeatures(ctx context.Context, nodeID int64) ([]GraphNodeFeature, error) {
	rows, err := q.db.QueryContext(ctx, getNodeFeatures, nodeID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphNodeFeature
	for rows.Next() {
		var i GraphNodeFeature
		if err := rows.Scan(&i.NodeID, &i.FeatureBit); 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 getNodeFeaturesBatch = `-- name: GetNodeFeaturesBatch :many
SELECT node_id, feature_bit
FROM graph_node_features
WHERE node_id IN (/*SLICE:ids*/?)
ORDER BY node_id, feature_bit
`

func (q *Queries) GetNodeFeaturesBatch(ctx context.Context, ids []int64) ([]GraphNodeFeature, error) {
	query := getNodeFeaturesBatch
	var queryParams []interface{}
	if len(ids) > 0 {
		for _, v := range ids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:ids*/?", makeQueryParams(len(queryParams), len(ids)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphNodeFeature
	for rows.Next() {
		var i GraphNodeFeature
		if err := rows.Scan(&i.NodeID, &i.FeatureBit); 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 getNodeFeaturesByPubKey = `-- name: GetNodeFeaturesByPubKey :many
SELECT f.feature_bit
FROM graph_nodes n
    JOIN graph_node_features f ON f.node_id = n.id
WHERE n.pub_key = $1
  AND n.version = $2
`

type GetNodeFeaturesByPubKeyParams struct {
	PubKey  []byte
	Version int16
}

func (q *Queries) GetNodeFeaturesByPubKey(ctx context.Context, arg GetNodeFeaturesByPubKeyParams) ([]int32, error) {
	rows, err := q.db.QueryContext(ctx, getNodeFeaturesByPubKey, arg.PubKey, arg.Version)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []int32
	for rows.Next() {
		var feature_bit int32
		if err := rows.Scan(&feature_bit); err != nil {
			return nil, err
		}
		items = append(items, feature_bit)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getNodeIDByPubKey = `-- name: GetNodeIDByPubKey :one
SELECT id
FROM graph_nodes
WHERE pub_key = $1
  AND version = $2
`

type GetNodeIDByPubKeyParams struct {
	PubKey  []byte
	Version int16
}

func (q *Queries) GetNodeIDByPubKey(ctx context.Context, arg GetNodeIDByPubKeyParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, getNodeIDByPubKey, arg.PubKey, arg.Version)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const getNodesByIDs = `-- name: GetNodesByIDs :many
SELECT id, version, pub_key, alias, last_update, color, signature
FROM graph_nodes
WHERE id IN (/*SLICE:ids*/?)
`

func (q *Queries) GetNodesByIDs(ctx context.Context, ids []int64) ([]GraphNode, error) {
	query := getNodesByIDs
	var queryParams []interface{}
	if len(ids) > 0 {
		for _, v := range ids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:ids*/?", makeQueryParams(len(queryParams), len(ids)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphNode
	for rows.Next() {
		var i GraphNode
		if err := rows.Scan(
			&i.ID,
			&i.Version,
			&i.PubKey,
			&i.Alias,
			&i.LastUpdate,
			&i.Color,
			&i.Signature,
		); 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 getNodesByLastUpdateRange = `-- name: GetNodesByLastUpdateRange :many
SELECT id, version, pub_key, alias, last_update, color, signature
FROM graph_nodes
WHERE last_update >= $1
  AND last_update <= $2
  -- Pagination: We use (last_update, pub_key) as a compound cursor.
  -- This ensures stable ordering and allows us to resume from where we left off.
  -- We use COALESCE with -1 as sentinel since timestamps are always positive.
  AND (
    -- Include rows with last_update greater than cursor (or all rows if cursor is -1)
    last_update > COALESCE($3, -1)
    OR 
    -- For rows with same last_update, use pub_key as tiebreaker
    (last_update = COALESCE($3, -1) 
     AND pub_key > $4)
  )
  -- Optional filter for public nodes only
  AND (
    -- If only_public is false or not provided, include all nodes
    COALESCE($5, FALSE) IS FALSE
    OR 
    -- For V1 protocol, a node is public if it has at least one public channel.
    -- A public channel has bitcoin_1_signature set (channel announcement received).
    EXISTS (
      SELECT 1
      FROM graph_channels c
      WHERE c.version = 1
        AND c.bitcoin_1_signature IS NOT NULL
        AND (c.node_id_1 = graph_nodes.id OR c.node_id_2 = graph_nodes.id)
    )
  )
ORDER BY last_update ASC, pub_key ASC
LIMIT COALESCE($6, 999999999)
`

type GetNodesByLastUpdateRangeParams struct {
	StartTime  sql.NullInt64
	EndTime    sql.NullInt64
	LastUpdate sql.NullInt64
	LastPubKey []byte
	OnlyPublic interface{}
	MaxResults interface{}
}

func (q *Queries) GetNodesByLastUpdateRange(ctx context.Context, arg GetNodesByLastUpdateRangeParams) ([]GraphNode, error) {
	rows, err := q.db.QueryContext(ctx, getNodesByLastUpdateRange,
		arg.StartTime,
		arg.EndTime,
		arg.LastUpdate,
		arg.LastPubKey,
		arg.OnlyPublic,
		arg.MaxResults,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphNode
	for rows.Next() {
		var i GraphNode
		if err := rows.Scan(
			&i.ID,
			&i.Version,
			&i.PubKey,
			&i.Alias,
			&i.LastUpdate,
			&i.Color,
			&i.Signature,
		); 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 getPruneEntriesForHeights = `-- name: GetPruneEntriesForHeights :many
SELECT block_height, block_hash
FROM graph_prune_log
WHERE block_height
   IN (/*SLICE:heights*/?)
`

func (q *Queries) GetPruneEntriesForHeights(ctx context.Context, heights []int64) ([]GraphPruneLog, error) {
	query := getPruneEntriesForHeights
	var queryParams []interface{}
	if len(heights) > 0 {
		for _, v := range heights {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:heights*/?", makeQueryParams(len(queryParams), len(heights)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:heights*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphPruneLog
	for rows.Next() {
		var i GraphPruneLog
		if err := rows.Scan(&i.BlockHeight, &i.BlockHash); 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 getPruneHashByHeight = `-- name: GetPruneHashByHeight :one
SELECT block_hash
FROM graph_prune_log
WHERE block_height = $1
`

func (q *Queries) GetPruneHashByHeight(ctx context.Context, blockHeight int64) ([]byte, error) {
	row := q.db.QueryRowContext(ctx, getPruneHashByHeight, blockHeight)
	var block_hash []byte
	err := row.Scan(&block_hash)
	return block_hash, err
}

const getPruneTip = `-- name: GetPruneTip :one
SELECT block_height, block_hash
FROM graph_prune_log
ORDER BY block_height DESC
LIMIT 1
`

func (q *Queries) GetPruneTip(ctx context.Context) (GraphPruneLog, error) {
	row := q.db.QueryRowContext(ctx, getPruneTip)
	var i GraphPruneLog
	err := row.Scan(&i.BlockHeight, &i.BlockHash)
	return i, err
}

const getPublicV1ChannelsBySCID = `-- name: GetPublicV1ChannelsBySCID :many
SELECT id, version, scid, node_id_1, node_id_2, outpoint, capacity, bitcoin_key_1, bitcoin_key_2, node_1_signature, node_2_signature, bitcoin_1_signature, bitcoin_2_signature
FROM graph_channels
WHERE node_1_signature IS NOT NULL
  AND scid >= $1
  AND scid < $2
`

type GetPublicV1ChannelsBySCIDParams struct {
	StartScid []byte
	EndScid   []byte
}

func (q *Queries) GetPublicV1ChannelsBySCID(ctx context.Context, arg GetPublicV1ChannelsBySCIDParams) ([]GraphChannel, error) {
	rows, err := q.db.QueryContext(ctx, getPublicV1ChannelsBySCID, arg.StartScid, arg.EndScid)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphChannel
	for rows.Next() {
		var i GraphChannel
		if err := rows.Scan(
			&i.ID,
			&i.Version,
			&i.Scid,
			&i.NodeID1,
			&i.NodeID2,
			&i.Outpoint,
			&i.Capacity,
			&i.BitcoinKey1,
			&i.BitcoinKey2,
			&i.Node1Signature,
			&i.Node2Signature,
			&i.Bitcoin1Signature,
			&i.Bitcoin2Signature,
		); 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 getSCIDByOutpoint = `-- name: GetSCIDByOutpoint :one
SELECT scid from graph_channels
WHERE outpoint = $1 AND version = $2
`

type GetSCIDByOutpointParams struct {
	Outpoint string
	Version  int16
}

func (q *Queries) GetSCIDByOutpoint(ctx context.Context, arg GetSCIDByOutpointParams) ([]byte, error) {
	row := q.db.QueryRowContext(ctx, getSCIDByOutpoint, arg.Outpoint, arg.Version)
	var scid []byte
	err := row.Scan(&scid)
	return scid, err
}

const getSourceNodesByVersion = `-- name: GetSourceNodesByVersion :many
SELECT sn.node_id, n.pub_key
FROM graph_source_nodes sn
    JOIN graph_nodes n ON sn.node_id = n.id
WHERE n.version = $1
`

type GetSourceNodesByVersionRow struct {
	NodeID int64
	PubKey []byte
}

func (q *Queries) GetSourceNodesByVersion(ctx context.Context, version int16) ([]GetSourceNodesByVersionRow, error) {
	rows, err := q.db.QueryContext(ctx, getSourceNodesByVersion, version)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GetSourceNodesByVersionRow
	for rows.Next() {
		var i GetSourceNodesByVersionRow
		if err := rows.Scan(&i.NodeID, &i.PubKey); 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 getV1DisabledSCIDs = `-- name: GetV1DisabledSCIDs :many
SELECT c.scid
FROM graph_channels c
    JOIN graph_channel_policies cp ON cp.channel_id = c.id
WHERE cp.disabled = true
AND c.version = 1
GROUP BY c.scid
HAVING COUNT(*) > 1
`

// NOTE: this is V1 specific since for V1, disabled is a
// simple, single boolean. The proposed V2 policy
// structure will have a more complex disabled bit vector
// and so the query for V2 may differ.
func (q *Queries) GetV1DisabledSCIDs(ctx context.Context) ([][]byte, error) {
	rows, err := q.db.QueryContext(ctx, getV1DisabledSCIDs)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items [][]byte
	for rows.Next() {
		var scid []byte
		if err := rows.Scan(&scid); err != nil {
			return nil, err
		}
		items = append(items, scid)
	}
	if err := rows.Close(); err != nil {
		return nil, err
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

const getZombieChannel = `-- name: GetZombieChannel :one
SELECT scid, version, node_key_1, node_key_2
FROM graph_zombie_channels
WHERE scid = $1
AND version = $2
`

type GetZombieChannelParams struct {
	Scid    []byte
	Version int16
}

func (q *Queries) GetZombieChannel(ctx context.Context, arg GetZombieChannelParams) (GraphZombieChannel, error) {
	row := q.db.QueryRowContext(ctx, getZombieChannel, arg.Scid, arg.Version)
	var i GraphZombieChannel
	err := row.Scan(
		&i.Scid,
		&i.Version,
		&i.NodeKey1,
		&i.NodeKey2,
	)
	return i, err
}

const getZombieChannelsSCIDs = `-- name: GetZombieChannelsSCIDs :many
SELECT scid, version, node_key_1, node_key_2
FROM graph_zombie_channels
WHERE version = $1
  AND scid IN (/*SLICE:scids*/?)
`

type GetZombieChannelsSCIDsParams struct {
	Version int16
	Scids   [][]byte
}

func (q *Queries) GetZombieChannelsSCIDs(ctx context.Context, arg GetZombieChannelsSCIDsParams) ([]GraphZombieChannel, error) {
	query := getZombieChannelsSCIDs
	var queryParams []interface{}
	queryParams = append(queryParams, arg.Version)
	if len(arg.Scids) > 0 {
		for _, v := range arg.Scids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:scids*/?", makeQueryParams(len(queryParams), len(arg.Scids)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:scids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphZombieChannel
	for rows.Next() {
		var i GraphZombieChannel
		if err := rows.Scan(
			&i.Scid,
			&i.Version,
			&i.NodeKey1,
			&i.NodeKey2,
		); 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 highestSCID = `-- name: HighestSCID :one
SELECT scid
FROM graph_channels
WHERE version = $1
ORDER BY scid DESC
LIMIT 1
`

func (q *Queries) HighestSCID(ctx context.Context, version int16) ([]byte, error) {
	row := q.db.QueryRowContext(ctx, highestSCID, version)
	var scid []byte
	err := row.Scan(&scid)
	return scid, err
}

const insertChannelFeature = `-- name: InsertChannelFeature :exec
/* ─────────────────────────────────────────────
   graph_channel_features table queries
   ─────────────────────────────────────────────
*/

INSERT INTO graph_channel_features (
    channel_id, feature_bit
) VALUES (
    $1, $2
) ON CONFLICT (channel_id, feature_bit)
    -- Do nothing if the channel_id and feature_bit already exist.
    DO NOTHING
`

type InsertChannelFeatureParams struct {
	ChannelID  int64
	FeatureBit int32
}

func (q *Queries) InsertChannelFeature(ctx context.Context, arg InsertChannelFeatureParams) error {
	_, err := q.db.ExecContext(ctx, insertChannelFeature, arg.ChannelID, arg.FeatureBit)
	return err
}

const insertChannelMig = `-- name: InsertChannelMig :one
INSERT INTO graph_channels (
    version, scid, node_id_1, node_id_2,
    outpoint, capacity, bitcoin_key_1, bitcoin_key_2,
    node_1_signature, node_2_signature, bitcoin_1_signature,
    bitcoin_2_signature
) VALUES (
    $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
) ON CONFLICT (scid, version)
    -- If a conflict occurs, we have already migrated this channel. However, we
    -- still need to do an "UPDATE SET" here instead of "DO NOTHING" because
    -- otherwise, the "RETURNING id" part does not work.
    DO UPDATE SET
        node_id_1 = EXCLUDED.node_id_1,
        node_id_2 = EXCLUDED.node_id_2,
        outpoint = EXCLUDED.outpoint,
        capacity = EXCLUDED.capacity,
        bitcoin_key_1 = EXCLUDED.bitcoin_key_1,
        bitcoin_key_2 = EXCLUDED.bitcoin_key_2,
        node_1_signature = EXCLUDED.node_1_signature,
        node_2_signature = EXCLUDED.node_2_signature,
        bitcoin_1_signature = EXCLUDED.bitcoin_1_signature,
        bitcoin_2_signature = EXCLUDED.bitcoin_2_signature
RETURNING id
`

type InsertChannelMigParams struct {
	Version           int16
	Scid              []byte
	NodeID1           int64
	NodeID2           int64
	Outpoint          string
	Capacity          sql.NullInt64
	BitcoinKey1       []byte
	BitcoinKey2       []byte
	Node1Signature    []byte
	Node2Signature    []byte
	Bitcoin1Signature []byte
	Bitcoin2Signature []byte
}

// NOTE: This query is only meant to be used by the graph SQL migration since
// for that migration, in order to be retry-safe, we don't want to error out if
// we re-insert the same channel again (which would error if the normal
// CreateChannel query is used because of the uniqueness constraint on the scid
// and version columns).
func (q *Queries) InsertChannelMig(ctx context.Context, arg InsertChannelMigParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, insertChannelMig,
		arg.Version,
		arg.Scid,
		arg.NodeID1,
		arg.NodeID2,
		arg.Outpoint,
		arg.Capacity,
		arg.BitcoinKey1,
		arg.BitcoinKey2,
		arg.Node1Signature,
		arg.Node2Signature,
		arg.Bitcoin1Signature,
		arg.Bitcoin2Signature,
	)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const insertClosedChannel = `-- name: InsertClosedChannel :exec
/* ─────────────────────────────────────────────
   graph_closed_scid table queries
   ────────────────────────────────────────────-
*/

INSERT INTO graph_closed_scids (scid)
VALUES ($1)
ON CONFLICT (scid) DO NOTHING
`

func (q *Queries) InsertClosedChannel(ctx context.Context, scid []byte) error {
	_, err := q.db.ExecContext(ctx, insertClosedChannel, scid)
	return err
}

const insertEdgePolicyMig = `-- name: InsertEdgePolicyMig :one
INSERT INTO graph_channel_policies (
    version, channel_id, node_id, timelock, fee_ppm,
    base_fee_msat, min_htlc_msat, last_update, disabled,
    max_htlc_msat, inbound_base_fee_msat,
    inbound_fee_rate_milli_msat, message_flags, channel_flags,
    signature
) VALUES  (
    $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15
)
ON CONFLICT (channel_id, node_id, version)
    -- If a conflict occurs, we have already migrated this policy. However, we
    -- still need to do an "UPDATE SET" here instead of "DO NOTHING" because
    -- otherwise, the "RETURNING id" part does not work.
    DO UPDATE SET
        timelock = EXCLUDED.timelock,
        fee_ppm = EXCLUDED.fee_ppm,
        base_fee_msat = EXCLUDED.base_fee_msat,
        min_htlc_msat = EXCLUDED.min_htlc_msat,
        last_update = EXCLUDED.last_update,
        disabled = EXCLUDED.disabled,
        max_htlc_msat = EXCLUDED.max_htlc_msat,
        inbound_base_fee_msat = EXCLUDED.inbound_base_fee_msat,
        inbound_fee_rate_milli_msat = EXCLUDED.inbound_fee_rate_milli_msat,
        message_flags = EXCLUDED.message_flags,
        channel_flags = EXCLUDED.channel_flags,
        signature = EXCLUDED.signature
RETURNING id
`

type InsertEdgePolicyMigParams struct {
	Version                 int16
	ChannelID               int64
	NodeID                  int64
	Timelock                int32
	FeePpm                  int64
	BaseFeeMsat             int64
	MinHtlcMsat             int64
	LastUpdate              sql.NullInt64
	Disabled                sql.NullBool
	MaxHtlcMsat             sql.NullInt64
	InboundBaseFeeMsat      sql.NullInt64
	InboundFeeRateMilliMsat sql.NullInt64
	MessageFlags            sql.NullInt16
	ChannelFlags            sql.NullInt16
	Signature               []byte
}

// NOTE: This query is only meant to be used by the graph SQL migration since
// for that migration, in order to be retry-safe, we don't want to error out if
// we re-insert the same policy (which would error if the normal
// UpsertEdgePolicy query is used because of the constraint in that query that
// requires a policy update to have a newer last_update than the existing one).
func (q *Queries) InsertEdgePolicyMig(ctx context.Context, arg InsertEdgePolicyMigParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, insertEdgePolicyMig,
		arg.Version,
		arg.ChannelID,
		arg.NodeID,
		arg.Timelock,
		arg.FeePpm,
		arg.BaseFeeMsat,
		arg.MinHtlcMsat,
		arg.LastUpdate,
		arg.Disabled,
		arg.MaxHtlcMsat,
		arg.InboundBaseFeeMsat,
		arg.InboundFeeRateMilliMsat,
		arg.MessageFlags,
		arg.ChannelFlags,
		arg.Signature,
	)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const insertNodeFeature = `-- name: InsertNodeFeature :exec
/* ─────────────────────────────────────────────
   graph_node_features table queries
   ─────────────────────────────────────────────
*/

INSERT INTO graph_node_features (
    node_id, feature_bit
) VALUES (
    $1, $2
) ON CONFLICT (node_id, feature_bit)
    -- Do nothing if the feature already exists for the node.
    DO NOTHING
`

type InsertNodeFeatureParams struct {
	NodeID     int64
	FeatureBit int32
}

func (q *Queries) InsertNodeFeature(ctx context.Context, arg InsertNodeFeatureParams) error {
	_, err := q.db.ExecContext(ctx, insertNodeFeature, arg.NodeID, arg.FeatureBit)
	return err
}

const insertNodeMig = `-- name: InsertNodeMig :one
/* ─────────────────────────────────────────────
   Migration specific queries

   NOTE: once sqldbv2 is in place, these queries can be contained to a package
   dedicated to the migration that requires it, and so we can then remove
   it from the main set of "live" queries that the code-base has access to.
   ────────────────────────────────────────────-
*/

INSERT INTO graph_nodes (
    version, pub_key, alias, last_update, color, signature
) VALUES (
    $1, $2, $3, $4, $5, $6
)
ON CONFLICT (pub_key, version)
    -- If a conflict occurs, we have already migrated this node. However, we
    -- still need to do an "UPDATE SET" here instead of "DO NOTHING" because
    -- otherwise, the "RETURNING id" part does not work.
    DO UPDATE SET
        alias = EXCLUDED.alias,
        last_update = EXCLUDED.last_update,
        color = EXCLUDED.color,
        signature = EXCLUDED.signature
RETURNING id
`

type InsertNodeMigParams struct {
	Version    int16
	PubKey     []byte
	Alias      sql.NullString
	LastUpdate sql.NullInt64
	Color      sql.NullString
	Signature  []byte
}

// NOTE: This query is only meant to be used by the graph SQL migration since
// for that migration, in order to be retry-safe, we don't want to error out if
// we re-insert the same node (which would error if the normal UpsertNode query
// is used because of the constraint in that query that requires a node update
// to have a newer last_update than the existing node).
func (q *Queries) InsertNodeMig(ctx context.Context, arg InsertNodeMigParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, insertNodeMig,
		arg.Version,
		arg.PubKey,
		arg.Alias,
		arg.LastUpdate,
		arg.Color,
		arg.Signature,
	)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const isClosedChannel = `-- name: IsClosedChannel :one
SELECT EXISTS (
    SELECT 1
    FROM graph_closed_scids
    WHERE scid = $1
)
`

func (q *Queries) IsClosedChannel(ctx context.Context, scid []byte) (bool, error) {
	row := q.db.QueryRowContext(ctx, isClosedChannel, scid)
	var exists bool
	err := row.Scan(&exists)
	return exists, err
}

const isPublicV1Node = `-- name: IsPublicV1Node :one
SELECT EXISTS (
    SELECT 1
    FROM graph_channels c
    JOIN graph_nodes n ON n.id = c.node_id_1
    -- NOTE: we hard-code the version here since the clauses
    -- here that determine if a node is public is specific
    -- to the V1 gossip protocol. In V1, a node is public
    -- if it has a public channel and a public channel is one
    -- where we have the set of signatures of the channel
    -- announcement. It is enough to just check that we have
    -- one of the signatures since we only ever set them
    -- together.
    WHERE c.version = 1
      AND c.bitcoin_1_signature IS NOT NULL
      AND n.pub_key = $1
    UNION ALL
    SELECT 1
    FROM graph_channels c
    JOIN graph_nodes n ON n.id = c.node_id_2
    WHERE c.version = 1
      AND c.bitcoin_1_signature IS NOT NULL
      AND n.pub_key = $1
)
`

func (q *Queries) IsPublicV1Node(ctx context.Context, pubKey []byte) (bool, error) {
	row := q.db.QueryRowContext(ctx, isPublicV1Node, pubKey)
	var exists bool
	err := row.Scan(&exists)
	return exists, err
}

const isZombieChannel = `-- name: IsZombieChannel :one
SELECT EXISTS (
    SELECT 1
    FROM graph_zombie_channels
    WHERE scid = $1
    AND version = $2
) AS is_zombie
`

type IsZombieChannelParams struct {
	Scid    []byte
	Version int16
}

func (q *Queries) IsZombieChannel(ctx context.Context, arg IsZombieChannelParams) (bool, error) {
	row := q.db.QueryRowContext(ctx, isZombieChannel, arg.Scid, arg.Version)
	var is_zombie bool
	err := row.Scan(&is_zombie)
	return is_zombie, err
}

const listChannelsByNodeID = `-- name: ListChannelsByNodeID :many
SELECT c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity, c.bitcoin_key_1, c.bitcoin_key_2, c.node_1_signature, c.node_2_signature, c.bitcoin_1_signature, c.bitcoin_2_signature,
    n1.pub_key AS node1_pubkey,
    n2.pub_key AS node2_pubkey,

    -- Policy 1
    -- TODO(elle): use sqlc.embed to embed policy structs
    --  once this issue is resolved:
    --  https://github.com/sqlc-dev/sqlc/issues/2997
    cp1.id AS policy1_id,
    cp1.node_id AS policy1_node_id,
    cp1.version AS policy1_version,
    cp1.timelock AS policy1_timelock,
    cp1.fee_ppm AS policy1_fee_ppm,
    cp1.base_fee_msat AS policy1_base_fee_msat,
    cp1.min_htlc_msat AS policy1_min_htlc_msat,
    cp1.max_htlc_msat AS policy1_max_htlc_msat,
    cp1.last_update AS policy1_last_update,
    cp1.disabled AS policy1_disabled,
    cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat,
    cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat,
    cp1.message_flags AS policy1_message_flags,
    cp1.channel_flags AS policy1_channel_flags,
    cp1.signature AS policy1_signature,

       -- Policy 2
    cp2.id AS policy2_id,
    cp2.node_id AS policy2_node_id,
    cp2.version AS policy2_version,
    cp2.timelock AS policy2_timelock,
    cp2.fee_ppm AS policy2_fee_ppm,
    cp2.base_fee_msat AS policy2_base_fee_msat,
    cp2.min_htlc_msat AS policy2_min_htlc_msat,
    cp2.max_htlc_msat AS policy2_max_htlc_msat,
    cp2.last_update AS policy2_last_update,
    cp2.disabled AS policy2_disabled,
    cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat,
    cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
    cp2.message_flags AS policy2_message_flags,
    cp2.channel_flags AS policy2_channel_flags,
    cp2.signature AS policy2_signature

FROM graph_channels c
    JOIN graph_nodes n1 ON c.node_id_1 = n1.id
    JOIN graph_nodes n2 ON c.node_id_2 = n2.id
    LEFT JOIN graph_channel_policies cp1
    ON cp1.channel_id = c.id AND cp1.node_id = c.node_id_1 AND cp1.version = c.version
    LEFT JOIN graph_channel_policies cp2
    ON cp2.channel_id = c.id AND cp2.node_id = c.node_id_2 AND cp2.version = c.version
WHERE c.version = $1
  AND (c.node_id_1 = $2 OR c.node_id_2 = $2)
`

type ListChannelsByNodeIDParams struct {
	Version int16
	NodeID1 int64
}

type ListChannelsByNodeIDRow struct {
	GraphChannel                   GraphChannel
	Node1Pubkey                    []byte
	Node2Pubkey                    []byte
	Policy1ID                      sql.NullInt64
	Policy1NodeID                  sql.NullInt64
	Policy1Version                 sql.NullInt16
	Policy1Timelock                sql.NullInt32
	Policy1FeePpm                  sql.NullInt64
	Policy1BaseFeeMsat             sql.NullInt64
	Policy1MinHtlcMsat             sql.NullInt64
	Policy1MaxHtlcMsat             sql.NullInt64
	Policy1LastUpdate              sql.NullInt64
	Policy1Disabled                sql.NullBool
	Policy1InboundBaseFeeMsat      sql.NullInt64
	Policy1InboundFeeRateMilliMsat sql.NullInt64
	Policy1MessageFlags            sql.NullInt16
	Policy1ChannelFlags            sql.NullInt16
	Policy1Signature               []byte
	Policy2ID                      sql.NullInt64
	Policy2NodeID                  sql.NullInt64
	Policy2Version                 sql.NullInt16
	Policy2Timelock                sql.NullInt32
	Policy2FeePpm                  sql.NullInt64
	Policy2BaseFeeMsat             sql.NullInt64
	Policy2MinHtlcMsat             sql.NullInt64
	Policy2MaxHtlcMsat             sql.NullInt64
	Policy2LastUpdate              sql.NullInt64
	Policy2Disabled                sql.NullBool
	Policy2InboundBaseFeeMsat      sql.NullInt64
	Policy2InboundFeeRateMilliMsat sql.NullInt64
	Policy2MessageFlags            sql.NullInt16
	Policy2ChannelFlags            sql.NullInt16
	Policy2Signature               []byte
}

func (q *Queries) ListChannelsByNodeID(ctx context.Context, arg ListChannelsByNodeIDParams) ([]ListChannelsByNodeIDRow, error) {
	rows, err := q.db.QueryContext(ctx, listChannelsByNodeID, arg.Version, arg.NodeID1)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListChannelsByNodeIDRow
	for rows.Next() {
		var i ListChannelsByNodeIDRow
		if err := rows.Scan(
			&i.GraphChannel.ID,
			&i.GraphChannel.Version,
			&i.GraphChannel.Scid,
			&i.GraphChannel.NodeID1,
			&i.GraphChannel.NodeID2,
			&i.GraphChannel.Outpoint,
			&i.GraphChannel.Capacity,
			&i.GraphChannel.BitcoinKey1,
			&i.GraphChannel.BitcoinKey2,
			&i.GraphChannel.Node1Signature,
			&i.GraphChannel.Node2Signature,
			&i.GraphChannel.Bitcoin1Signature,
			&i.GraphChannel.Bitcoin2Signature,
			&i.Node1Pubkey,
			&i.Node2Pubkey,
			&i.Policy1ID,
			&i.Policy1NodeID,
			&i.Policy1Version,
			&i.Policy1Timelock,
			&i.Policy1FeePpm,
			&i.Policy1BaseFeeMsat,
			&i.Policy1MinHtlcMsat,
			&i.Policy1MaxHtlcMsat,
			&i.Policy1LastUpdate,
			&i.Policy1Disabled,
			&i.Policy1InboundBaseFeeMsat,
			&i.Policy1InboundFeeRateMilliMsat,
			&i.Policy1MessageFlags,
			&i.Policy1ChannelFlags,
			&i.Policy1Signature,
			&i.Policy2ID,
			&i.Policy2NodeID,
			&i.Policy2Version,
			&i.Policy2Timelock,
			&i.Policy2FeePpm,
			&i.Policy2BaseFeeMsat,
			&i.Policy2MinHtlcMsat,
			&i.Policy2MaxHtlcMsat,
			&i.Policy2LastUpdate,
			&i.Policy2Disabled,
			&i.Policy2InboundBaseFeeMsat,
			&i.Policy2InboundFeeRateMilliMsat,
			&i.Policy2MessageFlags,
			&i.Policy2ChannelFlags,
			&i.Policy2Signature,
		); 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 listChannelsForNodeIDs = `-- name: ListChannelsForNodeIDs :many
SELECT c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity, c.bitcoin_key_1, c.bitcoin_key_2, c.node_1_signature, c.node_2_signature, c.bitcoin_1_signature, c.bitcoin_2_signature,
       n1.pub_key AS node1_pubkey,
       n2.pub_key AS node2_pubkey,

       -- Policy 1
       -- TODO(elle): use sqlc.embed to embed policy structs
       --  once this issue is resolved:
       --  https://github.com/sqlc-dev/sqlc/issues/2997
       cp1.id AS policy1_id,
       cp1.node_id AS policy1_node_id,
       cp1.version AS policy1_version,
       cp1.timelock AS policy1_timelock,
       cp1.fee_ppm AS policy1_fee_ppm,
       cp1.base_fee_msat AS policy1_base_fee_msat,
       cp1.min_htlc_msat AS policy1_min_htlc_msat,
       cp1.max_htlc_msat AS policy1_max_htlc_msat,
       cp1.last_update AS policy1_last_update,
       cp1.disabled AS policy1_disabled,
       cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat,
       cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat,
       cp1.message_flags AS policy1_message_flags,
       cp1.channel_flags AS policy1_channel_flags,
       cp1.signature AS policy1_signature,

       -- Policy 2
       cp2.id AS policy2_id,
       cp2.node_id AS policy2_node_id,
       cp2.version AS policy2_version,
       cp2.timelock AS policy2_timelock,
       cp2.fee_ppm AS policy2_fee_ppm,
       cp2.base_fee_msat AS policy2_base_fee_msat,
       cp2.min_htlc_msat AS policy2_min_htlc_msat,
       cp2.max_htlc_msat AS policy2_max_htlc_msat,
       cp2.last_update AS policy2_last_update,
       cp2.disabled AS policy2_disabled,
       cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat,
       cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
       cp2.message_flags AS policy2_message_flags,
       cp2.channel_flags AS policy2_channel_flags,
       cp2.signature AS policy2_signature

FROM graph_channels c
         JOIN graph_nodes n1 ON c.node_id_1 = n1.id
         JOIN graph_nodes n2 ON c.node_id_2 = n2.id
         LEFT JOIN graph_channel_policies cp1
                   ON cp1.channel_id = c.id AND cp1.node_id = c.node_id_1 AND cp1.version = c.version
         LEFT JOIN graph_channel_policies cp2
                   ON cp2.channel_id = c.id AND cp2.node_id = c.node_id_2 AND cp2.version = c.version
WHERE c.version = $1
  AND (c.node_id_1 IN (/*SLICE:node1_ids*/?)
   OR c.node_id_2 IN (/*SLICE:node2_ids*/?))
`

type ListChannelsForNodeIDsParams struct {
	Version  int16
	Node1Ids []int64
	Node2Ids []int64
}

type ListChannelsForNodeIDsRow struct {
	GraphChannel                   GraphChannel
	Node1Pubkey                    []byte
	Node2Pubkey                    []byte
	Policy1ID                      sql.NullInt64
	Policy1NodeID                  sql.NullInt64
	Policy1Version                 sql.NullInt16
	Policy1Timelock                sql.NullInt32
	Policy1FeePpm                  sql.NullInt64
	Policy1BaseFeeMsat             sql.NullInt64
	Policy1MinHtlcMsat             sql.NullInt64
	Policy1MaxHtlcMsat             sql.NullInt64
	Policy1LastUpdate              sql.NullInt64
	Policy1Disabled                sql.NullBool
	Policy1InboundBaseFeeMsat      sql.NullInt64
	Policy1InboundFeeRateMilliMsat sql.NullInt64
	Policy1MessageFlags            sql.NullInt16
	Policy1ChannelFlags            sql.NullInt16
	Policy1Signature               []byte
	Policy2ID                      sql.NullInt64
	Policy2NodeID                  sql.NullInt64
	Policy2Version                 sql.NullInt16
	Policy2Timelock                sql.NullInt32
	Policy2FeePpm                  sql.NullInt64
	Policy2BaseFeeMsat             sql.NullInt64
	Policy2MinHtlcMsat             sql.NullInt64
	Policy2MaxHtlcMsat             sql.NullInt64
	Policy2LastUpdate              sql.NullInt64
	Policy2Disabled                sql.NullBool
	Policy2InboundBaseFeeMsat      sql.NullInt64
	Policy2InboundFeeRateMilliMsat sql.NullInt64
	Policy2MessageFlags            sql.NullInt16
	Policy2ChannelFlags            sql.NullInt16
	Policy2Signature               []byte
}

func (q *Queries) ListChannelsForNodeIDs(ctx context.Context, arg ListChannelsForNodeIDsParams) ([]ListChannelsForNodeIDsRow, error) {
	query := listChannelsForNodeIDs
	var queryParams []interface{}
	queryParams = append(queryParams, arg.Version)
	if len(arg.Node1Ids) > 0 {
		for _, v := range arg.Node1Ids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:node1_ids*/?", makeQueryParams(len(queryParams), len(arg.Node1Ids)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:node1_ids*/?", "NULL", 1)
	}
	if len(arg.Node2Ids) > 0 {
		for _, v := range arg.Node2Ids {
			queryParams = append(queryParams, v)
		}
		query = strings.Replace(query, "/*SLICE:node2_ids*/?", makeQueryParams(len(queryParams), len(arg.Node2Ids)), 1)
	} else {
		query = strings.Replace(query, "/*SLICE:node2_ids*/?", "NULL", 1)
	}
	rows, err := q.db.QueryContext(ctx, query, queryParams...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListChannelsForNodeIDsRow
	for rows.Next() {
		var i ListChannelsForNodeIDsRow
		if err := rows.Scan(
			&i.GraphChannel.ID,
			&i.GraphChannel.Version,
			&i.GraphChannel.Scid,
			&i.GraphChannel.NodeID1,
			&i.GraphChannel.NodeID2,
			&i.GraphChannel.Outpoint,
			&i.GraphChannel.Capacity,
			&i.GraphChannel.BitcoinKey1,
			&i.GraphChannel.BitcoinKey2,
			&i.GraphChannel.Node1Signature,
			&i.GraphChannel.Node2Signature,
			&i.GraphChannel.Bitcoin1Signature,
			&i.GraphChannel.Bitcoin2Signature,
			&i.Node1Pubkey,
			&i.Node2Pubkey,
			&i.Policy1ID,
			&i.Policy1NodeID,
			&i.Policy1Version,
			&i.Policy1Timelock,
			&i.Policy1FeePpm,
			&i.Policy1BaseFeeMsat,
			&i.Policy1MinHtlcMsat,
			&i.Policy1MaxHtlcMsat,
			&i.Policy1LastUpdate,
			&i.Policy1Disabled,
			&i.Policy1InboundBaseFeeMsat,
			&i.Policy1InboundFeeRateMilliMsat,
			&i.Policy1MessageFlags,
			&i.Policy1ChannelFlags,
			&i.Policy1Signature,
			&i.Policy2ID,
			&i.Policy2NodeID,
			&i.Policy2Version,
			&i.Policy2Timelock,
			&i.Policy2FeePpm,
			&i.Policy2BaseFeeMsat,
			&i.Policy2MinHtlcMsat,
			&i.Policy2MaxHtlcMsat,
			&i.Policy2LastUpdate,
			&i.Policy2Disabled,
			&i.Policy2InboundBaseFeeMsat,
			&i.Policy2InboundFeeRateMilliMsat,
			&i.Policy2MessageFlags,
			&i.Policy2ChannelFlags,
			&i.Policy2Signature,
		); 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 listChannelsPaginated = `-- name: ListChannelsPaginated :many
SELECT id, bitcoin_key_1, bitcoin_key_2, outpoint
FROM graph_channels c
WHERE c.version = $1 AND c.id > $2
ORDER BY c.id
LIMIT $3
`

type ListChannelsPaginatedParams struct {
	Version int16
	ID      int64
	Limit   int32
}

type ListChannelsPaginatedRow struct {
	ID          int64
	BitcoinKey1 []byte
	BitcoinKey2 []byte
	Outpoint    string
}

func (q *Queries) ListChannelsPaginated(ctx context.Context, arg ListChannelsPaginatedParams) ([]ListChannelsPaginatedRow, error) {
	rows, err := q.db.QueryContext(ctx, listChannelsPaginated, arg.Version, arg.ID, arg.Limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListChannelsPaginatedRow
	for rows.Next() {
		var i ListChannelsPaginatedRow
		if err := rows.Scan(
			&i.ID,
			&i.BitcoinKey1,
			&i.BitcoinKey2,
			&i.Outpoint,
		); 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 listChannelsWithPoliciesForCachePaginated = `-- name: ListChannelsWithPoliciesForCachePaginated :many
SELECT
    c.id as id,
    c.scid as scid,
    c.capacity AS capacity,

    -- Join node pubkeys
    n1.pub_key AS node1_pubkey,
    n2.pub_key AS node2_pubkey,

    -- Node 1 policy
    cp1.timelock AS policy_1_timelock,
    cp1.fee_ppm AS policy_1_fee_ppm,
    cp1.base_fee_msat AS policy_1_base_fee_msat,
    cp1.min_htlc_msat AS policy_1_min_htlc_msat,
    cp1.max_htlc_msat AS policy_1_max_htlc_msat,
    cp1.disabled AS policy_1_disabled,
    cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat,
    cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat,
    cp1.message_flags AS policy1_message_flags,
    cp1.channel_flags AS policy1_channel_flags,

    -- Node 2 policy
    cp2.timelock AS policy_2_timelock,
    cp2.fee_ppm AS policy_2_fee_ppm,
    cp2.base_fee_msat AS policy_2_base_fee_msat,
    cp2.min_htlc_msat AS policy_2_min_htlc_msat,
    cp2.max_htlc_msat AS policy_2_max_htlc_msat,
    cp2.disabled AS policy_2_disabled,
    cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat,
    cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
    cp2.message_flags AS policy2_message_flags,
    cp2.channel_flags AS policy2_channel_flags

FROM graph_channels c
JOIN graph_nodes n1 ON c.node_id_1 = n1.id
JOIN graph_nodes n2 ON c.node_id_2 = n2.id
LEFT JOIN graph_channel_policies cp1
    ON cp1.channel_id = c.id AND cp1.node_id = c.node_id_1 AND cp1.version = c.version
LEFT JOIN graph_channel_policies cp2
    ON cp2.channel_id = c.id AND cp2.node_id = c.node_id_2 AND cp2.version = c.version
WHERE c.version = $1 AND c.id > $2
ORDER BY c.id
LIMIT $3
`

type ListChannelsWithPoliciesForCachePaginatedParams struct {
	Version int16
	ID      int64
	Limit   int32
}

type ListChannelsWithPoliciesForCachePaginatedRow struct {
	ID                             int64
	Scid                           []byte
	Capacity                       sql.NullInt64
	Node1Pubkey                    []byte
	Node2Pubkey                    []byte
	Policy1Timelock                sql.NullInt32
	Policy1FeePpm                  sql.NullInt64
	Policy1BaseFeeMsat             sql.NullInt64
	Policy1MinHtlcMsat             sql.NullInt64
	Policy1MaxHtlcMsat             sql.NullInt64
	Policy1Disabled                sql.NullBool
	Policy1InboundBaseFeeMsat      sql.NullInt64
	Policy1InboundFeeRateMilliMsat sql.NullInt64
	Policy1MessageFlags            sql.NullInt16
	Policy1ChannelFlags            sql.NullInt16
	Policy2Timelock                sql.NullInt32
	Policy2FeePpm                  sql.NullInt64
	Policy2BaseFeeMsat             sql.NullInt64
	Policy2MinHtlcMsat             sql.NullInt64
	Policy2MaxHtlcMsat             sql.NullInt64
	Policy2Disabled                sql.NullBool
	Policy2InboundBaseFeeMsat      sql.NullInt64
	Policy2InboundFeeRateMilliMsat sql.NullInt64
	Policy2MessageFlags            sql.NullInt16
	Policy2ChannelFlags            sql.NullInt16
}

func (q *Queries) ListChannelsWithPoliciesForCachePaginated(ctx context.Context, arg ListChannelsWithPoliciesForCachePaginatedParams) ([]ListChannelsWithPoliciesForCachePaginatedRow, error) {
	rows, err := q.db.QueryContext(ctx, listChannelsWithPoliciesForCachePaginated, arg.Version, arg.ID, arg.Limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListChannelsWithPoliciesForCachePaginatedRow
	for rows.Next() {
		var i ListChannelsWithPoliciesForCachePaginatedRow
		if err := rows.Scan(
			&i.ID,
			&i.Scid,
			&i.Capacity,
			&i.Node1Pubkey,
			&i.Node2Pubkey,
			&i.Policy1Timelock,
			&i.Policy1FeePpm,
			&i.Policy1BaseFeeMsat,
			&i.Policy1MinHtlcMsat,
			&i.Policy1MaxHtlcMsat,
			&i.Policy1Disabled,
			&i.Policy1InboundBaseFeeMsat,
			&i.Policy1InboundFeeRateMilliMsat,
			&i.Policy1MessageFlags,
			&i.Policy1ChannelFlags,
			&i.Policy2Timelock,
			&i.Policy2FeePpm,
			&i.Policy2BaseFeeMsat,
			&i.Policy2MinHtlcMsat,
			&i.Policy2MaxHtlcMsat,
			&i.Policy2Disabled,
			&i.Policy2InboundBaseFeeMsat,
			&i.Policy2InboundFeeRateMilliMsat,
			&i.Policy2MessageFlags,
			&i.Policy2ChannelFlags,
		); 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 listChannelsWithPoliciesPaginated = `-- name: ListChannelsWithPoliciesPaginated :many
SELECT
    c.id, c.version, c.scid, c.node_id_1, c.node_id_2, c.outpoint, c.capacity, c.bitcoin_key_1, c.bitcoin_key_2, c.node_1_signature, c.node_2_signature, c.bitcoin_1_signature, c.bitcoin_2_signature,

    -- Join node pubkeys
    n1.pub_key AS node1_pubkey,
    n2.pub_key AS node2_pubkey,

    -- Node 1 policy
    cp1.id AS policy_1_id,
    cp1.node_id AS policy_1_node_id,
    cp1.version AS policy_1_version,
    cp1.timelock AS policy_1_timelock,
    cp1.fee_ppm AS policy_1_fee_ppm,
    cp1.base_fee_msat AS policy_1_base_fee_msat,
    cp1.min_htlc_msat AS policy_1_min_htlc_msat,
    cp1.max_htlc_msat AS policy_1_max_htlc_msat,
    cp1.last_update AS policy_1_last_update,
    cp1.disabled AS policy_1_disabled,
    cp1.inbound_base_fee_msat AS policy1_inbound_base_fee_msat,
    cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat,
    cp1.message_flags AS policy1_message_flags,
    cp1.channel_flags AS policy1_channel_flags,
    cp1.signature AS policy_1_signature,

    -- Node 2 policy
    cp2.id AS policy_2_id,
    cp2.node_id AS policy_2_node_id,
    cp2.version AS policy_2_version,
    cp2.timelock AS policy_2_timelock,
    cp2.fee_ppm AS policy_2_fee_ppm,
    cp2.base_fee_msat AS policy_2_base_fee_msat,
    cp2.min_htlc_msat AS policy_2_min_htlc_msat,
    cp2.max_htlc_msat AS policy_2_max_htlc_msat,
    cp2.last_update AS policy_2_last_update,
    cp2.disabled AS policy_2_disabled,
    cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat,
    cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
    cp2.message_flags AS policy2_message_flags,
    cp2.channel_flags AS policy2_channel_flags,
    cp2.signature AS policy_2_signature

FROM graph_channels c
JOIN graph_nodes n1 ON c.node_id_1 = n1.id
JOIN graph_nodes n2 ON c.node_id_2 = n2.id
LEFT JOIN graph_channel_policies cp1
    ON cp1.channel_id = c.id AND cp1.node_id = c.node_id_1 AND cp1.version = c.version
LEFT JOIN graph_channel_policies cp2
    ON cp2.channel_id = c.id AND cp2.node_id = c.node_id_2 AND cp2.version = c.version
WHERE c.version = $1 AND c.id > $2
ORDER BY c.id
LIMIT $3
`

type ListChannelsWithPoliciesPaginatedParams struct {
	Version int16
	ID      int64
	Limit   int32
}

type ListChannelsWithPoliciesPaginatedRow struct {
	GraphChannel                   GraphChannel
	Node1Pubkey                    []byte
	Node2Pubkey                    []byte
	Policy1ID                      sql.NullInt64
	Policy1NodeID                  sql.NullInt64
	Policy1Version                 sql.NullInt16
	Policy1Timelock                sql.NullInt32
	Policy1FeePpm                  sql.NullInt64
	Policy1BaseFeeMsat             sql.NullInt64
	Policy1MinHtlcMsat             sql.NullInt64
	Policy1MaxHtlcMsat             sql.NullInt64
	Policy1LastUpdate              sql.NullInt64
	Policy1Disabled                sql.NullBool
	Policy1InboundBaseFeeMsat      sql.NullInt64
	Policy1InboundFeeRateMilliMsat sql.NullInt64
	Policy1MessageFlags            sql.NullInt16
	Policy1ChannelFlags            sql.NullInt16
	Policy1Signature               []byte
	Policy2ID                      sql.NullInt64
	Policy2NodeID                  sql.NullInt64
	Policy2Version                 sql.NullInt16
	Policy2Timelock                sql.NullInt32
	Policy2FeePpm                  sql.NullInt64
	Policy2BaseFeeMsat             sql.NullInt64
	Policy2MinHtlcMsat             sql.NullInt64
	Policy2MaxHtlcMsat             sql.NullInt64
	Policy2LastUpdate              sql.NullInt64
	Policy2Disabled                sql.NullBool
	Policy2InboundBaseFeeMsat      sql.NullInt64
	Policy2InboundFeeRateMilliMsat sql.NullInt64
	Policy2MessageFlags            sql.NullInt16
	Policy2ChannelFlags            sql.NullInt16
	Policy2Signature               []byte
}

func (q *Queries) ListChannelsWithPoliciesPaginated(ctx context.Context, arg ListChannelsWithPoliciesPaginatedParams) ([]ListChannelsWithPoliciesPaginatedRow, error) {
	rows, err := q.db.QueryContext(ctx, listChannelsWithPoliciesPaginated, arg.Version, arg.ID, arg.Limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListChannelsWithPoliciesPaginatedRow
	for rows.Next() {
		var i ListChannelsWithPoliciesPaginatedRow
		if err := rows.Scan(
			&i.GraphChannel.ID,
			&i.GraphChannel.Version,
			&i.GraphChannel.Scid,
			&i.GraphChannel.NodeID1,
			&i.GraphChannel.NodeID2,
			&i.GraphChannel.Outpoint,
			&i.GraphChannel.Capacity,
			&i.GraphChannel.BitcoinKey1,
			&i.GraphChannel.BitcoinKey2,
			&i.GraphChannel.Node1Signature,
			&i.GraphChannel.Node2Signature,
			&i.GraphChannel.Bitcoin1Signature,
			&i.GraphChannel.Bitcoin2Signature,
			&i.Node1Pubkey,
			&i.Node2Pubkey,
			&i.Policy1ID,
			&i.Policy1NodeID,
			&i.Policy1Version,
			&i.Policy1Timelock,
			&i.Policy1FeePpm,
			&i.Policy1BaseFeeMsat,
			&i.Policy1MinHtlcMsat,
			&i.Policy1MaxHtlcMsat,
			&i.Policy1LastUpdate,
			&i.Policy1Disabled,
			&i.Policy1InboundBaseFeeMsat,
			&i.Policy1InboundFeeRateMilliMsat,
			&i.Policy1MessageFlags,
			&i.Policy1ChannelFlags,
			&i.Policy1Signature,
			&i.Policy2ID,
			&i.Policy2NodeID,
			&i.Policy2Version,
			&i.Policy2Timelock,
			&i.Policy2FeePpm,
			&i.Policy2BaseFeeMsat,
			&i.Policy2MinHtlcMsat,
			&i.Policy2MaxHtlcMsat,
			&i.Policy2LastUpdate,
			&i.Policy2Disabled,
			&i.Policy2InboundBaseFeeMsat,
			&i.Policy2InboundFeeRateMilliMsat,
			&i.Policy2MessageFlags,
			&i.Policy2ChannelFlags,
			&i.Policy2Signature,
		); 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 listNodeIDsAndPubKeys = `-- name: ListNodeIDsAndPubKeys :many
SELECT id, pub_key
FROM graph_nodes
WHERE version = $1  AND id > $2
ORDER BY id
LIMIT $3
`

type ListNodeIDsAndPubKeysParams struct {
	Version int16
	ID      int64
	Limit   int32
}

type ListNodeIDsAndPubKeysRow struct {
	ID     int64
	PubKey []byte
}

func (q *Queries) ListNodeIDsAndPubKeys(ctx context.Context, arg ListNodeIDsAndPubKeysParams) ([]ListNodeIDsAndPubKeysRow, error) {
	rows, err := q.db.QueryContext(ctx, listNodeIDsAndPubKeys, arg.Version, arg.ID, arg.Limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []ListNodeIDsAndPubKeysRow
	for rows.Next() {
		var i ListNodeIDsAndPubKeysRow
		if err := rows.Scan(&i.ID, &i.PubKey); 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 listNodesPaginated = `-- name: ListNodesPaginated :many
SELECT id, version, pub_key, alias, last_update, color, signature
FROM graph_nodes
WHERE version = $1 AND id > $2
ORDER BY id
LIMIT $3
`

type ListNodesPaginatedParams struct {
	Version int16
	ID      int64
	Limit   int32
}

func (q *Queries) ListNodesPaginated(ctx context.Context, arg ListNodesPaginatedParams) ([]GraphNode, error) {
	rows, err := q.db.QueryContext(ctx, listNodesPaginated, arg.Version, arg.ID, arg.Limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []GraphNode
	for rows.Next() {
		var i GraphNode
		if err := rows.Scan(
			&i.ID,
			&i.Version,
			&i.PubKey,
			&i.Alias,
			&i.LastUpdate,
			&i.Color,
			&i.Signature,
		); 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 upsertChanPolicyExtraType = `-- name: UpsertChanPolicyExtraType :exec
/* ─────────────────────────────────────────────
   graph_channel_policy_extra_types table queries
   ─────────────────────────────────────────────
*/

INSERT INTO graph_channel_policy_extra_types (
    channel_policy_id, type, value
)
VALUES ($1, $2, $3)
ON CONFLICT (channel_policy_id, type)
    -- If a conflict occurs on channel_policy_id and type, then we update the
    -- value.
    DO UPDATE SET value = EXCLUDED.value
`

type UpsertChanPolicyExtraTypeParams struct {
	ChannelPolicyID int64
	Type            int64
	Value           []byte
}

func (q *Queries) UpsertChanPolicyExtraType(ctx context.Context, arg UpsertChanPolicyExtraTypeParams) error {
	_, err := q.db.ExecContext(ctx, upsertChanPolicyExtraType, arg.ChannelPolicyID, arg.Type, arg.Value)
	return err
}

const upsertChannelExtraType = `-- name: UpsertChannelExtraType :exec
/* ─────────────────────────────────────────────
   graph_channel_extra_types table queries
   ─────────────────────────────────────────────
*/

INSERT INTO graph_channel_extra_types (
    channel_id, type, value
)
VALUES ($1, $2, $3)
    ON CONFLICT (channel_id, type)
    -- Update the value if a conflict occurs on channel_id and type.
    DO UPDATE SET value = EXCLUDED.value
`

type UpsertChannelExtraTypeParams struct {
	ChannelID int64
	Type      int64
	Value     []byte
}

func (q *Queries) UpsertChannelExtraType(ctx context.Context, arg UpsertChannelExtraTypeParams) error {
	_, err := q.db.ExecContext(ctx, upsertChannelExtraType, arg.ChannelID, arg.Type, arg.Value)
	return err
}

const upsertEdgePolicy = `-- name: UpsertEdgePolicy :one
/* ─────────────────────────────────────────────
   graph_channel_policies table queries
   ─────────────────────────────────────────────
*/

INSERT INTO graph_channel_policies (
    version, channel_id, node_id, timelock, fee_ppm,
    base_fee_msat, min_htlc_msat, last_update, disabled,
    max_htlc_msat, inbound_base_fee_msat,
    inbound_fee_rate_milli_msat, message_flags, channel_flags,
    signature
) VALUES  (
    $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15
)
ON CONFLICT (channel_id, node_id, version)
    -- Update the following fields if a conflict occurs on channel_id,
    -- node_id, and version.
    DO UPDATE SET
        timelock = EXCLUDED.timelock,
        fee_ppm = EXCLUDED.fee_ppm,
        base_fee_msat = EXCLUDED.base_fee_msat,
        min_htlc_msat = EXCLUDED.min_htlc_msat,
        last_update = EXCLUDED.last_update,
        disabled = EXCLUDED.disabled,
        max_htlc_msat = EXCLUDED.max_htlc_msat,
        inbound_base_fee_msat = EXCLUDED.inbound_base_fee_msat,
        inbound_fee_rate_milli_msat = EXCLUDED.inbound_fee_rate_milli_msat,
        message_flags = EXCLUDED.message_flags,
        channel_flags = EXCLUDED.channel_flags,
        signature = EXCLUDED.signature
WHERE EXCLUDED.last_update > graph_channel_policies.last_update
RETURNING id
`

type UpsertEdgePolicyParams struct {
	Version                 int16
	ChannelID               int64
	NodeID                  int64
	Timelock                int32
	FeePpm                  int64
	BaseFeeMsat             int64
	MinHtlcMsat             int64
	LastUpdate              sql.NullInt64
	Disabled                sql.NullBool
	MaxHtlcMsat             sql.NullInt64
	InboundBaseFeeMsat      sql.NullInt64
	InboundFeeRateMilliMsat sql.NullInt64
	MessageFlags            sql.NullInt16
	ChannelFlags            sql.NullInt16
	Signature               []byte
}

func (q *Queries) UpsertEdgePolicy(ctx context.Context, arg UpsertEdgePolicyParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, upsertEdgePolicy,
		arg.Version,
		arg.ChannelID,
		arg.NodeID,
		arg.Timelock,
		arg.FeePpm,
		arg.BaseFeeMsat,
		arg.MinHtlcMsat,
		arg.LastUpdate,
		arg.Disabled,
		arg.MaxHtlcMsat,
		arg.InboundBaseFeeMsat,
		arg.InboundFeeRateMilliMsat,
		arg.MessageFlags,
		arg.ChannelFlags,
		arg.Signature,
	)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const upsertNode = `-- name: UpsertNode :one
/* ─────────────────────────────────────────────
   graph_nodes table queries
   ───────────────────────────��─────────────────
*/

INSERT INTO graph_nodes (
    version, pub_key, alias, last_update, color, signature
) VALUES (
    $1, $2, $3, $4, $5, $6
)
ON CONFLICT (pub_key, version)
    -- Update the following fields if a conflict occurs on pub_key
    -- and version.
    DO UPDATE SET
        alias = EXCLUDED.alias,
        last_update = EXCLUDED.last_update,
        color = EXCLUDED.color,
        signature = EXCLUDED.signature
WHERE graph_nodes.last_update IS NULL
    OR EXCLUDED.last_update > graph_nodes.last_update
RETURNING id
`

type UpsertNodeParams struct {
	Version    int16
	PubKey     []byte
	Alias      sql.NullString
	LastUpdate sql.NullInt64
	Color      sql.NullString
	Signature  []byte
}

func (q *Queries) UpsertNode(ctx context.Context, arg UpsertNodeParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, upsertNode,
		arg.Version,
		arg.PubKey,
		arg.Alias,
		arg.LastUpdate,
		arg.Color,
		arg.Signature,
	)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const upsertNodeAddress = `-- name: UpsertNodeAddress :exec
/* ─────────────────────────────────────────────
   graph_node_addresses table queries
   ───────────────────────────────────��─────────
*/

INSERT INTO graph_node_addresses (
    node_id,
    type,
    address,
    position
) VALUES (
    $1, $2, $3, $4
) ON CONFLICT (node_id, type, position)
    DO UPDATE SET address = EXCLUDED.address
`

type UpsertNodeAddressParams struct {
	NodeID   int64
	Type     int16
	Address  string
	Position int32
}

func (q *Queries) UpsertNodeAddress(ctx context.Context, arg UpsertNodeAddressParams) error {
	_, err := q.db.ExecContext(ctx, upsertNodeAddress,
		arg.NodeID,
		arg.Type,
		arg.Address,
		arg.Position,
	)
	return err
}

const upsertNodeExtraType = `-- name: UpsertNodeExtraType :exec
/* ─────────────────────────────────────────────
   graph_node_extra_types table queries
   ─────────────────────────────────────────────
*/

INSERT INTO graph_node_extra_types (
    node_id, type, value
)
VALUES ($1, $2, $3)
ON CONFLICT (type, node_id)
    -- Update the value if a conflict occurs on type
    -- and node_id.
    DO UPDATE SET value = EXCLUDED.value
`

type UpsertNodeExtraTypeParams struct {
	NodeID int64
	Type   int64
	Value  []byte
}

func (q *Queries) UpsertNodeExtraType(ctx context.Context, arg UpsertNodeExtraTypeParams) error {
	_, err := q.db.ExecContext(ctx, upsertNodeExtraType, arg.NodeID, arg.Type, arg.Value)
	return err
}

const upsertPruneLogEntry = `-- name: UpsertPruneLogEntry :exec
/* ───────────────────────────���─────────────────
    graph_prune_log table queries
    ─────────────────────────────────────────────
*/

INSERT INTO graph_prune_log (
    block_height, block_hash
) VALUES (
    $1, $2
)
ON CONFLICT(block_height) DO UPDATE SET
    block_hash = EXCLUDED.block_hash
`

type UpsertPruneLogEntryParams struct {
	BlockHeight int64
	BlockHash   []byte
}

func (q *Queries) UpsertPruneLogEntry(ctx context.Context, arg UpsertPruneLogEntryParams) error {
	_, err := q.db.ExecContext(ctx, upsertPruneLogEntry, arg.BlockHeight, arg.BlockHash)
	return err
}

const upsertSourceNode = `-- name: UpsertSourceNode :one
INSERT INTO graph_nodes (
    version, pub_key, alias, last_update, color, signature
) VALUES (
    $1, $2, $3, $4, $5, $6
)
ON CONFLICT (pub_key, version)
    -- Update the following fields if a conflict occurs on pub_key
    -- and version.
    DO UPDATE SET
        alias = EXCLUDED.alias,
        last_update = EXCLUDED.last_update,
        color = EXCLUDED.color,
        signature = EXCLUDED.signature
WHERE graph_nodes.last_update IS NULL
    OR EXCLUDED.last_update >= graph_nodes.last_update
RETURNING id
`

type UpsertSourceNodeParams struct {
	Version    int16
	PubKey     []byte
	Alias      sql.NullString
	LastUpdate sql.NullInt64
	Color      sql.NullString
	Signature  []byte
}

// We use a separate upsert for our own node since we want to be less strict
// about the last_update field. For our own node, we always want to
// update the record even if the last_update is the same as what we have.
func (q *Queries) UpsertSourceNode(ctx context.Context, arg UpsertSourceNodeParams) (int64, error) {
	row := q.db.QueryRowContext(ctx, upsertSourceNode,
		arg.Version,
		arg.PubKey,
		arg.Alias,
		arg.LastUpdate,
		arg.Color,
		arg.Signature,
	)
	var id int64
	err := row.Scan(&id)
	return id, err
}

const upsertZombieChannel = `-- name: UpsertZombieChannel :exec
/* ─────────────────────────────────────────────
   graph_zombie_channels table queries
   ─────────────────────────────────────────────
*/

INSERT INTO graph_zombie_channels (scid, version, node_key_1, node_key_2)
VALUES ($1, $2, $3, $4)
ON CONFLICT (scid, version)
DO UPDATE SET
    -- If a conflict exists for the SCID and version pair, then we
    -- update the node keys.
    node_key_1 = COALESCE(EXCLUDED.node_key_1, graph_zombie_channels.node_key_1),
    node_key_2 = COALESCE(EXCLUDED.node_key_2, graph_zombie_channels.node_key_2)
`

type UpsertZombieChannelParams struct {
	Scid     []byte
	Version  int16
	NodeKey1 []byte
	NodeKey2 []byte
}

func (q *Queries) UpsertZombieChannel(ctx context.Context, arg UpsertZombieChannelParams) error {
	_, err := q.db.ExecContext(ctx, upsertZombieChannel,
		arg.Scid,
		arg.Version,
		arg.NodeKey1,
		arg.NodeKey2,
	)
	return err
}
