Skip to content

Commit b2797e4

Browse files
committed
onConflict intospection working
1 parent 758ca22 commit b2797e4

File tree

3 files changed

+98
-45
lines changed

3 files changed

+98
-45
lines changed

src/graphql.rs

Lines changed: 67 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,7 @@ pub enum EnumSource {
11101110
Enum(Arc<Enum>),
11111111
FilterIs,
11121112
TableColumns(Arc<Table>),
1113+
OnConflictTarget(Arc<Table>),
11131114
}
11141115

11151116
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
@@ -1435,40 +1436,43 @@ impl ___Type for MutationType {
14351436
let table_base_type_name = self.schema.graphql_table_base_type_name(table);
14361437

14371438
if self.schema.graphql_table_insert_types_are_valid(table) {
1439+
let mut args = vec![__InputValue {
1440+
name_: "objects".to_string(),
1441+
type_: __Type::NonNull(NonNullType {
1442+
type_: Box::new(__Type::List(ListType {
1443+
type_: Box::new(__Type::NonNull(NonNullType {
1444+
type_: Box::new(__Type::InsertInput(InsertInputType {
1445+
table: Arc::clone(table),
1446+
schema: Arc::clone(&self.schema),
1447+
})),
1448+
})),
1449+
})),
1450+
}),
1451+
description: None,
1452+
default_value: None,
1453+
sql_type: None,
1454+
}];
1455+
1456+
if table.has_upsert_support() {
1457+
args.push(__InputValue {
1458+
name_: "onConflict".to_string(),
1459+
type_: __Type::InsertOnConflictInput(InsertOnConflictType {
1460+
table: Arc::clone(table),
1461+
schema: Arc::clone(&self.schema),
1462+
}),
1463+
description: None,
1464+
default_value: None,
1465+
sql_type: None,
1466+
});
1467+
}
1468+
14381469
f.push(__Field {
14391470
name_: format!("insertInto{}Collection", table_base_type_name),
14401471
type_: __Type::InsertResponse(InsertResponseType {
14411472
table: Arc::clone(table),
14421473
schema: Arc::clone(&self.schema),
14431474
}),
1444-
args: vec![
1445-
__InputValue {
1446-
name_: "objects".to_string(),
1447-
type_: __Type::NonNull(NonNullType {
1448-
type_: Box::new(__Type::List(ListType {
1449-
type_: Box::new(__Type::NonNull(NonNullType {
1450-
type_: Box::new(__Type::InsertInput(InsertInputType {
1451-
table: Arc::clone(table),
1452-
schema: Arc::clone(&self.schema),
1453-
})),
1454-
})),
1455-
})),
1456-
}),
1457-
description: None,
1458-
default_value: None,
1459-
sql_type: None,
1460-
},
1461-
__InputValue {
1462-
name_: "onConflict".to_string(),
1463-
type_: __Type::InsertOnConflictInput(InsertOnConflictType {
1464-
table: Arc::clone(table),
1465-
schema: Arc::clone(&self.schema),
1466-
}),
1467-
description: None,
1468-
default_value: None,
1469-
sql_type: None,
1470-
},
1471-
],
1475+
args,
14721476
description: Some(format!(
14731477
"Adds one or more `{}` records to the collection",
14741478
table_base_type_name
@@ -1660,6 +1664,10 @@ impl ___Type for EnumType {
16601664
"{}Field",
16611665
self.schema.graphql_table_base_type_name(&table)
16621666
)),
1667+
EnumSource::OnConflictTarget(table) => Some(format!(
1668+
"{}OnConflictConstraint",
1669+
self.schema.graphql_table_base_type_name(&table)
1670+
)),
16631671
}
16641672
}
16651673

@@ -1709,6 +1717,18 @@ impl ___Type for EnumType {
17091717
deprecation_reason: None,
17101718
})
17111719
.collect(),
1720+
EnumSource::OnConflictTarget(table) => {
1721+
table
1722+
.on_conflict_indexes()
1723+
.iter()
1724+
.map(|ix| __EnumValue {
1725+
// TODO, apply name restrictions
1726+
name: ix.name.clone(),
1727+
description: None,
1728+
deprecation_reason: None,
1729+
})
1730+
.collect()
1731+
}
17121732
})
17131733
}
17141734
}
@@ -3166,13 +3186,9 @@ impl ___Type for InsertOnConflictType {
31663186
// If triggers are involved, we can't detect if a field is non-null. Default
31673187
// all fields to non-null and let postgres errors handle it.
31683188
type_: __Type::NonNull(NonNullType {
3169-
type_: Box::new(__Type::List(ListType {
3170-
type_: Box::new(__Type::NonNull(NonNullType {
3171-
type_: Box::new(__Type::Enum(EnumType {
3172-
enum_: EnumSource::TableColumns(Arc::clone(&self.table)),
3173-
schema: Arc::clone(&self.schema),
3174-
})),
3175-
})),
3189+
type_: Box::new(__Type::Enum(EnumType {
3190+
enum_: EnumSource::OnConflictTarget(Arc::clone(&self.table)),
3191+
schema: Arc::clone(&self.schema),
31763192
})),
31773193
}),
31783194
description: Some(
@@ -4275,15 +4291,22 @@ impl __Schema {
42754291
table: Arc::clone(table),
42764292
schema: Arc::clone(&schema_rc),
42774293
}));
4278-
// Used by on conflict
4279-
types_.push(__Type::Enum(EnumType {
4280-
enum_: EnumSource::TableColumns(Arc::clone(table)),
4281-
schema: Arc::clone(&schema_rc),
4282-
}));
4283-
types_.push(__Type::InsertOnConflictInput(InsertOnConflictType {
4284-
table: Arc::clone(table),
4285-
schema: Arc::clone(&schema_rc),
4286-
}));
4294+
4295+
// Used exclusively by onConflict
4296+
if table.has_upsert_support() {
4297+
types_.push(__Type::InsertOnConflictInput(InsertOnConflictType {
4298+
table: Arc::clone(table),
4299+
schema: Arc::clone(&schema_rc),
4300+
}));
4301+
types_.push(__Type::Enum(EnumType {
4302+
enum_: EnumSource::TableColumns(Arc::clone(table)),
4303+
schema: Arc::clone(&schema_rc),
4304+
}));
4305+
types_.push(__Type::Enum(EnumType {
4306+
enum_: EnumSource::OnConflictTarget(Arc::clone(table)),
4307+
schema: Arc::clone(&schema_rc),
4308+
}));
4309+
}
42874310
}
42884311

42894312
if self.graphql_table_update_types_are_valid(table) {

src/parser_util.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ pub fn validate_arg_from_type(type_: &__Type, value: &gson::Value) -> Result<gso
414414
EnumSource::FilterIs => value.clone(),
415415
// TODO(or): Do I need to check directives here?
416416
EnumSource::TableColumns(_e) => value.clone(),
417+
EnumSource::OnConflictTarget(_e) => value.clone(),
417418
}
418419
}
419420
None => return Err(format!("Invalid input for {} type", enum_name)),

src/sql_types.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,14 +545,39 @@ impl Table {
545545
column_names: column_names.clone(),
546546
is_unique: true,
547547
is_primary_key: true,
548-
name: "dummy".to_string(),
548+
name: "NOT REQUIRED".to_string(),
549549
})
550550
}
551551
} else {
552552
None
553553
}
554554
}
555555

556+
pub fn on_conflict_indexes(&self) -> Vec<&Index> {
557+
// Indexes that are valid targets for an on conflict clause
558+
// must be unique, real (not comment directives), and must
559+
// not contain serial or generated columns because we don't
560+
// allow those to be set in insert statements
561+
let unique_indexes = self.indexes.iter().filter(|x| x.is_unique);
562+
563+
let allowed_column_names = self
564+
.columns
565+
.iter()
566+
.filter(|x| x.permissions.is_insertable)
567+
.filter(|x| !x.is_generated)
568+
.filter(|x| !x.is_serial)
569+
.map(|x| &x.name)
570+
.collect::<HashSet<&String>>();
571+
572+
unique_indexes
573+
.filter(|uix| {
574+
uix.column_names
575+
.iter()
576+
.all(|col_name| allowed_column_names.contains(col_name))
577+
})
578+
.collect::<Vec<&Index>>()
579+
}
580+
556581
pub fn primary_key_columns(&self) -> Vec<&Arc<Column>> {
557582
self.primary_key()
558583
.map(|x| x.column_names)
@@ -567,6 +592,10 @@ impl Table {
567592
.collect::<Vec<&Arc<Column>>>()
568593
}
569594

595+
pub fn has_upsert_support(&self) -> bool {
596+
self.on_conflict_indexes().len() > 0
597+
}
598+
570599
pub fn is_any_column_selectable(&self) -> bool {
571600
self.columns.iter().any(|x| x.permissions.is_selectable)
572601
}

0 commit comments

Comments
 (0)