Skip to content

Commit e4b9f34

Browse files
authored
Merge pull request #109 from dev-five-git/apply-escaped
Apply escaped
2 parents a826d3f + 424e589 commit e4b9f34

5 files changed

Lines changed: 118 additions & 17 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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"}

Cargo.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vespertide-exporter/src/seaorm/mod.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ fn format_default_value(value: &StringOrBool, column_type: &ColumnType) -> Strin
301301
trimmed
302302
};
303303

304+
// Escape double quotes for embedding in Rust attribute strings
305+
let escaped = cleaned.replace('"', "\\\"");
306+
304307
// Format based on column type
305308
match column_type {
306309
// Numeric types: no quotes
@@ -320,7 +323,7 @@ fn format_default_value(value: &StringOrBool, column_type: &ColumnType) -> Strin
320323
match values {
321324
EnumValues::String(_) => {
322325
// String enum: use the string value as-is with quotes
323-
format!("default_value = \"{}\"", cleaned)
326+
format!("default_value = \"{}\"", escaped)
324327
}
325328
EnumValues::Integer(int_values) => {
326329
// Integer enum: can be either a number or a variant name
@@ -342,7 +345,7 @@ fn format_default_value(value: &StringOrBool, column_type: &ColumnType) -> Strin
342345
}
343346
// All other types: use quotes
344347
_ => {
345-
format!("default_value = \"{}\"", cleaned)
348+
format!("default_value = \"{}\"", escaped)
346349
}
347350
}
348351
}
@@ -3376,4 +3379,30 @@ mod tests {
33763379
// Should have original table name without prefix
33773380
assert!(result.contains("#[sea_orm(table_name = \"users\")]"));
33783381
}
3382+
3383+
#[test]
3384+
fn test_json_default_value_escapes_double_quotes() {
3385+
let table = TableDef {
3386+
name: "configs".into(),
3387+
description: None,
3388+
columns: vec![ColumnDef {
3389+
name: "data".into(),
3390+
r#type: ColumnType::Simple(SimpleColumnType::Json),
3391+
nullable: false,
3392+
default: Some(r#"{"hello": "world"}"#.into()),
3393+
comment: None,
3394+
primary_key: None,
3395+
unique: None,
3396+
index: None,
3397+
foreign_key: None,
3398+
}],
3399+
constraints: vec![],
3400+
};
3401+
let rendered = render_entity(&table);
3402+
assert!(
3403+
rendered.contains(r#"default_value = "{\"hello\": \"world\"}"#),
3404+
"Expected escaped quotes in default_value, got: {}",
3405+
rendered
3406+
);
3407+
}
33793408
}

crates/vespertide-exporter/src/sqlalchemy/mod.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,13 +435,15 @@ fn render_column(
435435
// Default value
436436
if let Some(ref default) = col.default {
437437
let default_str = default.to_sql();
438+
// Escape double quotes for embedding in Python strings
439+
let escaped = default_str.replace('"', "\\\"");
438440
// Check if it's a function call or literal
439441
if default_str.contains('(') {
440-
attrs.push(format!("server_default=text(\"{}\")", default_str));
442+
attrs.push(format!("server_default=text(\"{}\")", escaped));
441443
} else if default_str.starts_with('\'') || default_str.starts_with('"') {
442444
attrs.push(format!("server_default={}", default_str));
443445
} else {
444-
attrs.push(format!("server_default=\"{}\"", default_str));
446+
attrs.push(format!("server_default=\"{}\"", escaped));
445447
}
446448
}
447449

@@ -1484,4 +1486,37 @@ mod tests {
14841486
used2.add_column_type(&ColumnType::Simple(SimpleColumnType::Integer), true);
14851487
assert!(used2.needs_optional);
14861488
}
1489+
1490+
#[test]
1491+
fn test_json_default_value_escapes_double_quotes() {
1492+
let table = TableDef {
1493+
name: "configs".into(),
1494+
description: None,
1495+
columns: vec![
1496+
col("id", ColumnType::Simple(SimpleColumnType::Integer)),
1497+
ColumnDef {
1498+
name: "data".into(),
1499+
r#type: ColumnType::Simple(SimpleColumnType::Json),
1500+
nullable: false,
1501+
default: Some(r#"{"hello": "world"}"#.into()),
1502+
comment: None,
1503+
primary_key: None,
1504+
unique: None,
1505+
index: None,
1506+
foreign_key: None,
1507+
},
1508+
],
1509+
constraints: vec![TableConstraint::PrimaryKey {
1510+
auto_increment: false,
1511+
columns: vec!["id".into()],
1512+
}],
1513+
};
1514+
1515+
let result = render_entity(&table).unwrap();
1516+
assert!(
1517+
result.contains(r#"server_default="{\"hello\": \"world\"}"#),
1518+
"Expected escaped quotes in server_default, got: {}",
1519+
result
1520+
);
1521+
}
14871522
}

crates/vespertide-exporter/src/sqlmodel/mod.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,11 +374,13 @@ fn render_column(
374374
// Default value handling
375375
if let Some(ref default) = col.default {
376376
let default_str = default.to_sql();
377+
// Escape double quotes for embedding in Python strings
378+
let escaped = default_str.replace('"', "\\\"");
377379
// For server-side defaults, use sa_column_kwargs
378380
if default_str.contains('(') {
379381
field_args.push(format!(
380382
"sa_column_kwargs={{\"server_default\": text(\"{}\")}}",
381-
default_str
383+
escaped
382384
));
383385
} else if default_str == "true" {
384386
field_args.push("default=True".into());
@@ -387,14 +389,15 @@ fn render_column(
387389
} else if default_str.starts_with('\'') || default_str.starts_with('"') {
388390
// String literal - strip quotes for Python
389391
let stripped = default_str.trim_matches(|c| c == '\'' || c == '"');
390-
field_args.push(format!("default=\"{}\"", stripped));
392+
let stripped_escaped = stripped.replace('"', "\\\"");
393+
field_args.push(format!("default=\"{}\"", stripped_escaped));
391394
} else if default_str.parse::<f64>().is_ok() {
392395
field_args.push(format!("default={}", default_str));
393396
} else {
394397
// Assume it's a server default
395398
field_args.push(format!(
396399
"sa_column_kwargs={{\"server_default\": text(\"{}\")}}",
397-
default_str
400+
escaped
398401
));
399402
}
400403
} else if col.nullable {
@@ -1343,4 +1346,37 @@ mod tests {
13431346
used.add_column_type(&ColumnType::Simple(SimpleColumnType::Integer), true);
13441347
assert!(used.needs_optional);
13451348
}
1349+
1350+
#[test]
1351+
fn test_json_default_value_escapes_double_quotes() {
1352+
let table = TableDef {
1353+
name: "configs".into(),
1354+
description: None,
1355+
columns: vec![
1356+
col("id", ColumnType::Simple(SimpleColumnType::Integer)),
1357+
ColumnDef {
1358+
name: "data".into(),
1359+
r#type: ColumnType::Simple(SimpleColumnType::Json),
1360+
nullable: false,
1361+
default: Some(r#"{"hello": "world"}"#.into()),
1362+
comment: None,
1363+
primary_key: None,
1364+
unique: None,
1365+
index: None,
1366+
foreign_key: None,
1367+
},
1368+
],
1369+
constraints: vec![TableConstraint::PrimaryKey {
1370+
auto_increment: false,
1371+
columns: vec!["id".into()],
1372+
}],
1373+
};
1374+
1375+
let result = render_entity(&table).unwrap();
1376+
assert!(
1377+
result.contains(r#"server_default": text("{\"hello\": \"world\"}"#),
1378+
"Expected escaped quotes in server_default, got: {}",
1379+
result
1380+
);
1381+
}
13461382
}

0 commit comments

Comments
 (0)