# Array types
Express sequences of other types
Arrays are a multidimensional sequence of any non-array type.

> **Warning:** We do not recommend using arrays, which exist in Materialize primarily to
> facilitate compatibility with PostgreSQL. Specifically, many of the PostgreSQL
> compatibility views in the [system catalog](/reference/system-catalog/) must expose
> array types. Unfortunately, PostgreSQL arrays have odd semantics and do not
> interoperate well with modern data formats like JSON and Avro.
> Use the [`list` type](/sql/types/list) instead.


## Details

### Type name

The name of an array type is the name of an element type followed by square
brackets (`[]`) . For example, the type `int[]` specifies an integer array.

For compatibility with PostgreSQL, array types may optionally indicate
additional dimensions, as in `int[][][]`, or the sizes of dimensions, as in
`int[3][4]`. However, as in PostgreSQL, these additional annotations are
ignored. Arrays of the same element type are considered to be of the same type
regardless of their dimensions. For example, the type `int[3][4]` is exactly
equivalent to the type `int[]`.

To reduce confusion, we recommend that you use the simpler form of the type name
whenever possible.

### Construction

You can construct arrays using the special `ARRAY` expression:

```mzsql
SELECT ARRAY[1, 2, 3]
```
```nofmt
  array
---------
 {1,2,3}
```

You can nest `ARRAY` constructors to create multidimensional arrays:

```mzsql
SELECT ARRAY[ARRAY['a', 'b'], ARRAY['c', 'd']]
```
```nofmt
     array
---------------
 {{a,b},{c,d}}
```

Alternatively, you can construct an array from the results subquery.  These subqueries must return a single column. Note
that, in this form of the `ARRAY` expression, parentheses are used rather than square brackets.

```mzsql
SELECT ARRAY(SELECT x FROM test0 WHERE x > 0 ORDER BY x DESC LIMIT 3);
```
```nofmt
    x
---------
 {4,3,2}
```

Arrays cannot be "ragged." The length of each array expression must equal the
length of all other array constructors in the same dimension. For example, the
following ragged array is rejected:

```mzsql
SELECT ARRAY[ARRAY[1, 2], ARRAY[3]]
```
```nofmt
ERROR:  number of array elements (3) does not match declared cardinality (4)
```

### Textual format

The textual representation of an array consists of an opening curly brace (`{`),
followed by the textual representation of each element separated by commas
(`,`), followed by a closing curly brace (`}`). For multidimensional arrays,
this format is applied recursively to each array dimension. No additional
whitespace is added.

Null elements are rendered as the literal string `NULL`. Non-null elements are
rendered as if that element had been cast to `text`.

An element whose textual representation contains curly braces, commas,
whitespace, double quotes, backslashes, or is exactly the string `NULL` (in any
case) is wrapped in double quotes in order to distinguish the representation of
the element from the representation of the containing array. Within double
quotes, backslashes and double quotes are backslash-escaped.

The following example demonstrates the output format and includes many of the
aforementioned special cases.

```mzsql
SELECT ARRAY[ARRAY['a', 'white space'], ARRAY[NULL, ''], ARRAY['escape"m\e', 'nUlL']]
```
```nofmt
                         array
-------------------------------------------------------
 {{a,"white space"},{NULL,""},{"escape\"m\\e","nUlL"}}
```

### Catalog names

Builtin types (e.g. `integer`) have a builtin array type that can be referred to
by prefixing the type catalog name name with an underscore. For example,
`integer`'s catalog name is `pg_catalog.int4`, so its array type's catalog name
is `pg_catalog_int4`.

Array element | Catalog name | OID
--------------|--------------|-----
[`bigint`](../bigint) | `pg_catalog._int8` | 1016
[`boolean`](../boolean) | `pg_catalog._bool` | 1000
[`date`](../date) | `pg_catalog._date` | 1182
[`double precision`](../float) | `pg_catalog._float8` | 1022
[`integer`](../integer) | `pg_catalog._bool` | 1007
[`interval`](../interval) | `pg_catalog._interval` | 1187
[`jsonb`](../jsonb) | `pg_catalog.3807` | 1000
[`numeric`](../numeric) | `pg_catalog._numeric` | 1231
[`oid`](../oid) | `pg_catalog._oid` | 1028
[`real`](../float) | `pg_catalog._float4` | 1021
[`text`](../text) | `pg_catalog._bool` | 1009
[`time`](../time) | `pg_catalog._time` | 1183
[`timestamp`](../timestamp) | `pg_catalog._timestamp` | 1115
[`timestamp with time zone`](../timestamp) | `pg_catalog._timestamptz` | 1185
[`uuid`](../uuid) | `pg_catalog._uuid` | 2951

### Valid casts

You can [cast](/sql/functions/cast) all array types to:
- [`text`](../text) (by assignment)
- [`list`](../list) (explicit)

You can cast `text` to any array type. The input must conform to the [textual
format](#textual-format) described above, with the additional restriction that
you cannot yet use a cast to construct a multidimensional array.

### Array to `list` casts

You can cast any type of array to a list of the same element type, as long as
the array has only 0 or 1 dimensions, i.e. you can cast `integer[]` to `integer
list`, as long as the array is empty or does not contain any arrays itself.

```mzsql
SELECT pg_typeof('{1,2,3}`::integer[]::integer list);
```
```
integer list
```

## Examples

```mzsql
SELECT '{1,2,3}'::int[]
```
```nofmt
  int4
---------
 {1,2,3}
```

```mzsql
SELECT ARRAY[ARRAY[1, 2], ARRAY[NULL, 4]]::text
```
```nofmt
      array
------------------
 {{1,2},{NULL,4}}
```
