Skip to content

Commit

Permalink
on conflict functional
Browse files Browse the repository at this point in the history
  • Loading branch information
olirice committed Aug 26, 2024
1 parent fe15ade commit 8da22dc
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 9 deletions.
42 changes: 36 additions & 6 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,25 +200,22 @@ where
variable_definitions,
)?;

let insert_type: OnConflictType = match field.get_arg("onConflict") {
let conflict_type: OnConflictType = match field.get_arg("onConflict") {
None => return Ok(None),
Some(x) => match x.type_().unmodified_type() {
__Type::OnConflictInput(insert_on_conflict) => insert_on_conflict,
_ => return Err("Could not locate Insert Entity type".to_string()),
},
};

let filter: FilterBuilder =
read_argument_filter(field, query_field, variables, variable_definitions)?;

let on_conflict_builder = match validated {
gson::Value::Absent | gson::Value::Null => None,
gson::Value::Object(contents) => {
let constraint = match contents
.get("constraint")
.expect("OnConflict revalidation error. Expected constraint")
{
gson::Value::String(ix_name) => insert_type
gson::Value::String(ix_name) => conflict_type
.table
.indexes
.iter()
Expand All @@ -231,6 +228,36 @@ where
}
};

// TODO: Filter reading logic is partially duplicated from read_argument_filter
// ideally this should be refactored
let filter_gson = contents
.get("filter")
.expect("onConflict revalidation error");

let filter = match filter_gson {
gson::Value::Null | gson::Value::Absent => FilterBuilder { elems: vec![] },
gson::Value::Object(_) => {
let filter_type = conflict_type
.input_fields()
.expect("Failed to unwrap input fields on OnConflict type")
.iter()
.find(|in_f| in_f.name() == "filter")
.expect("Failed to get filter input_field on onConflict type")
.type_()
.unmodified_type();

if !matches!(filter_type, __Type::FilterEntity(_)) {
return Err("Could not locate Filter Entity type".to_string());
}
let filter_field_map = input_field_map(&filter_type);
let filter_elems = create_filters(&filter_gson, &filter_field_map)?;
FilterBuilder {
elems: filter_elems,
}
}
_ => return Err("OnConflict revalidation error. invalid filter object".to_string()),
};

let update_fields = match contents
.get("updateFields")
.expect("OnConflict revalidation error. Expected updateFields")
Expand All @@ -240,7 +267,7 @@ where
for col_name in col_names {
match col_name {
gson::Value::String(c) => {
let col = insert_type.table.columns.iter().find(|column| &column.name == c).expect("OnConflict revalidation error. updateFields: unknown column name");
let col = conflict_type.table.columns.iter().find(|column| &column.name == c).expect("OnConflict revalidation error. updateFields: unknown column name");
update_columns.insert(Arc::clone(col));
}
_ => return Err("OnConflict revalidation error. Expected updateFields to be column names".to_string()),
Expand Down Expand Up @@ -1145,11 +1172,14 @@ where
variable_definitions,
)?;

//return Err(format!("Err {:?}", validated));

let filter_type = field
.get_arg("filter")
.expect("failed to get filter argument")
.type_()
.unmodified_type();

if !matches!(filter_type, __Type::FilterEntity(_)) {
return Err("Could not locate Filter Entity type".to_string());
}
Expand Down
1 change: 0 additions & 1 deletion src/graphql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3181,7 +3181,6 @@ impl ___Type for OnConflictType {
fn input_fields(&self) -> Option<Vec<__InputValue>> {
Some(vec![
__InputValue {
// TODO: Create a custom type for available constraints
name_: "constraint".to_string(),
// If triggers are involved, we can't detect if a field is non-null. Default
// all fields to non-null and let postgres errors handle it.
Expand Down
5 changes: 4 additions & 1 deletion src/parser_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,10 @@ pub fn validate_arg_from_input_object(

match input_obj.get(&obj_field_key) {
None => {
validate_arg_from_type(&obj_field_type, &GsonValue::Null)?;
// If there was no provided key, use "Absent" so all arguments
// always exist in the validated input datat
validate_arg_from_type(&obj_field_type, &GsonValue::Absent)?;
out_map.insert(obj_field_key, GsonValue::Absent);
}
Some(x) => {
let out_val = validate_arg_from_type(&obj_field_type, x)?;
Expand Down
36 changes: 35 additions & 1 deletion src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,11 +309,45 @@ impl MutationEntrypoint<'_> for InsertBuilder {

let values_clause = values_rows_clause.join(", ");

let insert_quoted_block_name = rand_block_name();
let on_conflict_clause = match &self.on_conflict {
Some(on_conflict) => {
let constraint_name = &on_conflict.constraint.name;
let do_update_set_clause = on_conflict
.update_fields
.iter()
.map(|col| {
format!(
"{} = excluded.{}",
quote_ident(&col.name),
quote_ident(&col.name),
)
})
.join(", ");

let conflict_where_clause = on_conflict.filter.to_where_clause(
&insert_quoted_block_name,
&self.table,
param_context,
)?;

format!(
"
on conflict on constraint {constraint_name}
do update set {do_update_set_clause}
where {conflict_where_clause}
",
)
}
None => "".to_string(),
};

Ok(format!(
"
with affected as (
insert into {quoted_schema}.{quoted_table}({referenced_columns_clause})
insert into {quoted_schema}.{quoted_table} as {insert_quoted_block_name} ({referenced_columns_clause})
values {values_clause}
{on_conflict_clause}
returning {selectable_columns_clause}
)
select
Expand Down

0 comments on commit 8da22dc

Please sign in to comment.