mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 08:45:45 +02:00
fix stor
insert/delete collision (#15838)
# Description Based on some testing in [Discord](https://discord.com/channels/601130461678272522/1349836000804995196/1353138803640111135) we were able to find that `insert` and `delete` happening at the same time caused problems in the `stor` command. So, I added `conn.is_busy()` with a sleep to try and avoid that problem.  /cc @NotTheDr01ds @132ikl # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass (on Windows make sure to [enable developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging)) - `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. -->
This commit is contained in:
@ -112,16 +112,31 @@ impl SQLiteDatabase {
|
|||||||
if self.path == PathBuf::from(MEMORY_DB) {
|
if self.path == PathBuf::from(MEMORY_DB) {
|
||||||
open_connection_in_memory_custom()
|
open_connection_in_memory_custom()
|
||||||
} else {
|
} else {
|
||||||
Connection::open(&self.path).map_err(|e| ShellError::GenericError {
|
let conn = Connection::open(&self.path).map_err(|e| ShellError::GenericError {
|
||||||
error: "Failed to open SQLite database from open_connection".into(),
|
error: "Failed to open SQLite database from open_connection".into(),
|
||||||
msg: e.to_string(),
|
msg: e.to_string(),
|
||||||
span: None,
|
span: None,
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
})
|
})?;
|
||||||
|
conn.busy_handler(Some(SQLiteDatabase::sleeper))
|
||||||
|
.map_err(|e| ShellError::GenericError {
|
||||||
|
error: "Failed to set busy handler for SQLite database".into(),
|
||||||
|
msg: e.to_string(),
|
||||||
|
span: None,
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
})?;
|
||||||
|
Ok(conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sleeper(attempts: i32) -> bool {
|
||||||
|
log::warn!("SQLITE_BUSY, retrying after 250ms (attempt {})", attempts);
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(250));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_tables(&self, conn: &Connection) -> Result<Vec<DbTable>, SqliteError> {
|
pub fn get_tables(&self, conn: &Connection) -> Result<Vec<DbTable>, SqliteError> {
|
||||||
let mut table_names =
|
let mut table_names =
|
||||||
conn.prepare("SELECT name FROM sqlite_master WHERE type = 'table'")?;
|
conn.prepare("SELECT name FROM sqlite_master WHERE type = 'table'")?;
|
||||||
@ -668,13 +683,23 @@ pub fn convert_sqlite_value_to_nu_value(value: ValueRef, span: Span) -> Value {
|
|||||||
|
|
||||||
pub fn open_connection_in_memory_custom() -> Result<Connection, ShellError> {
|
pub fn open_connection_in_memory_custom() -> Result<Connection, ShellError> {
|
||||||
let flags = OpenFlags::default();
|
let flags = OpenFlags::default();
|
||||||
Connection::open_with_flags(MEMORY_DB, flags).map_err(|e| ShellError::GenericError {
|
let conn =
|
||||||
error: "Failed to open SQLite custom connection in memory".into(),
|
Connection::open_with_flags(MEMORY_DB, flags).map_err(|e| ShellError::GenericError {
|
||||||
msg: e.to_string(),
|
error: "Failed to open SQLite custom connection in memory".into(),
|
||||||
span: Some(Span::test_data()),
|
msg: e.to_string(),
|
||||||
help: None,
|
span: Some(Span::test_data()),
|
||||||
inner: vec![],
|
help: None,
|
||||||
})
|
inner: vec![],
|
||||||
|
})?;
|
||||||
|
conn.busy_handler(Some(SQLiteDatabase::sleeper))
|
||||||
|
.map_err(|e| ShellError::GenericError {
|
||||||
|
error: "Failed to set busy handler for SQLite custom connection in memory".into(),
|
||||||
|
msg: e.to_string(),
|
||||||
|
span: Some(Span::test_data()),
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
})?;
|
||||||
|
Ok(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_connection_in_memory() -> Result<Connection, ShellError> {
|
pub fn open_connection_in_memory() -> Result<Connection, ShellError> {
|
||||||
|
@ -109,7 +109,9 @@ impl Command for StorDelete {
|
|||||||
// dbg!(&sql_stmt);
|
// dbg!(&sql_stmt);
|
||||||
conn.execute(&sql_stmt, [])
|
conn.execute(&sql_stmt, [])
|
||||||
.map_err(|err| ShellError::GenericError {
|
.map_err(|err| ShellError::GenericError {
|
||||||
error: "Failed to open SQLite connection in memory from delete".into(),
|
error:
|
||||||
|
"Failed to delete using the SQLite connection in memory from delete.rs."
|
||||||
|
.into(),
|
||||||
msg: err.to_string(),
|
msg: err.to_string(),
|
||||||
span: Some(Span::test_data()),
|
span: Some(Span::test_data()),
|
||||||
help: None,
|
help: None,
|
||||||
|
@ -164,34 +164,35 @@ fn process(
|
|||||||
}
|
}
|
||||||
let new_table_name = table_name.unwrap_or("table".into());
|
let new_table_name = table_name.unwrap_or("table".into());
|
||||||
|
|
||||||
|
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| {
|
||||||
|
column_placeholders.push(col.to_string());
|
||||||
|
});
|
||||||
|
|
||||||
|
create_stmt.push_str(&column_placeholders.join(", "));
|
||||||
|
|
||||||
|
// Values are set as placeholders.
|
||||||
|
create_stmt.push_str(") VALUES (");
|
||||||
|
let mut value_placeholders: Vec<String> = Vec::new();
|
||||||
|
for (index, _) in record.columns().enumerate() {
|
||||||
|
value_placeholders.push(format!("?{}", index + 1));
|
||||||
|
}
|
||||||
|
create_stmt.push_str(&value_placeholders.join(", "));
|
||||||
|
create_stmt.push(')');
|
||||||
|
|
||||||
|
// dbg!(&create_stmt);
|
||||||
|
|
||||||
|
// Get the params from the passed values
|
||||||
|
let params = values_to_sql(record.values().cloned())?;
|
||||||
|
|
||||||
if let Ok(conn) = db.open_connection() {
|
if let Ok(conn) = db.open_connection() {
|
||||||
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| {
|
|
||||||
column_placeholders.push(col.to_string());
|
|
||||||
});
|
|
||||||
|
|
||||||
create_stmt.push_str(&column_placeholders.join(", "));
|
|
||||||
|
|
||||||
// Values are set as placeholders.
|
|
||||||
create_stmt.push_str(") VALUES (");
|
|
||||||
let mut value_placeholders: Vec<String> = Vec::new();
|
|
||||||
for (index, _) in record.columns().enumerate() {
|
|
||||||
value_placeholders.push(format!("?{}", index + 1));
|
|
||||||
}
|
|
||||||
create_stmt.push_str(&value_placeholders.join(", "));
|
|
||||||
create_stmt.push(')');
|
|
||||||
|
|
||||||
// dbg!(&create_stmt);
|
|
||||||
|
|
||||||
// Get the params from the passed values
|
|
||||||
let params = values_to_sql(record.values().cloned())?;
|
|
||||||
|
|
||||||
conn.execute(&create_stmt, params_from_iter(params))
|
conn.execute(&create_stmt, params_from_iter(params))
|
||||||
.map_err(|err| ShellError::GenericError {
|
.map_err(|err| ShellError::GenericError {
|
||||||
error: "Failed to open SQLite connection in memory from insert".into(),
|
error: "Failed to insert using the SQLite connection in memory from insert.rs."
|
||||||
|
.into(),
|
||||||
msg: err.to_string(),
|
msg: err.to_string(),
|
||||||
span: Some(Span::test_data()),
|
span: Some(Span::test_data()),
|
||||||
help: None,
|
help: None,
|
||||||
|
Reference in New Issue
Block a user