# Supported data types (MySQL)

## Writing: .NET to MySQL

| .NET value | Column | Stored as |
|---|---|---|
| `string` | `VARCHAR(n)`, `CHAR(n)`, `TEXT` | as written (`CHAR(n)` pads with spaces when it stores and drops them when it reads) |
| `byte` / `short` / `int` / `long` | `TINYINT` / `SMALLINT` / `INT` / `BIGINT` | `100` / `32000` / `2000000000` / `9000000000000` |
| `uint` | `INT UNSIGNED` | `4000000000` |
| `decimal` | `DECIMAL(p, s)` | `12345.678901` |
| `float` / `double` | `FLOAT` / `DOUBLE` | `3.25` / `2.718281828459045` (a `FLOAT` keeps about six significant digits) |
| `bool` | `BOOLEAN` (`TINYINT(1)`) | `1` |
| `Guid` | `CHAR(36)` | `6f9619ff-8b86-d011-b42d-00c04fc964ff` (lower case); to a `BINARY(16)` column as its 16 bytes when the schema is preloaded |
| `DateTimeOffset` | `TIMESTAMP(6)` | the moment, stored as UTC: `22:30 +08:00` is `14:30:00.000000` (the offset isn't kept) |
| `DateTime` | `DATETIME(6)`, `DATETIME` | as written: `2026-11-07 09:00:00.000000` (a plain `DATETIME` rounds to the second) |
| `DateOnly` / `TimeOnly` | `DATE` / `TIME(6)` | `2026-11-07` / `17:45:30.000000` |
| `byte[]`, `ArxBytea` | `BLOB`, `LONGBLOB`, `BINARY(n)` | `0xCAFE` |
| `ArxJsonB` | `JSON` | MySQL's own form of the document (invalid JSON: `ARX4003`) |
| `ArxEntityReference` | the lookup column | the referenced id |
| `ArxSelectionSetValue` | the set's column (an `ENUM` or `SET` column included) | the option's code |
| `null` | any | `NULL` |
| `sbyte` / `ushort` / `uint` / `ulong` | converted to `SMALLINT` / `INT` / `BIGINT` / `DECIMAL` | `-100` / `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 `Utc` | `TIMESTAMP(6)` | the moment |
| `DateTime` with Kind `Local` | `DATETIME(6)`, `TIMESTAMP(6)` | converted to UTC first |

Refused before anything is sent: any other type (`ARX1007 UnsupportedValueType`: write `uri.ToString()` rather than a
`Uri`); arrays (`ARX8001`: MySQL has no array columns, so store a list as delimited text, JSON or rows of its own);
`NaN` and the infinities (`ARX4001`). A `TIME` column also holds durations, up to ±838 hours (a `TimeSpan`).

With the column types preloaded (`ArxPreLoadMode.DataTypes`), a number with a fraction written to a whole-number column is refused before MySQL (`ARX4001 ValueOutOfRange`) on a create, an update and a write by a filter, where MySQL 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.

MySQL converts some values itself: `"42"` into an `INT`, `2.5` into an `INT` by rounding (half away from zero), a
year-first string such as `'2026-11-07'` into a `DATE`. Text that isn't a number, and a date string in any other order,
are errors (`ARX4003`). A preload with column types (`ArxPreLoadMode.DataTypes`) refuses a value of the wrong type before
the database. `RealCase2_EveryDataType` writes every row of this table and prints what MySQL stored.

## Dates and times

| You have | Use | Column |
|---|---|---|
| A moment (an order was placed) | `DateTimeOffset`, or `DateTime` with Kind `Utc` | `TIMESTAMP(6)` (1970 to 2038) |
| A moment, past 2038 | `DateTime` with Kind `Utc` | `DATETIME(6)` (store UTC, read it back as UTC) |
| A wall-clock time with no zone | `DateTime` with Kind `Unspecified` | `DATETIME(6)` |
| A calendar date | `DateOnly` | `DATE` |
| A time of day | `TimeOnly` | `TIME(6)` |
| A duration | a number (seconds, minutes), or a `TimeSpan` up to 838 hours | `INT`, `BIGINT`, `TIME` |

A `TIMESTAMP` is converted from the session's time zone to UTC when stored and back when read: ARX's sessions are UTC,
so the instant is stored and read as it is. A `DATETIME` stores the clock time it is given: a `DateTime` with Kind `Unspecified` or `Utc` as it is, and one with Kind `Local` (`DateTime.Now`) converted to UTC first, as every ARX write does with a local time. It reads back with no zone, so use Kind `Unspecified` for a wall-clock time and `Utc` for a moment, never `Local`. Fractional
seconds are kept only when the column declares them (`(6)`); MySQL rounds to the column's precision. Refused:
`default(DateTime)` (`ARX1013`: it means "not set"; use `null`), and a date string MySQL can't read (`ARX4003`: ARX never
parses strings as dates). Full rules: [Data types](/csharp/mysql/data-types).

## Reading: MySQL to .NET

| MySQL column | Reads back as | Notes |
|---|---|---|
| `VARCHAR(n)`, `CHAR(n)`, `TEXT` | `string` | `CHAR(n)` drops its padding spaces when read; `""` stays `""` |
| `TINYINT` / `SMALLINT` / `INT` / `BIGINT` | `sbyte` / `short` / `int` / `long` | `TINYINT UNSIGNED` is a `byte` |
| `INT UNSIGNED`, `BIGINT UNSIGNED` | `uint`, `ulong` | |
| `DECIMAL(p, s)` | `decimal` | More than 28 significant digits: `ARX4001 ValueOutOfRange` |
| `FLOAT` / `DOUBLE` | `float` / `double` | |
| `BOOLEAN` (`TINYINT(1)`) | `bool` | |
| `CHAR(36)` | `Guid` | Text that isn't a Guid: `ARX5003 SchemaMismatch` |
| `BINARY(16)` | `byte[]`; a `Guid` for a declared Guid id | |
| `TIMESTAMP` | `DateTime`, Kind `Utc` | The instant, read in ARX's UTC session (on your own connection, in its session's zone) |
| `DATETIME` | `DateTime`, Kind `Unspecified` | The clock time as stored: MySQL keeps no zone |
| `DATE` / `TIME` | `DateOnly` / `TimeOnly` | A `TIME` past 24 hours or below zero is a `TimeSpan` |
| `BLOB`, `LONGBLOB`, `BINARY(n)` | `byte[]` | |
| `JSON` | `string` | MySQL's own form of the document |
| `ENUM`, `SET` | `string` | The label; a `SET`'s labels comma-separated |
| 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.
