{"record":{"id":"e11065f041786535","repo":"siyuan-note/siyuan","slug":"sql-statement-is-empty","errorCode":null,"errorMessage":"SQL statement is empty","messagePattern":"SQL statement is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/sql/stmt_validate.go","lineNumber":188,"sourceCode":"// CheckAssetContentReadonlyStatement 在资源文件内容数据库连接上检查 SQL 是否只读。\nfunc CheckAssetContentReadonlyStatement(stmt string) error {\n\treturn checkReadonlyStatement(stmt, assetContentDB)\n}\n\n// CheckReadonlyStatementInBox 在指定笔记本对应的数据库连接上检查 SQL 是否只读。\nfunc CheckReadonlyStatementInBox(stmt, boxID string) error {\n\ttargetDB := db\n\tif boxDB := GetEncryptedDB(boxID); nil != boxDB {\n\t\ttargetDB = boxDB\n\t} else if IsEncryptedBoxFn != nil && IsEncryptedBoxFn(boxID) {\n\t\treturn errors.New(\"encrypted box db not opened for box \" + boxID)\n\t}\n\treturn checkReadonlyStatement(stmt, targetDB)\n}\n\nfunc checkReadonlyStatement(stmt string, targetDB *sql.DB) error {\n\tif strings.TrimSpace(stmt) == \"\" {\n\t\treturn errors.New(\"SQL statement is empty\")\n\t}\n\tif !isReadonlyQueryStatement(stmt) {\n\t\treturn errors.New(\"SQL statement is not a read-only query\")\n\t}\n\tif nil == targetDB {\n\t\treturn errors.New(\"database is nil\")\n\t}\n\tctx := context.Background()\n\tconn, err := targetDB.Conn(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer conn.Close()\n\n\treturn conn.Raw(func(dc any) error {\n\t\tsqliteConn, ok := dc.(*sqlite3.SQLiteConn)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"SQL driver connection type is unexpected: %T\", dc)","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/sql/stmt_validate.go#L170-L206","documentation":"Returned by checkReadonlyStatement when the SQL string is empty after TrimSpace. This is the first guard before any SQLite prepare — it refuses to run an empty/whitespace-only statement. Used by the read-only query validation path (CheckReadonlyStatement / CheckReadonlyStatementInBox / CheckAssetContentReadonlyStatement).","triggerScenarios":"Submitting an empty or whitespace-only SQL string (\"\", \"   \", \"\\n\\t\") to the SQL query API; a script passes an unset variable as the statement.","commonSituations":"UI submitted before typing; template/variable substitution produced empty string; CLI invoked with empty --stmt; trim of a comment-only statement yields empty.","solutions":["Provide a non-empty SELECT/WITH query string.","Guard the caller: skip the API call when strings.TrimSpace(stmt) == \"\".","If building SQL from variables, log the rendered statement before sending to catch empty substitutions."],"exampleFix":"// before\nstmt := strings.TrimSpace(userInput)\napi.QuerySQL(stmt) // errors if empty\n// after\nstmt := strings.TrimSpace(userInput)\nif stmt == \"\" { return errors.New(\"query is required\") }\napi.QuerySQL(stmt)","handlingStrategy":"validation","validationCode":"// Caller side: skip empty queries before calling the SQL API.\nconst trimmed = stmt.trim()\nif (trimmed === '') return new Error('query is required')\nawait api.querySQL(trimmed)","typeGuard":null,"tryCatchPattern":"try { await api.querySQL(stmt) }\ncatch (e) {\n  if (/statement is empty/i.test(String(e))) console.warn('skip empty SQL submission')\n  else throw e\n}","preventionTips":["Trim and check for empty before submitting SQL.","When building SQL from variables, log the rendered string to catch empty substitutions.","Disable the submit button in the UI when the query box is empty."],"tags":["sql","sqlite","validation","api"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}