Skip to content

Commit 305d4f2

Browse files
luis-dfnsCopilotjacek-prisma
authored andcommitted
fix(postgresql): parsing bug with expression based unique indexes on postgres (prisma#5726)
Fixes a bug where the unique key violation parsing incorrectly returns extra "comma" characters for expression based unique indexes such as: ``` CREATE UNIQUE INDEX "unique_idx" ON "table_name"( "column_1", (column_2->>'field'), ); ``` In which case the columns affected would previously be returned as `["column_1,"]` instead of `["column_1"]` --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: jacek-prisma <malec@prisma.io>
1 parent 4186f91 commit 305d4f2

2 files changed

Lines changed: 34 additions & 1 deletion

File tree

quaint/src/connector/postgres/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl From<PostgresError> for Error {
5353
.detail
5454
.as_ref()
5555
.and_then(|d| d.split(")=(").next())
56-
.and_then(|d| d.split(" (").nth(1).map(|s| s.replace('\"', "")))
56+
.and_then(|d| d.split_once(" (").map(|(_, rest)| rest.replace('"', "")))
5757
.map(|s| DatabaseConstraint::fields(s.split(", ")))
5858
.unwrap_or(DatabaseConstraint::CannotParse);
5959

quaint/src/tests/query/error.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,39 @@ async fn unique_constraint_violation(api: &mut dyn TestApi) -> crate::Result<()>
112112
Ok(())
113113
}
114114

115+
#[test_each_connector(tags("postgresql"))]
116+
async fn expression_based_unique_index(api: &mut dyn TestApi) -> crate::Result<()> {
117+
let table = api.create_temp_table("id1 int, json_data JSONB").await?;
118+
let index = api.create_index(&table, "id1, (json_data->>'field')").await?;
119+
120+
let insert = Insert::single_into(&table)
121+
.value("id1", 1)
122+
.value("json_data", Value::json(serde_json::json!({"field": "a"})));
123+
api.conn().insert(insert.clone().into()).await?;
124+
125+
let res = api.conn().insert(insert.clone().into()).await;
126+
127+
assert!(res.is_err());
128+
129+
let err = res.unwrap_err();
130+
131+
match &err.kind() {
132+
ErrorKind::UniqueConstraintViolation { constraint } => match constraint {
133+
DatabaseConstraint::Index(idx) => assert_eq!(&index, idx),
134+
DatabaseConstraint::Fields(fields) => {
135+
let fields = fields.iter().map(|s| s.as_str()).collect::<Vec<_>>();
136+
// PostgreSQL normalizes the expression: json_data->>'field' becomes (json_data ->> 'field'::text)
137+
assert_eq!(vec!["id1", "(json_data ->> 'field'::text)"], fields)
138+
}
139+
DatabaseConstraint::ForeignKey => panic!("Expecting index or field constraints."),
140+
DatabaseConstraint::CannotParse => panic!("Couldn't parse the error message."),
141+
},
142+
_ => panic!("{}", err),
143+
}
144+
145+
Ok(())
146+
}
147+
115148
#[test_each_connector]
116149
async fn null_constraint_violation(api: &mut dyn TestApi) -> crate::Result<()> {
117150
let table = api.create_temp_table("id1 int not null, id2 int not null").await?;

0 commit comments

Comments
 (0)