Skip to content

Commit 4708590

Browse files
committed
onConflict intospection working
1 parent 561ae3a commit 4708590

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
@@ -1112,6 +1112,7 @@ pub enum EnumSource {
11121112
Enum(Arc<Enum>),
11131113
FilterIs,
11141114
TableColumns(Arc<Table>),
1115+
OnConflictTarget(Arc<Table>),
11151116
}
11161117

11171118
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
@@ -1429,40 +1430,43 @@ impl ___Type for MutationType {
14291430
let table_base_type_name = self.schema.graphql_table_base_type_name(table);
14301431

14311432
if self.schema.graphql_table_insert_types_are_valid(table) {
1433+
let mut args = vec![__InputValue {
1434+
name_: "objects".to_string(),
1435+
type_: __Type::NonNull(NonNullType {
1436+
type_: Box::new(__Type::List(ListType {
1437+
type_: Box::new(__Type::NonNull(NonNullType {
1438+
type_: Box::new(__Type::InsertInput(InsertInputType {
1439+
table: Arc::clone(table),
1440+
schema: Arc::clone(&self.schema),
1441+
})),
1442+
})),
1443+
})),
1444+
}),
1445+
description: None,
1446+
default_value: None,
1447+
sql_type: None,
1448+
}];
1449+
1450+
if table.has_upsert_support() {
1451+
args.push(__InputValue {
1452+
name_: "onConflict".to_string(),
1453+
type_: __Type::InsertOnConflictInput(InsertOnConflictType {
1454+
table: Arc::clone(table),
1455+
schema: Arc::clone(&self.schema),
1456+
}),
1457+
description: None,
1458+
default_value: None,
1459+
sql_type: None,
1460+
});
1461+
}
1462+
14321463
f.push(__Field {
14331464
name_: format!("insertInto{}Collection", table_base_type_name),
14341465
type_: __Type::InsertResponse(InsertResponseType {
14351466
table: Arc::clone(table),
14361467
schema: Arc::clone(&self.schema),
14371468
}),
1438-
args: vec![
1439-
__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-
__InputValue {
1456-
name_: "onConflict".to_string(),
1457-
type_: __Type::InsertOnConflictInput(InsertOnConflictType {
1458-
table: Arc::clone(table),
1459-
schema: Arc::clone(&self.schema),
1460-
}),
1461-
description: None,
1462-
default_value: None,
1463-
sql_type: None,
1464-
},
1465-
],
1469+
args,
14661470
description: Some(format!(
14671471
"Adds one or more `{}` records to the collection",
14681472
table_base_type_name
@@ -1654,6 +1658,10 @@ impl ___Type for EnumType {
16541658
"{}Field",
16551659
self.schema.graphql_table_base_type_name(&table)
16561660
)),
1661+
EnumSource::OnConflictTarget(table) => Some(format!(
1662+
"{}OnConflictConstraint",
1663+
self.schema.graphql_table_base_type_name(&table)
1664+
)),
16571665
}
16581666
}
16591667

@@ -1703,6 +1711,18 @@ impl ___Type for EnumType {
17031711
deprecation_reason: None,
17041712
})
17051713
.collect(),
1714+
EnumSource::OnConflictTarget(table) => {
1715+
table
1716+
.on_conflict_indexes()
1717+
.iter()
1718+
.map(|ix| __EnumValue {
1719+
// TODO, apply name restrictions
1720+
name: ix.name.clone(),
1721+
description: None,
1722+
deprecation_reason: None,
1723+
})
1724+
.collect()
1725+
}
17061726
})
17071727
}
17081728
}
@@ -3159,13 +3179,9 @@ impl ___Type for InsertOnConflictType {
31593179
// If triggers are involved, we can't detect if a field is non-null. Default
31603180
// all fields to non-null and let postgres errors handle it.
31613181
type_: __Type::NonNull(NonNullType {
3162-
type_: Box::new(__Type::List(ListType {
3163-
type_: Box::new(__Type::NonNull(NonNullType {
3164-
type_: Box::new(__Type::Enum(EnumType {
3165-
enum_: EnumSource::TableColumns(Arc::clone(&self.table)),
3166-
schema: Arc::clone(&self.schema),
3167-
})),
3168-
})),
3182+
type_: Box::new(__Type::Enum(EnumType {
3183+
enum_: EnumSource::OnConflictTarget(Arc::clone(&self.table)),
3184+
schema: Arc::clone(&self.schema),
31693185
})),
31703186
}),
31713187
description: Some(
@@ -4141,15 +4157,22 @@ impl __Schema {
41414157
table: Arc::clone(table),
41424158
schema: Arc::clone(&schema_rc),
41434159
}));
4144-
// Used by on conflict
4145-
types_.push(__Type::Enum(EnumType {
4146-
enum_: EnumSource::TableColumns(Arc::clone(table)),
4147-
schema: Arc::clone(&schema_rc),
4148-
}));
4149-
types_.push(__Type::InsertOnConflictInput(InsertOnConflictType {
4150-
table: Arc::clone(table),
4151-
schema: Arc::clone(&schema_rc),
4152-
}));
4160+
4161+
// Used exclusively by onConflict
4162+
if table.has_upsert_support() {
4163+
types_.push(__Type::InsertOnConflictInput(InsertOnConflictType {
4164+
table: Arc::clone(table),
4165+
schema: Arc::clone(&schema_rc),
4166+
}));
4167+
types_.push(__Type::Enum(EnumType {
4168+
enum_: EnumSource::TableColumns(Arc::clone(table)),
4169+
schema: Arc::clone(&schema_rc),
4170+
}));
4171+
types_.push(__Type::Enum(EnumType {
4172+
enum_: EnumSource::OnConflictTarget(Arc::clone(table)),
4173+
schema: Arc::clone(&schema_rc),
4174+
}));
4175+
}
41534176
}
41544177

41554178
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
@@ -531,14 +531,39 @@ impl Table {
531531
column_names: column_names.clone(),
532532
is_unique: true,
533533
is_primary_key: true,
534-
name: "dummy".to_string(),
534+
name: "NOT REQUIRED".to_string(),
535535
})
536536
}
537537
} else {
538538
None
539539
}
540540
}
541541

542+
pub fn on_conflict_indexes(&self) -> Vec<&Index> {
543+
// Indexes that are valid targets for an on conflict clause
544+
// must be unique, real (not comment directives), and must
545+
// not contain serial or generated columns because we don't
546+
// allow those to be set in insert statements
547+
let unique_indexes = self.indexes.iter().filter(|x| x.is_unique);
548+
549+
let allowed_column_names = self
550+
.columns
551+
.iter()
552+
.filter(|x| x.permissions.is_insertable)
553+
.filter(|x| !x.is_generated)
554+
.filter(|x| !x.is_serial)
555+
.map(|x| &x.name)
556+
.collect::<HashSet<&String>>();
557+
558+
unique_indexes
559+
.filter(|uix| {
560+
uix.column_names
561+
.iter()
562+
.all(|col_name| allowed_column_names.contains(col_name))
563+
})
564+
.collect::<Vec<&Index>>()
565+
}
566+
542567
pub fn primary_key_columns(&self) -> Vec<&Arc<Column>> {
543568
self.primary_key()
544569
.map(|x| x.column_names)
@@ -553,6 +578,10 @@ impl Table {
553578
.collect::<Vec<&Arc<Column>>>()
554579
}
555580

581+
pub fn has_upsert_support(&self) -> bool {
582+
self.on_conflict_indexes().len() > 0
583+
}
584+
556585
pub fn is_any_column_selectable(&self) -> bool {
557586
self.columns.iter().any(|x| x.permissions.is_selectable)
558587
}

0 commit comments

Comments
 (0)