> This page location: Developer Features > Array and Bytea Function Improvements
> Full Neon documentation index: https://neon.com/docs/llms.txt

# PostgreSQL 18 Array and Bytea Function Improvements

**Summary**: In this tutorial, you will learn about PostgreSQL 18's new array and bytea function improvements including `array_sort()`, `array_reverse()`, `reverse()` for bytea, integer-to-bytea casting capabilities, and `MIN`/`MAX` aggregate support for arrays and composite types.

## Introduction to Array and Bytea Improvements

PostgreSQL 18 introduces several useful enhancements for working with arrays and binary data (bytea). These improvements address common developer needs that previously required custom functions or complex workarounds.

The key additions include native array sorting and reversing, bytea manipulation functions, simplified integer-to-bytea conversions, and expanded aggregate support.

## New Array Functions

PostgreSQL 18 adds two important array manipulation functions that developers have long requested.

### array_sort() Function

The `array_sort()` function provides native array sorting capabilities, eliminating the need for custom sorting implementations.

```sql
-- Sort different data types
SELECT array_sort(ARRAY[3, 1, 4, 1, 5, 9, 2, 6]);
-- Result: {1,1,2,3,4,5,6,9}

SELECT array_sort(ARRAY['zebra', 'apple', 'banana']);
-- Result: {apple,banana,zebra}
```

### array_reverse() Function

The `array_reverse()` function reverses the order of elements in the first dimension:

```sql
SELECT array_reverse(ARRAY[1, 2, 3, 4, 5]);
-- Result: {5,4,3,2,1}

SELECT array_reverse(ARRAY['first', 'second', 'third']);
-- Result: {third,second,first}
```

For multi-dimensional arrays, these functions operate only on the first dimension, so `array_reverse(ARRAY[ARRAY[1,2], ARRAY[3,4]])` becomes `{{3,4},{1,2}}`.

### Array Function Performance

Array functions work efficiently on reasonably sized arrays but may impact performance with very large arrays containing thousands of elements. The `array_sort()` and `array_reverse()` functions operate in memory, so consider the size of your arrays when using them in large datasets.

## Binary Data (Bytea) Improvements

PostgreSQL 18 introduces several improvements for working with binary data, including a new reverse function and better casting capabilities.

### New reverse() Function for Bytea

The `reverse()` function allows you to reverse the byte order of binary data:

```sql
SELECT reverse('\x12345678'::bytea);
-- Result: \x78563412
```

This can be used to convert between big-endian and little-endian formats, cryptographic operations that require byte reversal, and data format transformations.

## Integer-to-Bytea Casting

PostgreSQL 18 introduces casting between integer types and bytea. The bytea value has the most significant byte first (big-endian format).

### Basic Integer Casting

```sql
SELECT 1234::bytea;
-- Result: \x000004d2

SELECT (-1234)::bytea;
-- Result: \xfffffb2e
```

Different integer types produce different byte lengths - the output size is fixed based on the integer type, not the value:

```sql
SELECT 123::smallint::bytea;  -- Always 2 bytes: \x007b
SELECT 123::integer::bytea;   -- Always 4 bytes: \x0000007b
SELECT 123::bigint::bytea;    -- Always 8 bytes: \x000000000000007b
```

In previous versions, if you were to try to cast an integer directly to bytea, you would have to use a custom function or complex bit manipulation. Now, this is simplified with direct casting.

Whether you cast `123` or `123456`, an `integer::bytea` conversion always produces exactly 4 bytes.

You can also cast back from bytea to integers:

```sql
SELECT '\x007b'::bytea::smallint;
-- Result: 123
```

This feature is useful for compact binary storage of numeric IDs, protocol implementations that require specific byte representations, and interfacing with external systems that expect binary integer formats.

## MIN/MAX Aggregates for Arrays and Composite Types

PostgreSQL 18 extends `MIN()` and `MAX()` aggregate functions to work with arrays and composite types, using their natural comparison operators.

### Array Aggregates

Arrays are compared element by element (lexicographically). This means `{1,3}` is considered less than `{2,1}` because the first element is smaller.

Let's see how this works with arrays:

```sql
-- Sample table with arrays
CREATE TABLE sales_data (
    product VARCHAR(50),
    monthly_sales INTEGER[]
);

INSERT INTO sales_data VALUES
    ('Laptop', ARRAY[45, 52, 38]),
    ('Mouse', ARRAY[67, 71, 58]),
    ('Keyboard', ARRAY[23, 28, 15]);

-- Find minimum and maximum sales arrays
SELECT
    MIN(monthly_sales) AS min_sales_pattern,
    MAX(monthly_sales) AS max_sales_pattern
FROM sales_data;

 min_sales_pattern | max_sales_pattern
-------------------+-------------------
 {23,28,15}        | {67,71,58}
(1 row)
```

This query returns the `Keyboard` monthly_sales value for min and the `Mouse` value for max because it is evaluating based on the first element of each array.

### Composite Type Aggregates

Composite types are compared field by field, from left to right. Note that all field types in the composite must have comparison operators (`<`, `>`) defined, or you'll get an error.

```sql
-- Create a composite type
CREATE TYPE product_rating AS (
    average_score DECIMAL(3,2),
    review_count INTEGER
);

-- Use MIN/MAX with the composite type
SELECT
    MIN(rating) AS lowest_rating,
    MAX(rating) AS highest_rating
FROM (VALUES
    (ROW(4.5, 120)::product_rating),
    (ROW(4.2, 89)::product_rating),
    (ROW(4.8, 156)::product_rating)
) AS ratings(rating);

 lowest_rating | highest_rating
---------------+----------------
 (4.20,89)     | (4.80,156)
```

This query finds the lowest and highest product ratings based on average score and review count. In previous PostgreSQL versions, using `MIN()` or `MAX()` on arrays or composite types required custom aggregate functions or complex subqueries. Now, these operations are straightforward and come built-in.

### Aggregate Performance

The MIN/MAX aggregates on arrays and composite types currently require sequential scans through the data. Unlike simple column aggregates, these cannot benefit from standard B-tree indexes because PostgreSQL cannot directly index arbitrary array contents or composite type comparisons.

```sql
-- This will scan all rows to find MIN/MAX arrays
SELECT MIN(monthly_sales), MAX(monthly_sales) FROM sales_data;

```

---

## Related docs (Developer Features)

- [Virtual Generated Columns (Now Default)](https://neon.com/postgresql/postgresql-18/virtual-generated-columns)
- [UUIDv7: Timestamp-Ordered UUIDs](https://neon.com/postgresql/postgresql-18/uuidv7-support)
- [Enhanced RETURNING with OLD/NEW Values](https://neon.com/postgresql/postgresql-18/enhanced-returning)
- [Temporal Constraints with WITHOUT OVERLAPS](https://neon.com/postgresql/postgresql-18/temporal-constraints)
- [Logical Replication Improvements](https://neon.com/postgresql/postgresql-18/logical-replication-improvements)
