{"record":{"id":"b69dd9bb1ce162af","repo":"prestodb/presto","slug":"invalid-function-argument-b69dd9","errorCode":"INVALID_FUNCTION_ARGUMENT","errorMessage":"zorder function requires a ROW type","messagePattern":"zorder function requires a ROW type","errorType":"error_code","errorClass":"PrestoException","httpStatus":null,"severity":"error","filePath":"presto-iceberg/src/main/java/com/facebook/presto/iceberg/function/IcebergZOrderFunctions.java","lineNumber":136,"sourceCode":"        return ZOrderByteUtils.longToOrderedBytes(value);\n    }\n\n    /**\n     * Computes Z-order value from a ROW of columns.\n     * Usage: zorder(ROW(col1, col2, col3, ...))\n     *\n     * @param rowType The type information for the row\n     * @param rowBlock The row block containing the column values\n     * @return The interleaved Z-order binary value\n     */\n    @ScalarFunction(\"zorder\")\n    @TypeParameter(\"T\")\n    @SqlType(StandardTypes.VARBINARY)\n    @SqlNullable\n    public static Slice zorder(@TypeParameter(\"T\") Type rowType, @SqlType(\"T\") Block rowBlock)\n    {\n        if (!(rowType instanceof RowType)) {\n            throw new PrestoException(INVALID_FUNCTION_ARGUMENT, \"zorder function requires a ROW type\");\n        }\n\n        RowType row = (RowType) rowType;\n        List<RowType.Field> fields = row.getFields();\n        int fieldCount = fields.size();\n\n        if (fieldCount == 0) {\n            return Slices.wrappedBuffer(new byte[0]);\n        }\n\n        // Convert each field to ordered bytes based on its type\n        Slice[] columnBytes = new Slice[fieldCount];\n        int totalSize = 0;\n\n        for (int i = 0; i < fieldCount; i++) {\n            Type fieldType = fields.get(i).getType();\n\n            if (rowBlock.isNull(i)) {","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-iceberg/src/main/java/com/facebook/presto/iceberg/function/IcebergZOrderFunctions.java#L118-L154","documentation":"The zorder() SQL function computes a Z-order key from a ROW-typed argument. If the resolved type T is not a RowType (e.g. a scalar was passed or the binding produced a non-row type), it throws INVALID_FUNCTION_ARGUMENT since Z-ordering requires multiple named fields to interleave.","triggerScenarios":"Calling zorder(col) where col is not a row/struct type — e.g. zorder(a, b) misuse or a bind that resolves T to a primitive instead of row(...).","commonSituations":"Users writing ORDER BY zorder(x, y) expecting multi-arg support; clustering DDL against tables whose column was changed from a struct to a scalar.","solutions":["Wrap the arguments in a row: zorder(CAST(ROW(a, b) AS ROW(x <type>, y <type>))).","Verify the column used in zorder is declared as a ROW type in the table schema.","Check function binding — ensure the @TypeParameter T is bound to a row type, not a scalar."],"exampleFix":"// before\nSELECT zorder(x, y) FROM t;\n// after\nSELECT zorder(CAST(ROW(x, y) AS ROW(a double, b double))) FROM t;","handlingStrategy":"type-guard","validationCode":"boolean rowTyped(Type t) { return t instanceof RowType; }\n// SQL-side check before clustering: DESCRIBE table; ensure column is row(...)","typeGuard":"boolean isRowTyped(Type t) {\n    return t instanceof RowType;\n}","tryCatchPattern":"try { result = session.getMetadata().getFunctionRegistry()...zorder(...); } catch (PrestoException e) { if (e.getErrorCode() == INVALID_FUNCTION_ARGUMENT.toErrorCode()) { /* wrap args in ROW(...) */ } throw e; }","preventionTips":["Always pass a single row(...)-typed column to zorder","Use CAST(ROW(a,b) AS ROW(x t1, y t2)) for multiple columns","Verify column types with DESCRIBE before sort-order DDL"],"tags":["iceberg","zorder","function-argument","row-type"],"backgroundTag":"invalid-function-arguments","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}