Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ async fn multiple_changed_relation_names_due_to_mapped_models(api: &mut TestApi)
t.add_column("id", types::primary());
t.add_column("user_id", types::integer().nullable(false).unique(true));
t.add_column("user_id2", types::integer().nullable(false).unique(true));
t.add_column(
"createdAt",
types::custom("INTEGER NOT NULL DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER))"),
);

t.add_foreign_key(&["user_id"], "User", &["id"]);
t.add_foreign_key(&["user_id2"], "User", &["id"]);
Expand All @@ -29,6 +33,7 @@ async fn multiple_changed_relation_names_due_to_mapped_models(api: &mut TestApi)
id Int @id @default(autoincrement())
user_id Int @unique
user_id2 Int @unique
createdAt Int @default(dbgenerated("(CAST(unixepoch('subsec') * 1000 AS INTEGER))"))
custom_User Custom_User @relation("CustomRelationName", fields: [user_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
custom_User2 Custom_User @relation("AnotherCustomRelationName", fields: [user_id2], references: [id], onDelete: NoAction, onUpdate: NoAction)
}
Expand All @@ -47,6 +52,7 @@ async fn multiple_changed_relation_names_due_to_mapped_models(api: &mut TestApi)
id Int @id @default(autoincrement())
user_id Int @unique(map: "sqlite_autoindex_Post_1")
user_id2 Int @unique(map: "sqlite_autoindex_Post_2")
createdAt Int @default(dbgenerated("(CAST(unixepoch('subsec') * 1000 AS INTEGER))"))
custom_User2 Custom_User @relation("AnotherCustomRelationName", fields: [user_id2], references: [id], onDelete: NoAction, onUpdate: NoAction)
custom_User Custom_User @relation("CustomRelationName", fields: [user_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
}
Expand Down
24 changes: 18 additions & 6 deletions schema-engine/sql-schema-describer/src/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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[0..1] != *"(" && s[s.len() - 1..] == *")" {
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition logic is incorrect. This checks if the first character is NOT '(' AND the last character IS ')', which is the opposite of what should be checked. This will wrap strings that end with ')' but don't start with '(', and will not wrap strings that need wrapping.

The condition should be: !s.starts_with('(') || !s.ends_with(')') to check if either parenthesis is missing, or more likely: !(s.starts_with('(') && s.ends_with(')')) to check if the string is not already fully wrapped.

Suggested change
if s[0..1] != *"(" && s[s.len() - 1..] == *")" {
if !(s.starts_with('(') && s.ends_with(')')) {

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ken0x0a indeed, the condition should probably be !(s.starts_with('(') && s.ends_with(')'))

format!("({})", s).into()
} else {
s.into()
}
}

async fn push_columns(
table_name: &str,
container_id: Either<TableId, ViewId>,
Expand Down Expand Up @@ -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 => {
Expand All @@ -335,7 +347,7 @@ 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),
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wrap_in_parentheses function is not applied to the Binary column type, but it is applied to other column types (Int, BigInt, Float, Decimal, Boolean, DateTime). Since the issue being fixed is that SQLite's PRAGMA table_info removes parentheses from default values for all types, this should likely be applied consistently to Binary as well for consistency, unless there's a specific reason Binary defaults don't need parentheses wrapping.

Suggested change
ColumnTypeFamily::Binary => DefaultValue::db_generated(default_string),
ColumnTypeFamily::Binary => DefaultValue::db_generated(wrap_in_parentheses(&default_string)),

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ken0x0a I think this comment is valid and the function should be applied for all types

Expand Down
Loading