{"record":{"id":"529580509a25844a","repo":"risingwavelabs/risingwave","slug":"dist-key-must-in-order-key","errorCode":null,"errorMessage":"dist_key must in order_key","messagePattern":"dist_key must in order_key","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/optimizer/plan_node/logical_join.rs","lineNumber":408,"sourceCode":"            | JoinType::AsofInner\n            | JoinType::AsofLeftOuter => {}\n            _ => return Ok(None),\n        };\n\n        let table = logical_scan.table();\n        let output_column_ids = logical_scan.output_column_ids();\n\n        // Verify that the right join key columns are the the prefix of the primary key and\n        // also contain the distribution key.\n        let order_col_ids = table.order_column_ids();\n        let dist_key = table.distribution_key.clone();\n        // The at least prefix of order key that contains distribution key.\n        let mut dist_key_in_order_key_pos = vec![];\n        for d in dist_key {\n            let pos = table\n                .order_column_indices()\n                .position(|x| x == d)\n                .expect(\"dist_key must in order_key\");\n            dist_key_in_order_key_pos.push(pos);\n        }\n        // The shortest prefix of order key that contains distribution key.\n        //\n        // If the lookup table has a singleton distribution (i.e. an empty distribution key),\n        // any non-empty prefix of the order key can be used for the lookup, so we require a\n        // prefix of length at least 1.\n        let shortest_prefix_len = dist_key_in_order_key_pos\n            .iter()\n            .max()\n            .map_or(1, |pos| pos + 1);\n\n        // Reorder the join equal predicate to match the order key.\n        let reorder_idx =\n            Self::lookup_prefix_reorder_idx(&predicate, &output_column_ids, &order_col_ids);\n        if reorder_idx.len() < shortest_prefix_len {\n            return Ok(None);\n        }","sourceCodeStart":390,"sourceCodeEnd":426,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/optimizer/plan_node/logical_join.rs#L390-L426","documentation":"gen_batch_lookup_join computes the position of each distribution key column within the lookup table's order key and expect()s it to be found. If a distribution key column is not part of the primary-key/order columns, the invariant `dist_key must in order_key` is violated and it panics.","triggerScenarios":"Calling gen_batch_lookup_join (public) with a lookup table whose distribution key columns are not a subset of its order column indices.","commonSituations":"Lookup join against a table with a custom/non-default distribution or after schema changes where the distribution no longer aligns with the primary key; also hit by API users constructing lookup joins programmatically.","solutions":["Ensure the lookup table's distribution key is a prefix/subset of its primary key (order key) columns","Re-create the table with a PRIMARY KEY that matches its distribution","Skip lookup-join conversion when the invariant does not hold and use a normal hash join","Validate table.distribution_key ⊆ order_column_indices before calling"],"exampleFix":"// before\nlet pos = table.order_column_indices().position(|x| x == d).expect(\"dist_key must in order_key\");\n// after\nlet pos = match table.order_column_indices().position(|x| x == d) {\n    Some(p) => p,\n    None => return Err(anyhow!(\"distribution key {:?} not in order key of lookup table\", d)),\n};","handlingStrategy":"validation","validationCode":"fn dist_key_in_order_key(table: &TableCatalog) -> bool { table.distribution_key.iter().all(|d| table.order_column_indices().any(|o| o == *d)) }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Define tables with PRIMARY KEY aligned to distribution key","Validate dist_key ⊆ order_column_indices before lookup join conversion","Add precondition assertions with descriptive errors instead of expect"],"tags":["optimizer","lookup-join","distribution","primary-key"],"backgroundTag":"internal-invariant-violation","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"}