{"record":{"id":"8d1157416289a5cd","repo":"Tencent/WeKnora","slug":"locking-clauses-for-update-etc-are-not-allowed","errorCode":null,"errorMessage":"locking clauses (FOR UPDATE, etc.) are not allowed","messagePattern":"locking clauses \\(FOR UPDATE, etc\\.\\) are not allowed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/utils/inject.go","lineNumber":1363,"sourceCode":"\n\t// Check for UNION/INTERSECT/EXCEPT (compound queries)\n\tif stmt.Op != pg_query.SetOperation_SETOP_NONE {\n\t\treturn fmt.Errorf(\"compound queries (UNION/INTERSECT/EXCEPT) are not allowed\")\n\t}\n\n\t// Check for WITH clause (CTEs)\n\tif v.checkCTEs && stmt.WithClause != nil {\n\t\treturn fmt.Errorf(\"WITH clause (CTEs) is not allowed\")\n\t}\n\n\t// Check for INTO clause (SELECT INTO)\n\tif stmt.IntoClause != nil {\n\t\treturn fmt.Errorf(\"SELECT INTO is not allowed\")\n\t}\n\n\t// Check for LOCKING clause (FOR UPDATE, etc.)\n\tif len(stmt.LockingClause) > 0 {\n\t\treturn fmt.Errorf(\"locking clauses (FOR UPDATE, etc.) are not allowed\")\n\t}\n\n\t// Validate FROM clause\n\tfor _, fromItem := range stmt.FromClause {\n\t\tif err := v.validateFromItem(fromItem, tablesInQuery, result); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// Validate target list (SELECT columns)\n\tfor _, target := range stmt.TargetList {\n\t\tif err := v.validateNode(target, result); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// Validate WHERE clause\n\tif stmt.WhereClause != nil {","sourceCodeStart":1345,"sourceCodeEnd":1381,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/utils/inject.go#L1345-L1381","documentation":"validateSelectStmt rejects SELECT statements with a non-empty LockingClause, i.e. row-locking modifiers like FOR UPDATE, FOR SHARE, FOR NO KEY UPDATE, or FOR KEY SHARE. These clauses change transactional/locking semantics rather than just reading data, so the validator disallows them to keep validated queries side-effect-free. The check is unconditional (len(stmt.LockingClause) > 0).","triggerScenarios":"Calling the validation API with \"SELECT * FROM users WHERE id = $1 FOR UPDATE\" (typical in SELECT-then-UPDATE transaction patterns); the parse tree has LockingClause entries.","commonSituations":"ORM query builders that append FOR UPDATE for pessimistic locking inside transactions; developer copies a transactional query into the read-path validator; queue/debounce job implementations using FOR UPDATE SKIP LOCKED.","solutions":["Remove the locking clause for the validated read path and apply FOR UPDATE in the transactional layer instead.","Use a transactional execution path (e.g. db.Exec within a transaction) that skips this read-only validator for locking queries.","Split the flow: validate the plain SELECT, then issue the locking variant directly through the DB driver."],"exampleFix":"// before (rejected)\nq := \"SELECT * FROM jobs WHERE id = $1 FOR UPDATE\"\n\n// after: validate the plain query, lock in the transaction\nvalidate(\"SELECT * FROM jobs WHERE id = $1\")\ntx.Exec(\"SELECT * FROM jobs WHERE id = $1 FOR UPDATE\", id)","handlingStrategy":"validation","validationCode":"re := regexp.MustCompile(`(?i)\\bFOR\\s+(UPDATE|SHARE|NO\\s+KEY\\s+UPDATE|KEY\\s+SHARE)\\b`)\nif re.MatchString(sql) {\n    return fmt.Errorf(\"locking clause rejected; run this query on the transactional path, not the validated read path\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Reserve FOR UPDATE for explicit transaction code that bypasses the read-only validator.","Keep validation and execution paths aligned: validate what you validate-run, transact what you transact-run.","Watch ORM/QueryBuilder options that auto-append locking clauses.","Grep code review diffs for FOR UPDATE/FOR SHARE in read paths."],"tags":["sql","validation","locking","transactions","postgres"],"backgroundTag":"unsupported-sql-construct","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}