|
| 1 | +//! SQL queries to load dynamic data sources |
| 2 | +
|
| 3 | +use diesel::pg::PgConnection; |
| 4 | +use diesel::prelude::{ExpressionMethods, JoinOnDsl, QueryDsl, RunQueryDsl}; |
| 5 | + |
| 6 | +use graph::{ |
| 7 | + components::store::StoredDynamicDataSource, |
| 8 | + data::subgraph::Source, |
| 9 | + prelude::{bigdecimal::ToPrimitive, web3::types::H160, BigDecimal, StoreError}, |
| 10 | +}; |
| 11 | + |
| 12 | +// Diesel tables for some of the metadata |
| 13 | +// See also: ed42d219c6704a4aab57ce1ea66698e7 |
| 14 | +// Changes to the GraphQL schema might require changes to these tables. |
| 15 | +// The definitions of the tables can be generated with |
| 16 | +// cargo run -p graph-store-postgres --example layout -- \ |
| 17 | +// -g diesel store/postgres/src/subgraphs.graphql subgraphs |
| 18 | +// BEGIN GENERATED CODE |
| 19 | +table! { |
| 20 | + subgraphs.dynamic_ethereum_contract_data_source (vid) { |
| 21 | + vid -> BigInt, |
| 22 | + id -> Text, |
| 23 | + kind -> Text, |
| 24 | + name -> Text, |
| 25 | + network -> Nullable<Text>, |
| 26 | + source -> Text, |
| 27 | + mapping -> Text, |
| 28 | + templates -> Nullable<Array<Text>>, |
| 29 | + ethereum_block_hash -> Binary, |
| 30 | + ethereum_block_number -> Numeric, |
| 31 | + deployment -> Text, |
| 32 | + context -> Nullable<Text>, |
| 33 | + block_range -> Range<Integer>, |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +table! { |
| 38 | + subgraphs.ethereum_contract_source (vid) { |
| 39 | + vid -> BigInt, |
| 40 | + id -> Text, |
| 41 | + address -> Nullable<Binary>, |
| 42 | + abi -> Text, |
| 43 | + start_block -> Nullable<Numeric>, |
| 44 | + block_range -> Range<Integer>, |
| 45 | + } |
| 46 | +} |
| 47 | + |
| 48 | +// END GENERATED CODE |
| 49 | + |
| 50 | +allow_tables_to_appear_in_same_query!( |
| 51 | + dynamic_ethereum_contract_data_source, |
| 52 | + ethereum_contract_source |
| 53 | +); |
| 54 | + |
| 55 | +fn to_source( |
| 56 | + deployment: &str, |
| 57 | + ds_id: &str, |
| 58 | + (address, abi, start_block): (Option<Vec<u8>>, String, Option<BigDecimal>), |
| 59 | +) -> Result<Source, StoreError> { |
| 60 | + // Treat a missing address as an error. TODO: Is that correct? |
| 61 | + let address = match address { |
| 62 | + Some(address) => address, |
| 63 | + None => { |
| 64 | + return Err(StoreError::ConstraintViolation(format!( |
| 65 | + "Dynamic data source {} for deployment {} is missing an address", |
| 66 | + ds_id, deployment |
| 67 | + ))); |
| 68 | + } |
| 69 | + }; |
| 70 | + if address.len() != 20 { |
| 71 | + return Err(StoreError::ConstraintViolation(format!( |
| 72 | + "Data source address 0x`{:?}` for dynamic data source {} in deployment {} should have be 20 bytes long but is {} bytes long", |
| 73 | + address, ds_id, deployment, |
| 74 | + address.len() |
| 75 | + ))); |
| 76 | + } |
| 77 | + let address = Some(H160::from_slice(address.as_slice())); |
| 78 | + |
| 79 | + // Assume a missing start block is the same as 0 |
| 80 | + let start_block = start_block |
| 81 | + .map(|s| { |
| 82 | + s.to_u64().ok_or_else(|| { |
| 83 | + StoreError::ConstraintViolation(format!( |
| 84 | + "Start block {:?} for dynamic data source {} in deployment {} is not a u64", |
| 85 | + s, ds_id, deployment |
| 86 | + )) |
| 87 | + }) |
| 88 | + }) |
| 89 | + .transpose()? |
| 90 | + .unwrap_or(0); |
| 91 | + |
| 92 | + Ok(Source { |
| 93 | + address, |
| 94 | + abi, |
| 95 | + start_block, |
| 96 | + }) |
| 97 | +} |
| 98 | + |
| 99 | +#[allow(dead_code)] |
| 100 | +pub fn load(conn: &PgConnection, id: &str) -> Result<Vec<StoredDynamicDataSource>, StoreError> { |
| 101 | + use dynamic_ethereum_contract_data_source as decds; |
| 102 | + use ethereum_contract_source as ecs; |
| 103 | + |
| 104 | + let dds: Vec<_> = decds::table |
| 105 | + .inner_join(ecs::table.on(decds::source.eq(ecs::id))) |
| 106 | + .filter(decds::deployment.eq(id)) |
| 107 | + .select(( |
| 108 | + decds::id, |
| 109 | + decds::name, |
| 110 | + decds::context, |
| 111 | + (ecs::address, ecs::abi, ecs::start_block), |
| 112 | + )) |
| 113 | + .load::<( |
| 114 | + String, |
| 115 | + String, |
| 116 | + Option<String>, |
| 117 | + (Option<Vec<u8>>, String, Option<BigDecimal>), |
| 118 | + )>(conn)?; |
| 119 | + |
| 120 | + let mut data_sources = Vec::new(); |
| 121 | + for (ds_id, name, context, source) in dds.into_iter() { |
| 122 | + let source = to_source(id, &ds_id, source)?; |
| 123 | + let data_source = StoredDynamicDataSource { |
| 124 | + name, |
| 125 | + source, |
| 126 | + context, |
| 127 | + }; |
| 128 | + data_sources.push(data_source); |
| 129 | + } |
| 130 | + Ok(data_sources) |
| 131 | +} |
0 commit comments