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 err

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Remove the INTO clause; use CREATE TABLE AS (ctas) separately outside this validator if you need a new table.
  2. Use a plain SELECT and create/insert the target table in application code.
  3. 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

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


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/d77b082d516abb58. Report an issue: GitHub.