Skip to content
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

feat: support createManyAndReturn #4842

Merged
merged 10 commits into from May 3, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion prisma-fmt/src/get_dmmf.rs

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -1,6 +1,7 @@
mod create;
mod create_list;
mod create_many;
mod create_many_and_return;
mod default_value;
mod delete;
mod delete_many;
Expand Down
Expand Up @@ -265,13 +265,16 @@ pub(crate) async fn create_records_returning(
selected_fields: FieldSelection,
ctx: &Context<'_>,
) -> crate::Result<ManyRecords> {
let field_names: Vec<String> = selected_fields.db_names().collect();
let idents = selected_fields.type_identifiers_with_arities();
let field_names: Vec<String> = selected_fields.db_names().collect();
Weakky marked this conversation as resolved.
Show resolved Hide resolved
let meta = column_metadata::create(&field_names, &idents);
let mut records = ManyRecords::new(field_names.clone());
let inserts = generate_insert_statements(model, args, skip_duplicates, Some(&selected_fields.into()), ctx);

let mut records = ManyRecords::new(field_names.clone());

for insert in inserts {
let result_set = conn.query(insert.into()).await?;

for result_row in result_set {
let sql_row = result_row.to_sql_row(&meta)?;
let record = Record::from(sql_row);
Expand Down
Expand Up @@ -137,6 +137,7 @@ fn read_many_by_queries(
record_not_found()
} else {
let nested: Vec<QueryResult> = process_nested(tx, query.nested, Some(&records)).await?;

Ok(RecordSelection {
name: query.name,
fields: query.selection_order,
Expand Down Expand Up @@ -268,7 +269,7 @@ async fn aggregate(
}))
}

fn process_nested<'conn>(
pub(crate) fn process_nested<'conn>(
tx: &'conn mut dyn ConnectionLike,
nested: Vec<ReadQuery>,
parent_result: Option<&'conn ManyRecords>,
Expand Down
13 changes: 11 additions & 2 deletions query-engine/core/src/interpreter/query_interpreters/write.rs
Expand Up @@ -72,18 +72,21 @@ async fn create_many(
.create_records_returning(&q.model, q.args, q.skip_duplicates, selected_fields.fields, trace_id)
.await?;

let nested: Vec<QueryResult> = super::read::process_nested(tx, selected_fields.nested, Some(&records)).await?;

let selection = RecordSelection {
name: q.name,
fields: selected_fields.order,
records,
nested: vec![],
nested,
model: q.model,
virtual_fields: vec![],
};

Ok(QueryResult::RecordSelection(Some(Box::new(selection))))
} else {
let affected_records = tx.create_records(&q.model, q.args, q.skip_duplicates, trace_id).await?;

Ok(QueryResult::Count(affected_records))
}
}
Expand All @@ -108,6 +111,7 @@ async fn create_many_split_by_shape(

if let Some(selected_fields) = q.selected_fields {
let mut result: Option<ManyRecords> = None;

for args in args_by_shape.into_values() {
let current_batch = tx
.create_records_returning(
Expand Down Expand Up @@ -137,24 +141,29 @@ async fn create_many_split_by_shape(
.await?
};

let nested: Vec<QueryResult> =
super::read::process_nested(tx, selected_fields.nested.clone(), Some(&records)).await?;

let selection = RecordSelection {
name: q.name,
fields: selected_fields.order,
records,
nested: vec![],
nested,
model: q.model,
virtual_fields: vec![],
};

Ok(QueryResult::RecordSelection(Some(Box::new(selection))))
} else {
let mut result = 0;

for args in args_by_shape.into_values() {
let affected_records = tx
.create_records(&q.model, args, q.skip_duplicates, trace_id.clone())
.await?;
result += affected_records;
}

Ok(QueryResult::Count(result))
}
}
Expand Down
3 changes: 2 additions & 1 deletion query-engine/core/src/query_ast/write.rs
@@ -1,6 +1,6 @@
//! Write query AST
use super::{FilteredNestedMutation, FilteredQuery};
use crate::{RecordQuery, ToGraphviz};
use crate::{ReadQuery, RecordQuery, ToGraphviz};
use connector::{DatasourceFieldName, NativeUpsert, RecordFilter, WriteArgs};
use query_structure::{prelude::*, Filter};
use std::collections::HashMap;
Expand Down Expand Up @@ -286,6 +286,7 @@ pub struct CreateManyRecords {
pub struct CreateManyRecordsFields {
pub fields: FieldSelection,
pub order: Vec<String>,
pub nested: Vec<ReadQuery>,
}

#[derive(Debug, Clone)]
Expand Down
3 changes: 2 additions & 1 deletion query-engine/core/src/query_graph_builder/builder.rs
Expand Up @@ -113,7 +113,8 @@ impl<'a> QueryGraphBuilder<'a> {
(QueryTag::Aggregate, Some(m)) => read::aggregate(parsed_field, m).map(Into::into),
(QueryTag::GroupBy, Some(m)) => read::group_by(parsed_field, m).map(Into::into),
(QueryTag::CreateOne, Some(m)) => QueryGraph::root(|g| write::create_record(g, query_schema, m, parsed_field)),
(QueryTag::CreateMany, Some(m)) => QueryGraph::root(|g| write::create_many_records(g, query_schema,m, parsed_field)),
(QueryTag::CreateMany, Some(m)) => QueryGraph::root(|g| write::create_many_records(g, query_schema, m, false, parsed_field)),
(QueryTag::CreateManyAndReturn, Some(m)) => QueryGraph::root(|g| write::create_many_records(g, query_schema, m, true, parsed_field)),
(QueryTag::UpdateOne, Some(m)) => QueryGraph::root(|g| write::update_record(g, query_schema, m, parsed_field)),
(QueryTag::UpdateMany, Some(m)) => QueryGraph::root(|g| write::update_many_records(g, query_schema, m, parsed_field)),
(QueryTag::UpsertOne, Some(m)) => QueryGraph::root(|g| write::upsert_record(g, query_schema, m, parsed_field)),
Expand Down
2 changes: 1 addition & 1 deletion query-engine/core/src/query_graph_builder/mod.rs
Expand Up @@ -3,8 +3,8 @@
mod builder;
mod error;
mod extractors;
mod read;

pub(crate) mod read;
pub(crate) mod write;
pub(crate) use extractors::*;

Expand Down
7 changes: 2 additions & 5 deletions query-engine/core/src/query_graph_builder/read/one.rs
Expand Up @@ -44,11 +44,8 @@ fn find_unique_with_options(

let name = field.name;
let alias = field.alias;
let nested_fields = field.nested_fields.unwrap().fields;
let selection_order = utils::collect_selection_order(&nested_fields);
let selected_fields = utils::collect_selected_fields(&nested_fields, None, &model, query_schema)?;
let nested = utils::collect_nested_queries(nested_fields, &model, query_schema)?;
let selected_fields = utils::merge_relation_selections(selected_fields, None, &nested);
let (selected_fields, selection_order, nested) =
utils::extract_selected_fields(field.nested_fields.unwrap().fields, &model, query_schema)?;

let relation_load_strategy = get_relation_load_strategy(requested_rel_load_strategy, None, &nested, query_schema)?;

Expand Down
13 changes: 13 additions & 0 deletions query-engine/core/src/query_graph_builder/read/utils.rs
Expand Up @@ -306,3 +306,16 @@ fn query_can_be_resolved_with_joins(cursor: Option<&SelectionResult>, nested_que
_ => false,
})
}

pub(crate) fn extract_selected_fields(
nested_fields: Vec<FieldPair<'_>>,
model: &Model,
query_schema: &QuerySchema,
) -> crate::QueryGraphBuilderResult<(FieldSelection, Vec<String>, Vec<ReadQuery>)> {
let selection_order = utils::collect_selection_order(&nested_fields);
let selected_fields = utils::collect_selected_fields(&nested_fields, None, model, query_schema)?;
let nested = utils::collect_nested_queries(nested_fields, model, query_schema)?;
let selected_fields = utils::merge_relation_selections(selected_fields, None, &nested);

Ok((selected_fields, selection_order, nested))
}
17 changes: 16 additions & 1 deletion query-engine/core/src/query_graph_builder/write/create.rs
Expand Up @@ -68,6 +68,7 @@ pub(crate) fn create_many_records(
graph: &mut QueryGraph,
query_schema: &QuerySchema,
model: Model,
with_field_selection: bool,
mut field: ParsedField<'_>,
) -> QueryGraphBuilderResult<()> {
graph.flag_transactional();
Expand All @@ -93,16 +94,30 @@ pub(crate) fn create_many_records(
})
.collect::<QueryGraphBuilderResult<Vec<_>>>()?;

let selected_fields = if with_field_selection {
let (selected_fields, selection_order, nested_read) =
super::read::utils::extract_selected_fields(field.nested_fields.unwrap().fields, &model, query_schema)?;

Some(CreateManyRecordsFields {
fields: selected_fields,
order: selection_order,
nested: nested_read,
})
} else {
None
};

let query = CreateManyRecords {
name: field.name,
model,
args,
skip_duplicates,
selected_fields: None,
selected_fields,
split_by_shape: !query_schema.has_capability(ConnectorCapability::SupportsDefaultInInsert),
};

graph.create_node(Query::Write(WriteQuery::CreateManyRecords(query)));

Ok(())
}

Expand Down
Expand Up @@ -65,6 +65,7 @@ pub fn nested_create(
Some(CreateManyRecordsFields {
fields: selected_fields,
order: selection_order,
nested: Vec::new(),
})
} else {
None
Expand Down
Binary file not shown.
46 changes: 45 additions & 1 deletion query-engine/schema/src/build/mutations/create_many.rs
Expand Up @@ -2,7 +2,7 @@ use super::*;
use crate::{Identifier, IdentifierType, InputField, InputType, OutputField, OutputType, QueryInfo, QueryTag};
use constants::*;
use input_types::{fields::data_input_mapper::*, list_union_type};
use output_types::objects;
use output_types::{field, objects};
use psl::datamodel_connector::ConnectorCapability;
use query_structure::{Model, RelationFieldRef};

Expand All @@ -22,6 +22,50 @@ pub(crate) fn create_many(ctx: &'_ QuerySchema, model: Model) -> OutputField<'_>
)
}

/// Builds a create many mutation field (e.g. createManyUsers) for given model.
pub(crate) fn create_many_and_return(ctx: &'_ QuerySchema, model: Model) -> OutputField<'_> {
let field_name = format!("createMany{}AndReturn", model.name());
Weakky marked this conversation as resolved.
Show resolved Hide resolved
let model_id = model.id;
let object_type = create_many_and_return_output_type(ctx, model.clone());

field(
field_name,
move || create_many_arguments(ctx, model),
OutputType::list(InnerOutputType::Object(object_type)),
Some(QueryInfo {
model: Some(model_id),
tag: QueryTag::CreateManyAndReturn,
}),
)
}

pub(crate) fn create_many_and_return_output_type(ctx: &'_ QuerySchema, model: Model) -> ObjectType<'_> {
let model_id = model.id;
let mut obj = ObjectType::new(
Identifier::new_model(IdentifierType::CreateManyAndReturnOutput(model.clone())),
move || {
let mut fields: Vec<_> = model
.fields()
.scalar()
.map(|sf| field::map_output_field(ctx, sf.into()))
.collect();

// If the relation is inlined in the enclosing model, that means the foreign keys can be set at creation
// and thus it makes sense to enable querying this relation.
for rf in model.fields().relation() {
if rf.is_inlined_on_enclosing_model() {
fields.push(field::map_output_field(ctx, rf.into()));
}
}

fields
},
);

obj.model = Some(model_id);
obj
}

/// Builds "skip_duplicates" and "data" arguments intended for the create many field.
pub(crate) fn create_many_arguments(ctx: &'_ QuerySchema, model: Model) -> Vec<InputField<'_>> {
let create_many_type = InputType::object(create_many_object_type(ctx, model, None));
Expand Down
2 changes: 1 addition & 1 deletion query-engine/schema/src/build/mutations/mod.rs
@@ -1,7 +1,7 @@
pub(crate) mod create_many;
pub(crate) mod create_one;

pub(crate) use create_many::create_many;
pub(crate) use create_many::{create_many, create_many_and_return};
pub(crate) use create_one::create_one;

use super::*;
@@ -1,6 +1,6 @@
use super::*;
use input_types::fields::arguments;
use mutations::{create_many, create_one};
use mutations::{create_many, create_many_and_return, create_one};
use psl::datamodel_connector::ConnectorCapability;
use query_structure::{DefaultKind, PrismaValue};

Expand All @@ -20,8 +20,13 @@ pub(crate) fn mutation_fields(ctx: &QuerySchema) -> Vec<FieldFn> {
field!(create_one, model);

field!(upsert_item_field, model);

if ctx.has_capability(ConnectorCapability::CreateMany) {
field!(create_many, model);

if ctx.has_capability(ConnectorCapability::InsertReturning) {
field!(create_many_and_return, model);
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions query-engine/schema/src/build/output_types/query_type.rs
Expand Up @@ -15,7 +15,7 @@ pub(crate) fn query_fields(ctx: &QuerySchema) -> Vec<FieldFn> {
for model in ctx.internal_data_model.models() {
field!(find_first_field, model);
field!(find_first_or_throw_field, model);
field!(all_items_field, model);
field!(find_many_field, model);
field!(plain_aggregation_field, model);
field!(group_by_aggregation_field, model);
field!(find_unique_field, model);
Expand Down Expand Up @@ -113,7 +113,7 @@ fn find_first_or_throw_field(ctx: &QuerySchema, model: Model) -> OutputField<'_>
}

/// Builds a "multiple" query arity items field (e.g. "users", "posts", ...) for given model.
fn all_items_field(ctx: &QuerySchema, model: Model) -> OutputField<'_> {
fn find_many_field(ctx: &QuerySchema, model: Model) -> OutputField<'_> {
let field_name = format!("findMany{}", model.name());
let object_type = objects::model::model_object_type(ctx, model.clone());
let model_id = model.id;
Expand Down
4 changes: 4 additions & 0 deletions query-engine/schema/src/identifier_type.rs
Expand Up @@ -22,6 +22,7 @@ pub enum IdentifierType {
CompositeUpdateManyInput(CompositeType),
CompositeUpsertObjectInput(CompositeType),
CreateManyInput(Model, Option<RelationField>),
CreateManyAndReturnOutput(Model),
CreateOneScalarList(ScalarField),
Enum(InternalEnum),
FieldUpdateOperationsInput(bool, String),
Expand Down Expand Up @@ -296,6 +297,9 @@ impl std::fmt::Display for IdentifierType {
Some(ref rf) => write!(f, "{}CreateMany{}Input", model.name(), capitalize(rf.name())),
_ => write!(f, "{}CreateManyInput", model.name()),
},
IdentifierType::CreateManyAndReturnOutput(model) => {
write!(f, "CreateMany{}AndReturnOutputType", model.name())
}
IdentifierType::UncheckedUpdateManyInput(model, related_field) => match related_field {
Some(rf) => write!(
f,
Expand Down
3 changes: 3 additions & 0 deletions query-engine/schema/src/query_schema.rs
Expand Up @@ -232,6 +232,7 @@ pub enum QueryTag {
FindMany,
CreateOne,
CreateMany,
CreateManyAndReturn,
UpdateOne,
UpdateMany,
DeleteOne,
Expand All @@ -257,6 +258,7 @@ impl fmt::Display for QueryTag {
Self::FindMany => "findMany",
Self::CreateOne => "createOne",
Self::CreateMany => "createMany",
Self::CreateManyAndReturn => "createManyAndReturn",
Self::UpdateOne => "updateOne",
Self::UpdateMany => "updateMany",
Self::DeleteOne => "deleteOne",
Expand Down Expand Up @@ -285,6 +287,7 @@ impl From<&str> for QueryTag {
"findMany" => Self::FindMany,
"createOne" => Self::CreateOne,
"createMany" => Self::CreateMany,
"createManyAndReturn" => Self::CreateManyAndReturn,
"updateOne" => Self::UpdateOne,
"updateMany" => Self::UpdateMany,
"deleteOne" => Self::DeleteOne,
Expand Down