Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions .changepacks/changepack_log_1gif03GIm-FpqHt2Zs8Or.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"changes":{"crates/vespertide-planner/Cargo.toml":"Patch","crates/vespertide-query/Cargo.toml":"Patch","crates/vespertide-loader/Cargo.toml":"Patch","crates/vespertide-core/Cargo.toml":"Patch","crates/vespertide-exporter/Cargo.toml":"Patch","crates/vespertide-naming/Cargo.toml":"Patch","crates/vespertide-cli/Cargo.toml":"Patch","crates/vespertide/Cargo.toml":"Patch","crates/vespertide-config/Cargo.toml":"Patch","crates/vespertide-macro/Cargo.toml":"Patch"},"note":"Apply escaped into default_value","date":"2026-02-13T14:28:40.532707200Z"}
20 changes: 10 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 31 additions & 2 deletions crates/vespertide-exporter/src/seaorm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,9 @@ fn format_default_value(value: &StringOrBool, column_type: &ColumnType) -> Strin
trimmed
};

// Escape double quotes for embedding in Rust attribute strings
let escaped = cleaned.replace('"', "\\\"");

// Format based on column type
match column_type {
// Numeric types: no quotes
Expand All @@ -320,7 +323,7 @@ fn format_default_value(value: &StringOrBool, column_type: &ColumnType) -> Strin
match values {
EnumValues::String(_) => {
// String enum: use the string value as-is with quotes
format!("default_value = \"{}\"", cleaned)
format!("default_value = \"{}\"", escaped)
}
EnumValues::Integer(int_values) => {
// Integer enum: can be either a number or a variant name
Expand All @@ -342,7 +345,7 @@ fn format_default_value(value: &StringOrBool, column_type: &ColumnType) -> Strin
}
// All other types: use quotes
_ => {
format!("default_value = \"{}\"", cleaned)
format!("default_value = \"{}\"", escaped)
}
}
}
Expand Down Expand Up @@ -3376,4 +3379,30 @@ mod tests {
// Should have original table name without prefix
assert!(result.contains("#[sea_orm(table_name = \"users\")]"));
}

#[test]
fn test_json_default_value_escapes_double_quotes() {
let table = TableDef {
name: "configs".into(),
description: None,
columns: vec![ColumnDef {
name: "data".into(),
r#type: ColumnType::Simple(SimpleColumnType::Json),
nullable: false,
default: Some(r#"{"hello": "world"}"#.into()),
comment: None,
primary_key: None,
unique: None,
index: None,
foreign_key: None,
}],
constraints: vec![],
};
let rendered = render_entity(&table);
assert!(
rendered.contains(r#"default_value = "{\"hello\": \"world\"}"#),
"Expected escaped quotes in default_value, got: {}",
rendered
);
}
}
39 changes: 37 additions & 2 deletions crates/vespertide-exporter/src/sqlalchemy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,13 +435,15 @@ fn render_column(
// Default value
if let Some(ref default) = col.default {
let default_str = default.to_sql();
// Escape double quotes for embedding in Python strings
let escaped = default_str.replace('"', "\\\"");
// Check if it's a function call or literal
if default_str.contains('(') {
attrs.push(format!("server_default=text(\"{}\")", default_str));
attrs.push(format!("server_default=text(\"{}\")", escaped));
} else if default_str.starts_with('\'') || default_str.starts_with('"') {
attrs.push(format!("server_default={}", default_str));
} else {
attrs.push(format!("server_default=\"{}\"", default_str));
attrs.push(format!("server_default=\"{}\"", escaped));
}
}

Expand Down Expand Up @@ -1484,4 +1486,37 @@ mod tests {
used2.add_column_type(&ColumnType::Simple(SimpleColumnType::Integer), true);
assert!(used2.needs_optional);
}

#[test]
fn test_json_default_value_escapes_double_quotes() {
let table = TableDef {
name: "configs".into(),
description: None,
columns: vec![
col("id", ColumnType::Simple(SimpleColumnType::Integer)),
ColumnDef {
name: "data".into(),
r#type: ColumnType::Simple(SimpleColumnType::Json),
nullable: false,
default: Some(r#"{"hello": "world"}"#.into()),
comment: None,
primary_key: None,
unique: None,
index: None,
foreign_key: None,
},
],
constraints: vec![TableConstraint::PrimaryKey {
auto_increment: false,
columns: vec!["id".into()],
}],
};

let result = render_entity(&table).unwrap();
assert!(
result.contains(r#"server_default="{\"hello\": \"world\"}"#),
"Expected escaped quotes in server_default, got: {}",
result
);
}
}
42 changes: 39 additions & 3 deletions crates/vespertide-exporter/src/sqlmodel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,11 +374,13 @@ fn render_column(
// Default value handling
if let Some(ref default) = col.default {
let default_str = default.to_sql();
// Escape double quotes for embedding in Python strings
let escaped = default_str.replace('"', "\\\"");
// For server-side defaults, use sa_column_kwargs
if default_str.contains('(') {
field_args.push(format!(
"sa_column_kwargs={{\"server_default\": text(\"{}\")}}",
default_str
escaped
));
} else if default_str == "true" {
field_args.push("default=True".into());
Expand All @@ -387,14 +389,15 @@ fn render_column(
} else if default_str.starts_with('\'') || default_str.starts_with('"') {
// String literal - strip quotes for Python
let stripped = default_str.trim_matches(|c| c == '\'' || c == '"');
field_args.push(format!("default=\"{}\"", stripped));
let stripped_escaped = stripped.replace('"', "\\\"");
field_args.push(format!("default=\"{}\"", stripped_escaped));
} else if default_str.parse::<f64>().is_ok() {
field_args.push(format!("default={}", default_str));
} else {
// Assume it's a server default
field_args.push(format!(
"sa_column_kwargs={{\"server_default\": text(\"{}\")}}",
default_str
escaped
));
}
} else if col.nullable {
Expand Down Expand Up @@ -1343,4 +1346,37 @@ mod tests {
used.add_column_type(&ColumnType::Simple(SimpleColumnType::Integer), true);
assert!(used.needs_optional);
}

#[test]
fn test_json_default_value_escapes_double_quotes() {
let table = TableDef {
name: "configs".into(),
description: None,
columns: vec![
col("id", ColumnType::Simple(SimpleColumnType::Integer)),
ColumnDef {
name: "data".into(),
r#type: ColumnType::Simple(SimpleColumnType::Json),
nullable: false,
default: Some(r#"{"hello": "world"}"#.into()),
comment: None,
primary_key: None,
unique: None,
index: None,
foreign_key: None,
},
],
constraints: vec![TableConstraint::PrimaryKey {
auto_increment: false,
columns: vec!["id".into()],
}],
};

let result = render_entity(&table).unwrap();
assert!(
result.contains(r#"server_default": text("{\"hello\": \"world\"}"#),
"Expected escaped quotes in server_default, got: {}",
result
);
}
}