{"record":{"id":"a9c96ad7ce510aff","repo":"t8y2/dbx","slug":"sql-is-empty","errorCode":null,"errorMessage":"SQL is empty","messagePattern":"SQL is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/dbx-sqlite-worker/src/runtime.rs","lineNumber":86,"sourceCode":"    if path.trim().is_empty() {\n        return Err(\"SQLite path is empty\".to_string());\n    }\n    if path.contains('\\0') {\n        return Err(\"SQLite path contains NUL\".to_string());\n    }\n    let flags = OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_URI;\n    if !Path::new(path).is_file() {\n        return Err(format!(\"File does not exist: {path}\"));\n    }\n    let conn = Connection::open_with_flags(path, flags).map_err(|e| format!(\"failed to open SQLite file: {e}\"))?;\n    conn.busy_timeout(Duration::from_secs(10)).map_err(|e| e.to_string())?;\n    Ok(conn)\n}\n\nfn query(conn: &Connection, sql: &str, max_rows: usize) -> WorkerBody {\n    let trimmed = sql.trim();\n    if trimmed.is_empty() {\n        return WorkerBody::err(\"SQL is empty\");\n    }\n    if sqlite_statement_returns_rows(trimmed) {\n        query_statement(conn, trimmed, max_rows)\n    } else {\n        match conn.execute_batch(trimmed) {\n            Ok(()) => WorkerBody::query(Vec::new(), Vec::new(), Vec::new(), conn.changes(), false),\n            Err(error) => WorkerBody::err(error.to_string()),\n        }\n    }\n}\n\nfn sqlite_statement_returns_rows(sql: &str) -> bool {\n    if sqlite_starts_with_keyword(sql, &[\"SELECT\", \"PRAGMA\", \"EXPLAIN\", \"WITH\"]) {\n        return true;\n    }\n    let Ok(statements) = Parser::parse_sql(&SQLiteDialect {}, sql) else {\n        return false;\n    };","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/crates/dbx-sqlite-worker/src/runtime.rs#L68-L104","documentation":"The worker's query() rejects SQL that is empty or whitespace-only after trimming, before attempting to parse or execute it. This avoids handing a blank statement to SQLite and gives a clear error instead of a parser complaint.","triggerScenarios":"Calling the Query op with sql = \"\", \"   \", or only newlines/comments after trim; a template interpolation that produced an empty string; UI sending an unsaved/blank editor buffer.","commonSituations":"Programmatic query builders emitting nothing when filters are empty; reading SQL from an empty file or unset env var; copy-paste losing the query text.","solutions":["Non-empty SQL before sending: check sql.trim().is_empty() on the caller side","Fix query-building logic that interpolates to an empty string","If reading SQL from a file/config, validate the content is loaded","Log or surface the input source so blank queries are caught in dev"],"exampleFix":"// before\nlet sql = std::env::var(\"SQL\").unwrap_or_default();\nworker.query(&sql)?; // \"\" -> SQL is empty\n// after\nlet sql = std::env::var(\"SQL\")?;\nassert!(!sql.trim().is_empty(), \"SQL must not be blank\");\nworker.query(&sql)?;","handlingStrategy":"validation","validationCode":"fn require_non_empty_sql(sql: &str) -> Result<&str, &'static str> {\n    let t = sql.trim();\n    if t.is_empty() { Err(\"SQL is empty\") } else { Ok(t) }\n}","typeGuard":"fn is_executable_sql(sql: &str) -> bool { !sql.trim().is_empty() }","tryCatchPattern":"match worker.send(WorkerOp::Query { sql: sql.to_string(), max_rows: None }).await {\n    Err(body) if body.error() == Some(\"SQL is empty\") => {\n        eprintln!(\"Query text was blank; check the query builder/input source\");\n    }\n    other => other?,\n}","preventionTips":["Trim and check SQL before sending","Guard query builders against producing empty strings when filters are absent","Validate file/config sources of SQL are non-empty","Surface blank-query errors in dev/tests early"],"tags":["sqlite","sql","validation","empty-input"],"backgroundTag":"empty-sql","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}