siyuan-note/siyuan · error
SQL statement is not single
Error message
SQL statement is not single
What it means
CheckSingleStatement rejects an SQL string whose text contains more than one statement — containsMultipleStatements detected a `;` followed by non-whitespace, non-comment content. This guards the SQL query API (used by the embedded SQLite / SiYuan SQL endpoint) against statement stacking/injection. It is a hard validation, not a runtime fault.
Source
Thrown at kernel/sql/stmt_validate.go:155
i++
case '/' == ch && next == '*':
inBlockComment = true
i++
case ';' == ch:
tail := string(runes[i+1:])
if tailIsOnlyWhitespaceOrSQLComments(tail) {
// 分号后仅有空白与 SQL 注释时,SQLite 仍视为同一条语句末尾,不应判为多语句。
continue
}
return true
}
}
return false
}
func CheckSingleStatement(stmt string) error {
if containsMultipleStatements(stmt) {
return errors.New("SQL statement is not single")
}
return nil
}
// CheckReadonlyStatement 对整段 SQL 做 prepare(不执行),用 sqlite3_stmt_readonly 判断首条语句是否只读。
// 见 https://sqlite.org/c3ref/stmt_readonly.html
//
// 注意:若字符串里在语法上还有第二条及以后的语句,本函数只针对「首条」对应的 stmt 做判断,
// 不会拒绝多语句。与 CheckSingleStatement 组合即可得到「单条 + 只读」策略。
// 仅允许 SELECT 和 WITH 查询,避免 SQLite 将 ATTACH、DETACH 和事务控制语句标记为只读后放行。
func CheckReadonlyStatement(stmt string) error {
return checkReadonlyStatement(stmt, db)
}
// CheckAssetContentReadonlyStatement 在资源文件内容数据库连接上检查 SQL 是否只读。
func CheckAssetContentReadonlyStatement(stmt string) error {
return checkReadonlyStatement(stmt, assetContentDB)
}View on GitHub (pinned to 251596fc0d)
Solutions
- Send only one SQL statement per request; remove the second statement.
- If a trailing `;` is the only extra content, the validator allows trailing whitespace/comments — verify there is no real second statement after it.
- For batch needs, issue separate API calls per statement rather than stacking.
Example fix
// before stmt := "SELECT * FROM blocks LIMIT 1; SELECT * FROM blocks LIMIT 2" // after stmt := "SELECT * FROM blocks LIMIT 1"
Defensive patterns
Strategy: validation
Validate before calling
// Caller side: reject multi-statement SQL before calling the kernel API.
function isSingleStatement(stmt: string): boolean {
// crude guard: no ';' except possibly one trailing
const trimmed = stmt.replace(/--[^
]*
/g, ' ').replace(/\/\*[\s\S]*?\*\//g, ' ').trim()
if (!trimmed.endsWith(';')) return trimmed.split(';').length === 1
return trimmed.slice(0, -1).split(';').filter(s => s.trim()).length <= 1
} Try / catch
try { await api.querySQL(stmt) }
catch (e) {
if (/not single/i.test(String(e))) console.warn('split into one statement per request')
else throw e
} Prevention
- Send one statement per API call; never stack with `;`.
- Validate user-supplied SQL on the client before submission.
- Remember a lone trailing `;` followed by whitespace/comments is allowed.
When it happens
Trigger: Submitting a query like `SELECT * FROM blocks; DROP TABLE blocks;` or `SELECT 1; SELECT 2` to the SQL API (/api/query/sql or CLI sql). Any `;` that is not trailing-whitespace-or-comment triggers it.
Common situations: User pastes multiple queries into the SQL box; a script concatenates queries with `;`; SQL client semicolon-terminates even single queries where the trailing content is another statement.
Related errors
- SQL statement is empty
- SQL statement is not a read-only query
- task list item marker length should be 1
- task list item marker can not be [ or ]
- custom emoji file is too large
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/13883d5ffad209cb.
Report an issue: GitHub.