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.


![image](https://github.com/user-attachments/assets/e01bccab-0aaa-40ab-b0bf-25e3c72aa037)

/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:
Darren Schroeder
2025-06-13 16:29:07 -05:00
committed by GitHub
parent cf1a53143c
commit f7888fce83
3 changed files with 63 additions and 35 deletions

View File

@ -112,16 +112,31 @@ impl SQLiteDatabase {
if self.path == PathBuf::from(MEMORY_DB) {
open_connection_in_memory_custom()
} 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(),
msg: e.to_string(),
span: None,
help: None,
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> {
let mut table_names =
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> {
let flags = OpenFlags::default();
let conn =
Connection::open_with_flags(MEMORY_DB, flags).map_err(|e| ShellError::GenericError {
error: "Failed to open SQLite custom connection in memory".into(),
msg: e.to_string(),
span: Some(Span::test_data()),
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> {

View File

@ -109,7 +109,9 @@ impl Command for StorDelete {
// dbg!(&sql_stmt);
conn.execute(&sql_stmt, [])
.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(),
span: Some(Span::test_data()),
help: None,

View File

@ -164,7 +164,6 @@ 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 column_placeholders: Vec<String> = Vec::new();
@ -189,9 +188,11 @@ fn process(
// Get the params from the passed values
let params = values_to_sql(record.values().cloned())?;
if let Ok(conn) = db.open_connection() {
conn.execute(&create_stmt, params_from_iter(params))
.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(),
span: Some(Span::test_data()),
help: None,