mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-09-11 10:46:25 +03:00
Remove ndc_models types from arguments and order by IR (#798)
Note: This PR is stacked on #797 and should be merged after it. This PR removes the use of ndc_models types from the arguments and order by IR. It then shifts the logic that maps the IR to the ndc_models into the plan part of the code where IR -> ndc_model mapping is performed. This will help isolate the IR from ndc_models and move us towards being able to have multiple IR -> ndc_models mapping codes, one per supported ndc version. This is a functional no-op PR. V3_GIT_ORIGIN_REV_ID: 66be868ff4c8185c6190537d570d88813cb7f410
This commit is contained in:
parent
e44589931c
commit
90dbbccce5
@ -8,7 +8,6 @@ use lang_graphql::ast::common::Name;
|
||||
use lang_graphql::normalized_ast::{InputField, Value};
|
||||
use metadata_resolve::TypeMapping;
|
||||
use metadata_resolve::{Qualified, QualifiedBaseType, QualifiedTypeName, QualifiedTypeReference};
|
||||
use ndc_models;
|
||||
use nonempty::NonEmpty;
|
||||
use open_dds::{
|
||||
data_connector::DataConnectorColumnName,
|
||||
@ -18,9 +17,16 @@ use schema::GDS;
|
||||
use schema::{
|
||||
Annotation, ArgumentNameAndPath, ArgumentPresets, InputAnnotation, ModelInputAnnotation,
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
||||
use super::permissions;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
pub enum Argument {
|
||||
/// The argument is provided as a literal value
|
||||
Literal { value: serde_json::Value },
|
||||
}
|
||||
|
||||
/// Takes a field path and a serde_json object, and insert a serde_json value
|
||||
/// into that object, following the field path.
|
||||
///
|
||||
@ -72,7 +78,7 @@ pub(crate) fn follow_field_path_and_insert_value(
|
||||
pub(crate) fn process_model_arguments_presets(
|
||||
argument_presets: &ArgumentPresets,
|
||||
session_variables: &SessionVariables,
|
||||
model_arguments: &mut BTreeMap<DataConnectorArgumentName, ndc_models::Argument>,
|
||||
model_arguments: &mut BTreeMap<DataConnectorArgumentName, arguments::Argument>,
|
||||
usage_counts: &mut UsagesCounts,
|
||||
) -> Result<(), error::Error> {
|
||||
let ArgumentPresets { argument_presets } = argument_presets;
|
||||
@ -102,7 +108,7 @@ pub(crate) fn process_model_arguments_presets(
|
||||
None => {
|
||||
model_arguments.insert(
|
||||
argument_name.clone(),
|
||||
ndc_models::Argument::Literal {
|
||||
arguments::Argument::Literal {
|
||||
value: actual_value,
|
||||
},
|
||||
);
|
||||
@ -111,14 +117,8 @@ pub(crate) fn process_model_arguments_presets(
|
||||
Some(field_path) => {
|
||||
if let Some(current_arg) = model_arguments.get_mut(&argument_name.clone()) {
|
||||
let current_arg = match current_arg {
|
||||
ndc_models::Argument::Variable { name: _ } => {
|
||||
Err(error::InternalEngineError::ArgumentPresetExecution {
|
||||
description: "unexpected; ndc argument can't be a variable"
|
||||
.to_string(),
|
||||
})
|
||||
}
|
||||
ndc_models::Argument::Literal { value } => Ok(value),
|
||||
}?;
|
||||
arguments::Argument::Literal { value } => value,
|
||||
};
|
||||
if let Some(current_arg_object) = current_arg.as_object_mut() {
|
||||
arguments::follow_field_path_and_insert_value(
|
||||
&field_path,
|
||||
@ -166,7 +166,7 @@ pub fn build_ndc_model_arguments<'a, TInputFieldIter: Iterator<Item = &'a InputF
|
||||
model_operation_field: &Name,
|
||||
arguments: TInputFieldIter,
|
||||
model_type_mappings: &BTreeMap<Qualified<CustomTypeName>, TypeMapping>,
|
||||
) -> Result<BTreeMap<DataConnectorArgumentName, ndc_models::Argument>, error::Error> {
|
||||
) -> Result<BTreeMap<DataConnectorArgumentName, arguments::Argument>, error::Error> {
|
||||
let mut ndc_arguments = BTreeMap::new();
|
||||
for argument in arguments {
|
||||
match argument.info.generic {
|
||||
@ -187,7 +187,7 @@ pub fn build_ndc_model_arguments<'a, TInputFieldIter: Iterator<Item = &'a InputF
|
||||
)?;
|
||||
ndc_arguments.insert(
|
||||
ndc_table_argument.clone(),
|
||||
ndc_models::Argument::Literal {
|
||||
arguments::Argument::Literal {
|
||||
value: mapped_argument_value,
|
||||
},
|
||||
);
|
||||
|
@ -36,7 +36,7 @@ pub struct ModelSelection<'s> {
|
||||
pub(crate) collection: &'s CollectionName,
|
||||
|
||||
// Arguments for the NDC collection
|
||||
pub(crate) arguments: BTreeMap<DataConnectorArgumentName, ndc_models::Argument>,
|
||||
pub(crate) arguments: BTreeMap<DataConnectorArgumentName, arguments::Argument>,
|
||||
|
||||
// The boolean expression that would fetch a single row from this model
|
||||
pub(crate) filter_clause: ResolvedFilterExpression<'s>,
|
||||
@ -58,7 +58,7 @@ pub struct ModelSelection<'s> {
|
||||
}
|
||||
|
||||
struct ModelSelectAggregateArguments<'s> {
|
||||
model_arguments: BTreeMap<DataConnectorArgumentName, ndc_models::Argument>,
|
||||
model_arguments: BTreeMap<DataConnectorArgumentName, arguments::Argument>,
|
||||
filter_input_arguments: FilterInputArguments<'s>,
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ pub(crate) fn model_selection_ir<'s>(
|
||||
selection_set: &normalized_ast::SelectionSet<'s, GDS>,
|
||||
data_type: &Qualified<CustomTypeName>,
|
||||
model_source: &'s metadata_resolve::ModelSource,
|
||||
arguments: BTreeMap<DataConnectorArgumentName, ndc_models::Argument>,
|
||||
arguments: BTreeMap<DataConnectorArgumentName, arguments::Argument>,
|
||||
filter_clauses: ResolvedFilterExpression<'s>,
|
||||
permissions_predicate: &'s metadata_resolve::FilterPermission,
|
||||
limit: Option<u32>,
|
||||
@ -364,7 +364,7 @@ fn model_aggregate_selection_ir<'s>(
|
||||
aggregate_selection_set: &normalized_ast::SelectionSet<'s, GDS>,
|
||||
data_type: &Qualified<CustomTypeName>,
|
||||
model_source: &'s metadata_resolve::ModelSource,
|
||||
arguments: BTreeMap<DataConnectorArgumentName, ndc_models::Argument>,
|
||||
arguments: BTreeMap<DataConnectorArgumentName, arguments::Argument>,
|
||||
filter_clauses: ResolvedFilterExpression<'s>,
|
||||
permissions_predicate: &'s metadata_resolve::FilterPermission,
|
||||
limit: Option<u32>,
|
||||
|
@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
||||
|
||||
use crate::model_tracking::{count_model, UsagesCounts};
|
||||
use lang_graphql::normalized_ast::{self as normalized_ast, InputField};
|
||||
use ndc_models;
|
||||
use open_dds::data_connector::DataConnectorColumnName;
|
||||
use schema::OrderByRelationshipAnnotation;
|
||||
use schema::{Annotation, InputAnnotation, ModelInputAnnotation};
|
||||
use serde::Serialize;
|
||||
@ -16,19 +16,33 @@ use schema::GDS;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct ResolvedOrderBy<'s> {
|
||||
pub(crate) order_by: ndc_models::OrderBy,
|
||||
pub(crate) order_by_elements: Vec<OrderByElement>,
|
||||
// relationships that were used in the order_by expression. This is helpful
|
||||
// for collecting relatinships and sending collection_relationships
|
||||
pub(crate) relationships: BTreeMap<NDCRelationshipName, LocalModelRelationshipInfo<'s>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct OrderByElement {
|
||||
pub order_direction: schema::ModelOrderByDirection,
|
||||
pub target: OrderByTarget,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum OrderByTarget {
|
||||
Column {
|
||||
name: DataConnectorColumnName,
|
||||
relationship_path: Vec<NDCRelationshipName>,
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) fn build_ndc_order_by<'s>(
|
||||
args_field: &InputField<'s, GDS>,
|
||||
usage_counts: &mut UsagesCounts,
|
||||
) -> Result<ResolvedOrderBy<'s>, error::Error> {
|
||||
match &args_field.value {
|
||||
normalized_ast::Value::List(arguments) => {
|
||||
let mut ndc_order_elements = Vec::new();
|
||||
let mut order_by_elements = Vec::new();
|
||||
let mut relationships = BTreeMap::new();
|
||||
|
||||
for v in arguments {
|
||||
@ -53,7 +67,7 @@ pub(crate) fn build_ndc_order_by<'s>(
|
||||
&mut relationships,
|
||||
usage_counts,
|
||||
)?;
|
||||
ndc_order_elements.extend(order_by_element);
|
||||
order_by_elements.extend(order_by_element);
|
||||
} else {
|
||||
Err(error::Error::OrderByObjectShouldExactlyHaveOneKeyValuePair)?;
|
||||
}
|
||||
@ -64,9 +78,7 @@ pub(crate) fn build_ndc_order_by<'s>(
|
||||
}
|
||||
}
|
||||
Ok(ResolvedOrderBy {
|
||||
order_by: ndc_models::OrderBy {
|
||||
elements: ndc_order_elements,
|
||||
},
|
||||
order_by_elements,
|
||||
relationships,
|
||||
})
|
||||
}
|
||||
@ -107,7 +119,7 @@ pub(crate) fn build_ndc_order_by_element<'s>(
|
||||
mut relationship_paths: Vec<NDCRelationshipName>,
|
||||
relationships: &mut BTreeMap<NDCRelationshipName, LocalModelRelationshipInfo<'s>>,
|
||||
usage_counts: &mut UsagesCounts,
|
||||
) -> Result<Vec<ndc_models::OrderByElement>, error::Error> {
|
||||
) -> Result<Vec<OrderByElement>, error::Error> {
|
||||
match argument.info.generic {
|
||||
// The column that we want to use for ordering. If the column happens to be
|
||||
// a relationship column, we'll have to join all the paths to specify NDC,
|
||||
@ -119,10 +131,7 @@ pub(crate) fn build_ndc_order_by_element<'s>(
|
||||
let order_direction = match &order_by_value.info.generic {
|
||||
Annotation::Input(InputAnnotation::Model(
|
||||
ModelInputAnnotation::ModelOrderByDirection { direction },
|
||||
)) => match &direction {
|
||||
schema::ModelOrderByDirection::Asc => ndc_models::OrderDirection::Asc,
|
||||
schema::ModelOrderByDirection::Desc => ndc_models::OrderDirection::Desc,
|
||||
},
|
||||
)) => direction,
|
||||
&annotation => {
|
||||
return Err(error::InternalEngineError::UnexpectedAnnotation {
|
||||
annotation: annotation.clone(),
|
||||
@ -130,48 +139,12 @@ pub(crate) fn build_ndc_order_by_element<'s>(
|
||||
}
|
||||
};
|
||||
|
||||
let mut order_by_element_path = Vec::new();
|
||||
// When using a nested relationship column, you'll have to provide all the relationships(paths)
|
||||
// NDC has to traverse to access the column. The ordering of that paths is important.
|
||||
// The order decides how to access the column.
|
||||
//
|
||||
// For example, if you have a model called `User` with a relationship column called `Posts`
|
||||
// which has a relationship column called `Comments` which has a non-relationship column
|
||||
// called `text`, you'll have to provide the following paths to access the `text` column:
|
||||
// ["UserPosts", "PostsComments"]
|
||||
for path in &relationship_paths {
|
||||
order_by_element_path.push(ndc_models::PathElement {
|
||||
relationship: ndc_models::RelationshipName::from(path.0.as_str()),
|
||||
arguments: BTreeMap::new(),
|
||||
// 'AND' predicate indicates that the column can be accessed
|
||||
// by joining all the relationships paths provided
|
||||
predicate: Some(Box::new(ndc_models::Expression::And {
|
||||
// TODO(naveen): Add expressions here, when we support sorting with predicates.
|
||||
//
|
||||
// There are two types of sorting:
|
||||
// 1. Sorting without predicates
|
||||
// 2. Sorting with predicates
|
||||
//
|
||||
// In the 1st sort, we sort all the elements of the results either in ascending
|
||||
// or descing order based on the order_by argument.
|
||||
//
|
||||
// In the 2nd sort, we want fetch the entire result but only sort a subset
|
||||
// of result and put those sorted set either at the beginning or at the end of the
|
||||
// result.
|
||||
//
|
||||
// Currently we only support the 1st type of sort. Hence we don't have any expressions/predicate.
|
||||
expressions: Vec::new(),
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
let order_element = ndc_models::OrderByElement {
|
||||
order_direction,
|
||||
let order_element = OrderByElement {
|
||||
order_direction: order_direction.clone(),
|
||||
// TODO(naveen): When aggregates are supported, extend this to support other ndc_models::OrderByTarget
|
||||
target: ndc_models::OrderByTarget::Column {
|
||||
name: ndc_models::FieldName::from(ndc_column.as_str()),
|
||||
path: order_by_element_path,
|
||||
field_path: None,
|
||||
target: OrderByTarget::Column {
|
||||
name: ndc_column.clone(),
|
||||
relationship_path: relationship_paths,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,9 @@ use indexmap::IndexMap;
|
||||
use super::relationships;
|
||||
use super::selection_set;
|
||||
use crate::ir::aggregates::{AggregateFieldSelection, AggregateSelectionSet};
|
||||
use crate::ir::arguments;
|
||||
use crate::ir::model_selection::ModelSelection;
|
||||
use crate::ir::order_by;
|
||||
use crate::plan::error;
|
||||
use crate::remote_joins::types::{JoinLocations, MonotonicCounter, RemoteJoin};
|
||||
|
||||
@ -34,7 +36,10 @@ pub(crate) fn ndc_query<'s, 'ir>(
|
||||
fields: ndc_fields,
|
||||
limit: ir.limit,
|
||||
offset: ir.offset,
|
||||
order_by: ir.order_by.as_ref().map(|x| x.order_by.clone()),
|
||||
order_by: ir
|
||||
.order_by
|
||||
.as_ref()
|
||||
.map(|x| ndc_order_by(&x.order_by_elements)),
|
||||
predicate: ir.filter_clause.expression.clone(),
|
||||
};
|
||||
|
||||
@ -123,10 +128,85 @@ pub(crate) fn ndc_ir<'s, 'ir>(
|
||||
arguments: ir
|
||||
.arguments
|
||||
.iter()
|
||||
.map(|(k, v)| (ndc_models::ArgumentName::from(k.as_str()), v.clone()))
|
||||
.map(|(k, v)| {
|
||||
let literal_value = match v {
|
||||
arguments::Argument::Literal { value } => value,
|
||||
};
|
||||
(
|
||||
ndc_models::ArgumentName::from(k.as_str()),
|
||||
ndc_models::Argument::Literal {
|
||||
value: literal_value.clone(),
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
collection_relationships,
|
||||
variables: None,
|
||||
};
|
||||
Ok((query_request, join_locations))
|
||||
}
|
||||
|
||||
fn ndc_order_by(order_by_elements: &[order_by::OrderByElement]) -> ndc_models::OrderBy {
|
||||
ndc_models::OrderBy {
|
||||
elements: order_by_elements
|
||||
.iter()
|
||||
.map(|element| ndc_models::OrderByElement {
|
||||
order_direction: match element.order_direction {
|
||||
schema::ModelOrderByDirection::Asc => ndc_models::OrderDirection::Asc,
|
||||
schema::ModelOrderByDirection::Desc => ndc_models::OrderDirection::Desc,
|
||||
},
|
||||
target: ndc_order_by_target(&element.target),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ndc_order_by_target(target: &order_by::OrderByTarget) -> ndc_models::OrderByTarget {
|
||||
match target {
|
||||
order_by::OrderByTarget::Column {
|
||||
name,
|
||||
relationship_path,
|
||||
} => {
|
||||
let mut order_by_element_path = Vec::new();
|
||||
// When using a nested relationship column, you'll have to provide all the relationships(paths)
|
||||
// NDC has to traverse to access the column. The ordering of that paths is important.
|
||||
// The order decides how to access the column.
|
||||
//
|
||||
// For example, if you have a model called `User` with a relationship column called `Posts`
|
||||
// which has a relationship column called `Comments` which has a non-relationship column
|
||||
// called `text`, you'll have to provide the following paths to access the `text` column:
|
||||
// ["UserPosts", "PostsComments"]
|
||||
for path in relationship_path {
|
||||
order_by_element_path.push(ndc_models::PathElement {
|
||||
relationship: ndc_models::RelationshipName::from(path.0.as_str()),
|
||||
arguments: BTreeMap::new(),
|
||||
// 'AND' predicate indicates that the column can be accessed
|
||||
// by joining all the relationships paths provided
|
||||
predicate: Some(Box::new(ndc_models::Expression::And {
|
||||
// TODO(naveen): Add expressions here, when we support sorting with predicates.
|
||||
//
|
||||
// There are two types of sorting:
|
||||
// 1. Sorting without predicates
|
||||
// 2. Sorting with predicates
|
||||
//
|
||||
// In the 1st sort, we sort all the elements of the results either in ascending
|
||||
// or descing order based on the order_by argument.
|
||||
//
|
||||
// In the 2nd sort, we want fetch the entire result but only sort a subset
|
||||
// of result and put those sorted set either at the beginning or at the end of the
|
||||
// result.
|
||||
//
|
||||
// Currently we only support the 1st type of sort. Hence we don't have any expressions/predicate.
|
||||
expressions: Vec::new(),
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
ndc_models::OrderByTarget::Column {
|
||||
name: ndc_models::FieldName::from(name.as_str()),
|
||||
path: order_by_element_path,
|
||||
field_path: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user