Tencent/WeKnora · error
SELECT INTO is not allowed
Error message
SELECT INTO is not allowed
What it means
validateSelectStmt rejects any statement with a non-nil IntoClause, i.e. SELECT ... INTO. This is a data-modifying construct (it creates a table), so the validator treats it as out of scope for a read-only SELECT validator and as a potential abuse vector. It is rejected unconditionally with no bypass option.
Source
Thrown at internal/utils/inject.go:1358
}
// 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 clause
for _, fromItem := range stmt.FromClause {
if err := v.validateFromItem(fromItem, tablesInQuery, result); err != nil {
return err
}
}
// Validate target list (SELECT columns)
for _, target := range stmt.TargetList {
if err := v.validateNode(target, result); err != nil {
return errView on GitHub (pinned to 988cbb0330)
Solutions
- Remove the INTO clause; use CREATE TABLE AS (ctas) separately outside this validator if you need a new table.
- Use a plain SELECT and create/insert the target table in application code.
- Run SELECT INTO outside the validated read path, with proper authorization.
Example fix
// before (rejected)
q := "SELECT * INTO users_backup FROM users"
// after
validate("SELECT * FROM users")
db.Exec("CREATE TABLE users_backup AS SELECT * FROM users") Defensive patterns
Strategy: validation
Validate before calling
re := regexp.MustCompile(`(?i)\bINTO\s+(TEMP\s+|UNLOGGED\s+)?[\w."]+\s+FROM\b`)
if re.MatchString(sql) {
return fmt.Errorf("SELECT INTO rejected; use CREATE TABLE AS outside the validated read path")
} Prevention
- Never route table-creating statements through the read-only SELECT validator.
- Use CREATE TABLE AS on a separate authorized execution path.
- Keep DDL and read paths in distinct code layers.
- Review ported psql scripts for SELECT INTO before integrating.
When it happens
Trigger: Passing "SELECT * INTO new_table FROM users" (or INTO TEMP/UNLOGGED forms) to the validation API; stmt.IntoClause is non-nil in the pg_query parse tree.
Common situations: Developer needs to materialize results server-side and reaches for SELECT INTO; ported scripts from psql workflows; accidental copy-paste of DDL-ish queries into a read-path validator.
Related errors
- compound queries (UNION/INTERSECT/EXCEPT) are not allowed
- WITH clause (CTEs) 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/d77b082d516abb58.
Report an issue: GitHub.