-
Notifications
You must be signed in to change notification settings - Fork 296
fix: prevent breaking default expr in SQLite #5748
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
base: main
Are you sure you want to change the base?
Changes from 3 commits
3088ecd
5d7398d
a4dbe4e
447aaff
ee8ace3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -270,6 +270,18 @@ impl<'a> SqlSchemaDescriber<'a> { | |
| } | ||
| } | ||
|
|
||
| /// Wraps the given string in parentheses if it is not already. | ||
| /// The result of `PRAGMA table_info` removes parentheses from default values, | ||
| /// so we need to add parentheses when generating prisma schema. | ||
| /// See <https://github.com/prisma/prisma/issues/29064> | ||
| fn wrap_in_parentheses(s: &str) -> Cow<'_, str> { | ||
| if !s.starts_with('(') && s.ends_with(')') { | ||
|
||
| format!("({})", s).into() | ||
| } else { | ||
| s.into() | ||
| } | ||
| } | ||
|
Comment on lines
+273
to
+283
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Edge case: The check In practice this may be unlikely for SQLite defaults reaching the 🛡️ Proposed robust implementation fn wrap_in_parentheses(s: &str) -> Cow<'_, str> {
- if !(s.starts_with('(') && s.ends_with(')')) {
+ if !is_wrapped_in_parentheses(s) {
format!("({})", s).into()
} else {
s.into()
}
}
+
+/// Returns true only if the outermost characters are a matched pair of parentheses
+/// enclosing the entire expression.
+fn is_wrapped_in_parentheses(s: &str) -> bool {
+ if !(s.starts_with('(') && s.ends_with(')')) {
+ return false;
+ }
+ let mut depth = 0i32;
+ for (i, ch) in s.char_indices() {
+ match ch {
+ '(' => depth += 1,
+ ')' => {
+ depth -= 1;
+ // If depth hits 0 before the last character, the outer parens aren't wrapping the whole thing.
+ if depth == 0 && i < s.len() - 1 {
+ return false;
+ }
+ }
+ _ => {}
+ }
+ }
+ depth == 0
+}
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This appears a legitimate issue with this code There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Would you like me to implement the more robust version that properly validates balanced parentheses? I can create the fix following the approach suggested in the review comment. Task: Fix wrap_in_parentheses edge case
💡 Use |
||
|
|
||
| async fn push_columns( | ||
| table_name: &str, | ||
| container_id: Either<TableId, ViewId>, | ||
|
|
@@ -306,26 +318,26 @@ async fn push_columns( | |
| Some(match &tpe.family { | ||
| ColumnTypeFamily::Int => match SqlSchemaDescriber::parse_int(&default_string) { | ||
| Some(int_value) => DefaultValue::value(int_value), | ||
| None => DefaultValue::db_generated(default_string), | ||
| None => DefaultValue::db_generated(wrap_in_parentheses(&default_string)), | ||
| }, | ||
| ColumnTypeFamily::BigInt => match SqlSchemaDescriber::parse_big_int(&default_string) { | ||
| Some(int_value) => DefaultValue::value(int_value), | ||
| None => DefaultValue::db_generated(default_string), | ||
| None => DefaultValue::db_generated(wrap_in_parentheses(&default_string)), | ||
| }, | ||
| ColumnTypeFamily::Float => match SqlSchemaDescriber::parse_float(&default_string) { | ||
| Some(float_value) => DefaultValue::value(float_value), | ||
| None => DefaultValue::db_generated(default_string), | ||
| None => DefaultValue::db_generated(wrap_in_parentheses(&default_string)), | ||
| }, | ||
| ColumnTypeFamily::Decimal => match SqlSchemaDescriber::parse_float(&default_string) { | ||
| Some(float_value) => DefaultValue::value(float_value), | ||
| None => DefaultValue::db_generated(default_string), | ||
| None => DefaultValue::db_generated(wrap_in_parentheses(&default_string)), | ||
| }, | ||
| ColumnTypeFamily::Boolean => match SqlSchemaDescriber::parse_int(&default_string) { | ||
| Some(PrismaValue::Int(1)) => DefaultValue::value(true), | ||
| Some(PrismaValue::Int(0)) => DefaultValue::value(false), | ||
| _ => match SqlSchemaDescriber::parse_bool(&default_string) { | ||
| Some(bool_value) => DefaultValue::value(bool_value), | ||
| None => DefaultValue::db_generated(default_string), | ||
| None => DefaultValue::db_generated(wrap_in_parentheses(&default_string)), | ||
| }, | ||
| }, | ||
| ColumnTypeFamily::String => { | ||
|
|
@@ -335,14 +347,14 @@ async fn push_columns( | |
| "current_timestamp" | "datetime(\'now\')" | "datetime(\'now\', \'localtime\')" => { | ||
| DefaultValue::now() | ||
| } | ||
| _ => DefaultValue::db_generated(default_string), | ||
| _ => DefaultValue::db_generated(wrap_in_parentheses(&default_string)), | ||
| }, | ||
| ColumnTypeFamily::Json => DefaultValue::value(default_string), | ||
| ColumnTypeFamily::Binary => DefaultValue::db_generated(default_string), | ||
| ColumnTypeFamily::Uuid => DefaultValue::db_generated(default_string), | ||
| ColumnTypeFamily::Binary => DefaultValue::db_generated(wrap_in_parentheses(&default_string)), | ||
| ColumnTypeFamily::Uuid => DefaultValue::db_generated(wrap_in_parentheses(&default_string)), | ||
| ColumnTypeFamily::Enum(_) => DefaultValue::value(PrismaValue::Enum(default_string)), | ||
| ColumnTypeFamily::Udt(_) | ColumnTypeFamily::Unsupported(_) => { | ||
| DefaultValue::db_generated(default_string) | ||
| DefaultValue::db_generated(wrap_in_parentheses(&default_string)) | ||
| } | ||
| }) | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Consider adding a dedicated test for the default expression fix instead of modifying an unrelated test.
This test is named
multiple_changed_relation_names_due_to_mapped_modelsand focuses on relation renaming. AddingcreatedAtwith a complex default expression here conflates two unrelated concerns. A standalone test specifically for the SQLite default expression parenthesization fix would:Also, please ensure the
expect!snapshot was regenerated viaUPDATE_EXPECT=1 cargo test. As per coding guidelines, "When updating PSL diagnostics or error messages, always regenerateexpect!snapshot tests usingUPDATE_EXPECT=1 cargo testto ensure test assertions match new output."Also applies to: 36-36, 55-55
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is valid, it might make sense to introduce a similar test that just covers this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jacek-prisma, thank you for confirming! I'd be happy to help create a dedicated test that specifically covers the SQLite default expression parenthesization fix. This will make the regression coverage clear and independent of the relation-renaming test.Would you like me to create a new test function that:
unixepochexample)Task: Add dedicated test for SQLite default expression parenthesization
sqlite_default_expression_with_parenthesesin the same file💡 Use
@coderabbitaiplan to create a plan for code changes