Tencent/WeKnora · error
compound queries (UNION/INTERSECT/EXCEPT) are not allowed
Error message
compound queries (UNION/INTERSECT/EXCEPT) are not allowed
What it means
validateSelectStmt rejects SELECT statements whose parse tree has a set operation (stmt.Op != SETOP_NONE), i.e. UNION, INTERSECT, or EXCEPT compound queries. The library restricts validation to simple single SELECTs because compound queries complicate table/alias tracking and can hide injection surface in the second branch. Any query combining two SELECTs via a set operator is rejected outright.
Source
Thrown at internal/utils/inject.go:1348
// Check length limits
if len(sql) < v.minLength {
return fmt.Errorf("SQL query too short (min %d characters)", v.minLength)
}
if len(sql) > v.maxLength {
return fmt.Errorf("SQL query too long (max %d characters)", v.maxLength)
}
return nil
}
// validateSelectStmt validates a SELECT statement with configured options
func (v *sqlValidator) validateSelectStmt(stmt *pg_query.SelectStmt, result *SQLValidationResult) error {
tablesInQuery := make(map[string]string) // table name -> alias
// Check for UNION/INTERSECT/EXCEPT (compound queries)
if stmt.Op != pg_query.SetOperation_SETOP_NONE {
return fmt.Errorf("compound queries (UNION/INTERSECT/EXCEPT) are not allowed")
}
// Check for WITH clause (CTEs)
if v.checkCTEs && stmt.WithClause != nil {
return fmt.Errorf("WITH clause (CTEs) is not allowed")
}
// Check for INTO clause (SELECT INTO)
if stmt.IntoClause != nil {
return fmt.Errorf("SELECT INTO is not allowed")
}
// Check for LOCKING clause (FOR UPDATE, etc.)
if len(stmt.LockingClause) > 0 {
return fmt.Errorf("locking clauses (FOR UPDATE, etc.) are not allowed")
}
// Validate FROM clauseView on GitHub (pinned to 988cbb0330)
Solutions
- Rewrite the query as a single SELECT (move logic into WHERE/JOIN, or dedupe results in application code instead of UNION).
- Validate each SELECT branch separately against the library, then combine results yourself.
- If compound queries are legitimate in your environment, bypass this validator or extend it to allow SETOP_UNION after validating both sides.
Example fix
// before
q := "SELECT id FROM users UNION SELECT id FROM admins"
// after: validate each side separately
validate("SELECT id FROM users")
validate("SELECT id FROM admins")
ids := unionInAppCode(idsUsers, idsAdmins) Defensive patterns
Strategy: validation
Validate before calling
upper := strings.ToUpper(sql)
for _, kw := range []string{" UNION ", " UNION ALL ", " INTERSECT ", " EXCEPT "} {
if strings.Contains(upper, kw) {
return fmt.Errorf("compound query with %s will be rejected; validate branches separately", strings.TrimSpace(kw))
}
} Prevention
- Treat the validator as single-SELECT only; document this for your team.
- Deduplicate or merge results in application code instead of UNION.
- Lint queries for set-operator keywords before submission.
- Validate each branch of a compound query independently.
When it happens
Trigger: Submitting a query like "SELECT a FROM t1 UNION SELECT b FROM t2" (or INTERSECT/EXCEPT) to the SQL validation API; validateSelectStmt sees stmt.Op != pg_query.SetOperation_SETOP_NONE and returns this error.
Common situations: Developer builds a legitimate paginated UNION query or dedup with UNION, not realizing the validator only accepts single SELECTs; query builders that emit EXCEPT for pagination.
Related errors
- WITH clause (CTEs) is not allowed
- SELECT INTO is not allowed
- locking clauses (FOR UPDATE, etc.) are not allowed
- invite code has expired
- join request not found
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/126b9b3c995ec922.
Report an issue: GitHub.