{"record":{"id":"a78ec74b3d9b0e16","repo":"risingwavelabs/risingwave","slug":"argument-of-must-be-boolean-not-type","errorCode":null,"errorMessage":"argument of {} must be boolean, not type {:?}","messagePattern":"argument of (.+?) must be boolean, not type (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/expr/mod.rs","lineNumber":396,"sourceCode":"            return Err(ErrorCode::BindError(\n                \"could not determine polymorphic type because input has type unknown\".into(),\n            ));\n        }\n        match self.return_type() {\n            DataType::Map(m) => Ok(m),\n            t => Err(ErrorCode::BindError(format!(\"expects map but got {t}\"))),\n        }\n    }\n\n    /// Shorthand to enforce implicit cast to boolean\n    pub fn enforce_bool_clause(self, clause: &str) -> RwResult<ExprImpl> {\n        if self.is_untyped() {\n            let inner = self.cast_implicit(&DataType::Boolean)?;\n            return Ok(inner);\n        }\n        let return_type = self.return_type();\n        if return_type != DataType::Boolean {\n            bail!(\n                \"argument of {} must be boolean, not type {:?}\",\n                clause,\n                return_type\n            )\n        }\n        Ok(self)\n    }\n\n    /// Create \"cast\" expr to string (`varchar`) type. This is different from a real cast, as\n    /// boolean is converted to a single char rather than full word.\n    ///\n    /// Choose between `cast_output` and `cast_{assign,explicit}(Varchar)` based on `PostgreSQL`'s\n    /// behavior on bools. For example, `concat(':', true)` is `:t` but `':' || true` is `:true`.\n    /// All other types have the same behavior when formatting to output and casting to string.\n    ///\n    /// References in `PostgreSQL`:\n    /// * [cast](https://github.com/postgres/postgres/blob/a3ff08e0b08dbfeb777ccfa8f13ebaa95d064c04/src/include/catalog/pg_cast.dat#L437-L444)\n    /// * [impl](https://github.com/postgres/postgres/blob/27b77ecf9f4d5be211900eda54d8155ada50d696/src/backend/utils/adt/bool.c#L204-L209)","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/expr/mod.rs#L378-L414","documentation":"Thrown by `enforce_bool_clause` when an expression used in a position that requires a boolean (e.g. the ON condition of a join, a filter, or a CASE/HAVING clause) has a non-boolean return type and is not untyped (untyped expressions are implicitly cast to BOOLEAN instead). The error reports the clause name and the actual return type, so it is essentially a static type check failure during query binding/planning.","triggerScenarios":"Writing a WHERE/ON/HAVING/CHECK clause whose expression evaluates to a non-boolean type, e.g. `WHERE 1` with a typed integer column expression, `JOIN t ON a.some_int = b.some_int` without comparison, or using a string/UDTF expression directly as a predicate.","commonSituations":"Porting MySQL-style `WHERE col` truthiness idioms that Postgres/RisingWave rejects; forgetting the comparison operator (`ON a.id b.id`); a UDF or subquery returning INTEGER/TEXT used directly as a filter; accidental operator precedence making the clause a non-boolean expression.","solutions":["Add an explicit boolean comparison: change `WHERE flag_col` to `WHERE flag_col = TRUE` (or `<> 0` for integer flags).","For join conditions, use a proper predicate: `JOIN t ON a.id = b.id AND a.enabled`.","Cast once at the source: wrap the expression with `::boolean` or `(expr)::boolean = TRUE` if the underlying type is intentionally convertible.","Fix the UDF/subquery return type to BOOLEAN if the expression author intended a predicate."],"exampleFix":"-- before\nSELECT * FROM t JOIN u ON t.id u.id;\n-- after\nSELECT * FROM t JOIN u ON t.id = u.id;","handlingStrategy":"validation","validationCode":"-- check the clause expression type ahead of running the query\n-- (in app code) ensure the predicate column is BOOLEAN before composing WHERE clauses\nSELECT data_type FROM information_schema.columns\nWHERE table_name = 't' AND column_name = 'flag_col';","typeGuard":null,"tryCatchPattern":"match enforce_bool_clause(expr, \"WHERE\") {\n    Ok(e) => proceed(e),\n    Err(ExprError::BoolCheck(clause, ty)) => {\n        // auto-wrap: expr.cast_implicit(DataType::Boolean) or reject query with clear message\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Never rely on integer/string truthiness; always write explicit `= TRUE` / `<> 0` comparisons.","Design UDFs used in filters to return BOOLEAN.","Check column types with information_schema before generating predicates in ORMs/query builders.","Use parenthesization to guarantee operator precedence in complex boolean clauses."],"tags":["type-check","sql","boolean","query-planning"],"backgroundTag":"type-mismatch","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}