Fix improperly escaped strings in stor update (#14921)

# Description

Fixes #14909 with the same technique used in #12820 for `stor insert`.
Single quotes (and others) now work properly in strings passed to `stor
update`. Also did some minor refactoring on `stor insert` so it matches
the changes in `stor update`.

# User-Facing Changes

Bug-fix.

# Tests + Formatting

Test added for this scenario.

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting

N/A
This commit is contained in:
Douglas 2025-01-26 08:20:39 -05:00 committed by GitHub
parent a011791631
commit f88ed6ecd5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 59 additions and 45 deletions

View File

@ -161,27 +161,23 @@ fn process(
let new_table_name = table_name.unwrap_or("table".into());
if let Ok(conn) = db.open_connection() {
let mut create_stmt = format!("INSERT INTO {} ( ", new_table_name);
let mut create_stmt = format!("INSERT INTO {} (", new_table_name);
let mut column_placeholders: Vec<String> = Vec::new();
let cols = record.columns();
cols.for_each(|col| {
create_stmt.push_str(&format!("{}, ", col));
column_placeholders.push(col.to_string());
});
if create_stmt.ends_with(", ") {
create_stmt.pop();
create_stmt.pop();
}
create_stmt.push_str(&column_placeholders.join(", "));
// Values are set as placeholders.
create_stmt.push_str(") VALUES ( ");
create_stmt.push_str(") VALUES (");
let mut value_placeholders: Vec<String> = Vec::new();
for (index, _) in record.columns().enumerate() {
create_stmt.push_str(&format!("?{}, ", index + 1));
value_placeholders.push(format!("?{}", index + 1));
}
if create_stmt.ends_with(", ") {
create_stmt.pop();
create_stmt.pop();
}
create_stmt.push_str(&value_placeholders.join(", "));
create_stmt.push(')');
// dbg!(&create_stmt);

View File

@ -1,6 +1,7 @@
use crate::database::{SQLiteDatabase, MEMORY_DB};
use crate::database::{values_to_sql, SQLiteDatabase, MEMORY_DB};
use nu_engine::command_prelude::*;
use nu_protocol::Signals;
use rusqlite::params_from_iter;
#[derive(Clone)]
pub struct StorUpdate;
@ -163,36 +164,12 @@ fn process(
let mut update_stmt = format!("UPDATE {} ", new_table_name);
update_stmt.push_str("SET ");
let vals = record.iter();
vals.for_each(|(key, val)| match val {
Value::Int { val, .. } => {
update_stmt.push_str(&format!("{} = {}, ", key, val));
}
Value::Float { val, .. } => {
update_stmt.push_str(&format!("{} = {}, ", key, val));
}
Value::String { val, .. } => {
update_stmt.push_str(&format!("{} = '{}', ", key, val));
}
Value::Date { val, .. } => {
update_stmt.push_str(&format!("{} = '{}', ", key, val));
}
Value::Bool { val, .. } => {
update_stmt.push_str(&format!("{} = {}, ", key, val));
}
_ => {
// return Err(ShellError::UnsupportedInput {
// msg: format!("{} is not a valid datepart, expected one of year, month, day, hour, minute, second, millisecond, microsecond, nanosecond", part.item),
// input: "value originates from here".to_string(),
// msg_span: span,
// input_span: val.span(),
// });
}
});
if update_stmt.ends_with(", ") {
update_stmt.pop();
update_stmt.pop();
let mut placeholders: Vec<String> = Vec::new();
for (index, (key, _)) in record.iter().enumerate() {
placeholders.push(format!("{} = ?{}", key, index + 1));
}
update_stmt.push_str(&placeholders.join(", "));
// Yup, this is a bit janky, but I'm not sure a better way to do this without having
// --and and --or flags as well as supporting ==, !=, <>, is null, is not null, etc.
@ -202,7 +179,10 @@ fn process(
}
// dbg!(&update_stmt);
conn.execute(&update_stmt, [])
// Get the params from the passed values
let params = values_to_sql(record.values().cloned())?;
conn.execute(&update_stmt, params_from_iter(params))
.map_err(|err| ShellError::GenericError {
error: "Failed to open SQLite connection in memory from update".into(),
msg: err.to_string(),

View File

@ -105,6 +105,7 @@ mod sort_by;
mod source_env;
mod split_column;
mod split_row;
mod stor;
mod str_;
mod table;
mod take;

View File

@ -0,0 +1,37 @@
use nu_test_support::{nu, pipeline};
#[test]
fn stor_insert() {
let actual = nu!(pipeline(
r#"
stor create --table-name test_table --columns { id: int, value: str };
stor insert -t test_table --data-record {
id: 1
value: "'Initial value'"
};
stor open | query db 'select value from test_table' | get 0.value
"#
));
assert_eq!(actual.out, "'Initial value'");
}
#[test]
fn stor_update_with_quote() {
let actual = nu!(pipeline(
r#"
stor create --table-name test_table --columns { id: int, value: str };
stor insert -t test_table --data-record {
id: 1
value: "'Initial value'"
};
stor update -t test_table --where-clause 'id = 1' --update-record {
id: 1
value: "This didn't work, but should now."
};
stor open | query db 'select value from test_table' | get 0.value
"#
));
assert_eq!(actual.out, "This didn't work, but should now.");
}