iota_graphql_rpc/types/event/
lookups.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::fmt::Write;
6
7use crate::{
8    data::pg::bytea_literal,
9    filter, query,
10    raw_query::RawQuery,
11    types::{
12        cursor::Page,
13        digest::Digest,
14        event::Cursor,
15        iota_address::IotaAddress,
16        type_filter::{ModuleFilter, TypeFilter},
17    },
18};
19
20fn select_ev(sender: Option<IotaAddress>, from: &str) -> RawQuery {
21    let query = query!(format!(
22        "SELECT tx_sequence_number, event_sequence_number FROM {}",
23        from
24    ));
25
26    if let Some(sender) = sender {
27        return query.filter(format!("sender = {}", bytea_literal(sender.as_slice())));
28    }
29
30    query
31}
32
33pub(crate) fn select_sender(sender: IotaAddress) -> RawQuery {
34    select_ev(Some(sender), "event_senders")
35}
36
37pub(crate) fn select_event_type(event_type: &TypeFilter, sender: Option<IotaAddress>) -> RawQuery {
38    match event_type {
39        TypeFilter::ByModule(ModuleFilter::ByPackage(p)) => {
40            filter!(
41                select_ev(sender, "event_struct_package"),
42                format!("package = {}", bytea_literal(p.as_slice()))
43            )
44        }
45        TypeFilter::ByModule(ModuleFilter::ByModule(p, m)) => {
46            filter!(
47                select_ev(sender, "event_struct_module"),
48                format!(
49                    "package = {} and module = {{}}",
50                    bytea_literal(p.as_slice())
51                ),
52                m
53            )
54        }
55        TypeFilter::ByType(tag) => {
56            let package = tag.address;
57            let module = tag.module.to_string();
58            let mut name = tag.name.as_str().to_owned();
59            let (table, col_name) = if tag.type_params.is_empty() {
60                ("event_struct_name", "type_name")
61            } else {
62                let mut prefix = "<";
63                for param in &tag.type_params {
64                    name += prefix;
65                    // SAFETY: write! to String always succeeds.
66                    write!(
67                        name,
68                        "{}",
69                        param.to_canonical_display(/* with_prefix */ true)
70                    )
71                    .unwrap();
72                    prefix = ", ";
73                }
74                name += ">";
75                ("event_struct_instantiation", "type_instantiation")
76            };
77
78            filter!(
79                select_ev(sender, table),
80                format!(
81                    "package = {} and module = {{}} and {} = {{}}",
82                    bytea_literal(package.as_slice()),
83                    col_name
84                ),
85                module,
86                name
87            )
88        }
89    }
90}
91
92pub(crate) fn select_emit_module(
93    emit_module: &ModuleFilter,
94    sender: Option<IotaAddress>,
95) -> RawQuery {
96    match emit_module {
97        ModuleFilter::ByPackage(p) => {
98            filter!(
99                select_ev(sender, "event_emit_package"),
100                format!("package = {}", bytea_literal(p.as_slice()))
101            )
102        }
103        ModuleFilter::ByModule(p, m) => {
104            filter!(
105                select_ev(sender, "event_emit_module"),
106                format!(
107                    "package = {} and module = {{}}",
108                    bytea_literal(p.as_slice())
109                ),
110                m
111            )
112        }
113    }
114}
115
116/// Adds filters to bound an events query from above and below based on cursors
117/// and filters. The query will always at least be bounded by `tx_hi`, the
118/// current exclusive upperbound on transaction sequence numbers, based on the
119/// consistency cursor.
120pub(crate) fn add_bounds(
121    mut query: RawQuery,
122    tx_digest_filter: &Option<Digest>,
123    page: &Page<Cursor>,
124    tx_hi: i64,
125) -> RawQuery {
126    query = filter!(query, format!("tx_sequence_number < {}", tx_hi));
127
128    if let Some(after) = page.after() {
129        query = filter!(
130            query,
131            format!(
132                "ROW(tx_sequence_number, event_sequence_number) >= ({}, {})",
133                after.tx, after.e
134            )
135        );
136    }
137
138    if let Some(before) = page.before() {
139        query = filter!(
140            query,
141            format!(
142                "ROW(tx_sequence_number, event_sequence_number) <= ({}, {})",
143                before.tx, before.e
144            )
145        );
146    }
147
148    if let Some(digest) = tx_digest_filter {
149        query = filter!(
150            query,
151            format!(
152                "tx_sequence_number = (SELECT tx_sequence_number FROM tx_digests WHERE tx_digest = {})",
153                bytea_literal(digest.as_slice()),
154            )
155        );
156    }
157
158    query
159}