-
Notifications
You must be signed in to change notification settings - Fork 1.1k
graph,graphql,server,store: Subgraph Sql Service #5382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
b765438
b365a81
8154735
8844eb5
49949c6
86345d8
ee44541
a1a7c64
d3bebe7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,7 @@ use graph::derive::CheapClone; | |
| use graph::prelude::*; | ||
| use graph::schema::{ | ||
| ast as sast, ApiSchema, INTROSPECTION_SCHEMA_FIELD_NAME, INTROSPECTION_TYPE_FIELD_NAME, | ||
| META_FIELD_NAME, META_FIELD_TYPE, | ||
| META_FIELD_NAME, META_FIELD_TYPE, SQL_CSV_FIELD_TYPE, SQL_JSON_FIELD_TYPE, | ||
| }; | ||
| use graph::schema::{ErrorPolicy, BLOCK_FIELD_TYPE}; | ||
|
|
||
|
|
@@ -256,7 +256,6 @@ impl StoreResolver { | |
| let parent_hash = parent_hash | ||
| .map(|hash| r::Value::String(format!("{}", hash))) | ||
| .unwrap_or(r::Value::Null); | ||
|
|
||
| let mut map = BTreeMap::new(); | ||
| let block = object! { | ||
| hash: hash, | ||
|
|
@@ -281,6 +280,90 @@ impl StoreResolver { | |
| ); | ||
| return Ok(r::Value::object(map)); | ||
| } | ||
|
|
||
| fn handle_sql(&self, field: &a::Field) -> Result<r::Value, QueryExecutionError> { | ||
|
|
||
| let input = field | ||
| .argument_value("input") | ||
| .ok_or_else(|| QueryExecutionError::EmptyQuery)?; | ||
|
|
||
| let input = match input { | ||
| graph::data::value::Value::Object(s) => s, | ||
| _ => { | ||
| return Err(QueryExecutionError::SqlError( | ||
| "Input is not an object".into(), | ||
| )) | ||
| } | ||
| }; | ||
|
|
||
| enum Format { | ||
| Json, | ||
| Csv, | ||
| } | ||
|
|
||
| let format = match input.get("format") { | ||
| Some(graph::data::value::Value::Enum(s)) => match s.as_str() { | ||
| "JSON" => Format::Json, | ||
| "CSV" => Format::Csv, | ||
| _ => { | ||
| return Err(QueryExecutionError::SqlError( | ||
| "Format must be json or csv".into(), | ||
| )) | ||
| } | ||
| }, | ||
| _ => Format::Json, | ||
| }; | ||
|
|
||
| let query = match input.get("query") { | ||
| Some(graph::data::value::Value::String(s)) => s, | ||
| _ => { | ||
| return Err(QueryExecutionError::SqlError( | ||
| "Query must be a string".into(), | ||
| )) | ||
| } | ||
| }; | ||
|
|
||
| let result = self.store.execute_sql(&query)?; | ||
| let result = result.into_iter().map(|q| q.0).collect::<Vec<_>>(); | ||
| let row_count = result.len(); | ||
| // columns should be available even if there's no data | ||
| // diesel doesn't support "dynamic query" so it doesn't return column names | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's now a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is good to know. I got to this crate when I was developing but at the time graph-node wasn't using diesel v2. Gonna take a look at it |
||
| // we are using this hacky way to get column names | ||
| // from the first row of the result | ||
| let columns = match result.first() { | ||
| Some(r::Value::Object(obj)) => obj | ||
| .iter() | ||
| .map(|(key, _)| r::Value::String(key.into())) | ||
| .collect::<Vec<_>>(), | ||
| _ => vec![], | ||
| }; | ||
| let sql_result = match format { | ||
| Format::Json => object! { | ||
| __typename: SQL_JSON_FIELD_TYPE, | ||
| columns: r::Value::List(columns), | ||
| rows: result, | ||
| rowCount: r::Value::Int(row_count as i64), | ||
| }, | ||
| Format::Csv => object! { | ||
| __typename: SQL_CSV_FIELD_TYPE, | ||
| columns: r::Value::List(columns), | ||
| result: r::Value::String(result.into_iter().filter_map(|v| { | ||
| match v { | ||
| r::Value::Object(obj) => Some( | ||
| obj | ||
| .iter() | ||
| .map(|(_, v)| v.to_string()) | ||
| .collect::<Vec<_>>() | ||
| .join(",")), | ||
| _ => None, | ||
| } | ||
| }).collect::<Vec<_>>().join("\n")), | ||
| rowCount: r::Value::Int(row_count as i64), | ||
| }, | ||
| }; | ||
|
|
||
| Ok(sql_result) | ||
| } | ||
| } | ||
|
|
||
| #[async_trait] | ||
|
|
@@ -329,6 +412,11 @@ impl Resolver for StoreResolver { | |
| if object_type.is_meta() { | ||
| return self.lookup_meta(field).await; | ||
| } | ||
|
|
||
| if object_type.is_sql() { | ||
| return self.handle_sql(field); | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the reason why we have this check here is in the remote case where the schema contains a type called |
||
|
|
||
| if let Some(r::Value::List(children)) = prefetched_object { | ||
| if children.len() > 1 { | ||
| let derived_from_field = | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the comment above should say something why
sql_itemsgets split out here (it's also missing an explanation whymeta_itemsare split out). In a nutshell:intro_setneeds to be handled differently because we use a different resolver (IntrospectionResolver)meta_itemsneed to be handled separately becauseprefetchcan not handle themsql_itemsneed to be handled separately for the same reason (?) If so, maybe we can just put them intometa_itemsand rename that variable to something more descriptive (maybenoprefetch_itemsthough I don't love that either)