# Supported data types (PostgreSQL)

## Writing: .NET to PostgreSQL

| .NET value | Column | Stored as |
|---|---|---|
| `string` | `text`, `varchar(n)`, `char(n)` | as written (`char(n)` pads with spaces) |
| `short` / `int` / `long` | `smallint` / `int` / `bigint` | `32000` / `2000000000` / `9000000000000` |
| `decimal` | `numeric(p, s)` | `12345.678901` |
| `float` / `double` | `real` / `double precision` | `3.25` / `2.718281828459045` |
| `bool` | `boolean` | `true` |
| `Guid` | `uuid` | `6f9619ff-8b86-d011-b42d-00c04fc964ff` |
| `DateTime` (Kind `Utc`), `DateTimeOffset` | `timestamptz` | the UTC instant: `2026-11-07 14:30:00+00` |
| `DateTime` (Kind `Unspecified`) | `timestamp` | as written: `2026-11-07 09:00:00` |
| `DateOnly` / `TimeOnly` / `TimeSpan` | `date` / `time` / `interval` | `2026-11-07` / `17:45:30` / `1 day 12:30:00` |
| `byte[]`, `ArxBytea` | `bytea` | `\xcafe` |
| `ArxJsonB` | `jsonb` | the JSON |
| `string[]`, `int[]`, `decimal[]`, `Guid[]`, `DateOnly[]`, `DateTime[]`, … | `text[]`, `int[]`, … | `{red,green}`, `{1,2,3}`, … |
| `ArxEntityReference` | the lookup column | the referenced id |
| `ArxSelectionSetValue` | the set's column | the option's code |
| `null` | any | `NULL` |
| `byte`, `sbyte` / `ushort` / `uint` / `ulong` | converted to `smallint` / `int` / `bigint` / `numeric` | `200` / `65000` / `4000000000` / `18446744073709551615` |
| `char` | text | `X` |
| a plain `enum` | refused (ARX1007) | write `(int)value` or `value.ToString()`, or use a selection set, which stores the code its set declares |
| `DateTime` with Kind `Local` | `timestamptz` | converted to UTC |

Any other type is refused before anything is sent (`ARX1007 UnsupportedValueType`): write `uri.ToString()` rather than
a `Uri`. `RealCase2_EveryDataType` writes every row of this table and prints what PostgreSQL stored.

With the column types preloaded (`ArxPreLoadMode.DataTypes`), a number with a fraction written to a whole-number column is refused before PostgreSQL (`ARX4001 ValueOutOfRange`) on a create, an update and a write by a filter, where PostgreSQL would round it (7.9 stored as 8); a condition in a read (`qty < 7.5`) isn't refused. A `NaN` or an infinity written to a declared `decimal` column is refused the same way. Without declared types, PostgreSQL stores a `double` `NaN` in a `numeric` column, and it reads back as a `double`.

## Dates and times

| You have | Use | Column |
|---|---|---|
| A moment (an order was placed) | `DateTimeOffset`, or `DateTime` with Kind `Utc` | `timestamptz` |
| A wall-clock time with no zone | `DateTime` with Kind `Unspecified` | `timestamp` |
| A calendar date | `DateOnly` | `date` |
| A time of day | `TimeOnly` | `time` |
| A duration | `TimeSpan` | `interval` |

The same moment written as a `DateTimeOffset` (+08:00), a UTC `DateTime` or a local `DateTime` is stored as the same
instant. A `DateTime` with Kind `Unspecified` written to a `timestamptz` column is read in the session's time zone:
ARX's own connections are UTC (`Timezone=UTC`), so `09:00` is stored as `09:00+00`; a connection string that names
another zone (`Timezone=Asia/Manila`) makes it `01:00+00`. Give a moment Kind `Utc`, or a `DateTimeOffset`, so the
session can't change it. Refused: `default(DateTime)` (`ARX1013`: it means "not set", and PostgreSQL would store `-infinity`; use
`null`), and a date given as a string (`ARX4004`: ARX never parses strings as dates). Full rules:
[Data types](/csharp/postgresql/data-types).

## Reading: PostgreSQL to .NET

| PostgreSQL column | Reads back as | Notes |
|---|---|---|
| `text`, `varchar(n)`, `char(n)` | `string` | `char(n)` keeps its padding spaces; `""` stays `""` |
| `smallint` / `integer` / `bigint` | `short` / `int` / `long` | |
| `numeric(p, s)` | `decimal` | More than 28 significant digits: `ARX4001 ValueOutOfRange`. `NaN`, `Infinity` and `-Infinity`, which a `numeric` can hold and a `decimal` can't, read back as a `double` (from 0.1.0-preview.5; the read failed before) |
| `real` / `double precision` | `float` / `double` | |
| `boolean` | `bool` | |
| `uuid` | `Guid` | |
| `timestamptz` | `DateTime`, Kind `Utc` | The instant, in UTC whatever the session's time zone |
| `timestamp` | `DateTime`, Kind `Unspecified` | The wall-clock time as stored |
| `date` / `time` / `interval` | `DateOnly` / `TimeOnly` / `TimeSpan` | An interval with months or years: `ARX4004 DataTypeMismatch` (a `TimeSpan` can't hold one) |
| `bytea` | `byte[]` | |
| `json`, `jsonb` | `ArxJsonB` | `SerializedValue` holds the JSON text |
| An array (`text[]`, `int[]`, `date[]`, …) | `T[]` (`string[]`, `int[]`, `DateOnly[]`, …) | With NULL elements, a value type reads as `T?[]` (`int?[]`) |
| An enum type | `string` | Its label, from the first read when the schema is preloaded |
| A composite type | `string` | Its text form |
| A domain | Its base type | |
| A declared lookup (`.Lookup(...)`) | `ArxEntityReference` | `Name` from the referenced table's `.DisplayName(...)` |
| A selection set column | Its stored code | `entity.GetSelectionSet(column, set, catalog)` gives the option |
| NULL | `null` | The attribute is present, with a null value |

`RealCase1_EveryDataTypeReadBack` writes one row of every type with Create and prints each value's .NET type as it
reads back.
