{"record":{"id":"275dde1cc7d127ce","repo":"t8y2/dbx","slug":"sql-is-required-275dde","errorCode":null,"errorMessage":"SQL is required","messagePattern":"SQL is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/tdengine/src/driver.rs","lineNumber":436,"sourceCode":"            affected_rows += i64::from(result_set.affected_rows());\n        }\n        if statements.iter().any(|statement| may_change_metadata(statement)) {\n            self.table_cache = None;\n        }\n        Ok(QueryResult {\n            columns: Vec::new(),\n            column_types: Vec::new(),\n            rows: Vec::new(),\n            affected_rows,\n            execution_time_ms: start.elapsed().as_millis() as i64,\n            truncated: false,\n        })\n    }\n\n    pub async fn get_explain_info(&self, sql: &str, token: &CancellationToken, timeout_secs: u64) -> Result<String> {\n        let sql = trim_sql(sql);\n        if sql.is_empty() {\n            bail!(\"SQL is required\");\n        }\n        let rows = self.query_rows(&format!(\"EXPLAIN {sql}\"), token, timeout_secs).await?;\n        Ok(rows\n            .into_iter()\n            .map(|row| row.iter().map(display_json_value).collect::<Vec<_>>().join(\"\\t\"))\n            .collect::<Vec<_>>()\n            .join(\"\\n\"))\n    }\n\n    async fn prepare_database(&mut self, options: &QueryOptions, token: &CancellationToken) -> Result<()> {\n        let database = if options.schema.trim().is_empty() { options.database.trim() } else { options.schema.trim() };\n        if database.is_empty() || database == self.current_database {\n            return Ok(());\n        }\n        self.use_database(database, token, options.timeout_secs).await\n    }\n\n    async fn use_database(&mut self, database: &str, token: &CancellationToken, timeout_secs: u64) -> Result<()> {","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/tdengine/src/driver.rs#L418-L454","documentation":"get_explain_info runs 'EXPLAIN <sql>' and requires non-empty SQL. trim_sql strips whitespace; if nothing remains, the driver rejects the call instead of sending a meaningless EXPLAIN statement to the server.","triggerScenarios":"Calling get_explain_info with an empty string or a string containing only whitespace/newlines.","commonSituations":"A UI sends the explain request before the user typed a query, or an upstream variable holding the SQL was never populated.","solutions":["Pass the actual SQL text to get_explain_info","Check sql.trim().is_empty() at the caller/API layer and return a 400-style validation error before invoking the driver"],"exampleFix":"// before\ndriver.get_explain_info(\"\", &token, 10).await?;\n// after\nif sql.trim().is_empty() { return Err(anyhow!(\"SQL is required\")); }\ndriver.get_explain_info(sql, &token, 10).await?;","handlingStrategy":"validation","validationCode":"anyhow::ensure!(!sql.trim().is_empty(), \"SQL is required before EXPLAIN\");","typeGuard":"fn has_sql(s: &str) -> bool { !s.trim().is_empty() }","tryCatchPattern":null,"preventionTips":["Disable the Explain button until SQL text exists","Validate input at the API boundary","Trim user input before storing"],"tags":["rust","tdengine","validation","sql","explain"],"backgroundTag":"missing-required-parameter","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"}