# postgres.text

> A module of postgres's API.

`postgres.text` — the PostgreSQL text-format codec.

libpq hands back every result cell as text (`PQgetvalue` never returns binary
unless the caller asked for binary-format results, which this tin does not).
This module is the two-way bridge between that text and Mojo values:
`decode_*` turns a column's text into a typed value given its OID; `encode_*`
turns a Mojo value back into the text `PQexecParams`/`COPY` expect. Pure
Mojo — no FFI, no libpq call, so it is usable and testable without a server.

Dates and timestamps decode to integers (days/microseconds since the Unix
epoch, 1970-01-01) using the proleptic Gregorian calendar via Howard
Hinnant's `days_from_civil` / `civil_from_days` — see
http://howardhinnant.github.io/date_algorithms.html. `numeric` deliberately
stays a `String`: coercing arbitrary-precision decimals to `Float64` would
silently lose precision, so callers that want a number parse it themselves.
`bytea` decodes both text sub-formats libpq can emit — hex (the
`bytea_output=hex` server default) and the legacy octal-escape format — so no
`PQunescapeBytea` call is needed either.

## Functions

### `oid_name`

```mojo
def oid_name(oid: UInt32) -> String
```

Return the PostgreSQL type name for a well-known OID, or `"oid:<n>"` for anything this tin does not special-case.

### `is_text_oid`

```mojo
def is_text_oid(oid: UInt32) -> Bool
```

True for OIDs whose natural Mojo representation is `String` — TEXT/VARCHAR/BPCHAR/UUID/JSON/JSONB, and NUMERIC (kept a `String` on purpose; see the module docstring).

### `decode_bool`

```mojo
def decode_bool(s: StringSpan) -> Bool
```

Decode `bool` text. The server always emits `t`/`f`; `true`/`false` are also accepted because COPY CSV round-trips can carry them.

### `encode_bool`

```mojo
def encode_bool(v: Bool) -> String
```

### `decode_int16`

```mojo
def decode_int16(s: StringSpan) -> Int16
```

### `decode_int32`

```mojo
def decode_int32(s: StringSpan) -> Int32
```

### `decode_int64`

```mojo
def decode_int64(s: StringSpan) -> Int64
```

### `encode_int16`

```mojo
def encode_int16(v: Int16) -> String
```

### `encode_int32`

```mojo
def encode_int32(v: Int32) -> String
```

### `encode_int64`

```mojo
def encode_int64(v: Int64) -> String
```

### `decode_float32`

```mojo
def decode_float32(s: StringSpan) -> Float32
```

`atof` already accepts PostgreSQL's `NaN`/`Infinity`/`-Infinity` spellings case-insensitively (and the `inf`/`nan` short forms), plus standard decimal/scientific notation, so this is a thin wrapper that narrows to `Float32` and rewraps the error with the offending text.

### `decode_float64`

```mojo
def decode_float64(s: StringSpan) -> Float64
```

### `encode_float32`

```mojo
def encode_float32(v: Float32) -> String
```

### `encode_float64`

```mojo
def encode_float64(v: Float64) -> String
```

### `decode_bytea`

```mojo
def decode_bytea(s: StringSpan) -> List[UInt8]
```

Decode `bytea` text — either the `\x`-prefixed hex format (`bytea_output=hex`, the server default) or the legacy escape format (`\\` for a literal backslash, `\ooo` three-octal-digit byte escapes, every other byte literal).

### `encode_bytea`

```mojo
def encode_bytea(data: Span[UInt8]) -> String
```

`\x` + lowercase hex — the format `bytea_output=hex` expects.

### `is_infinite`

```mojo
def is_infinite(s: StringSpan) -> Bool
```

True for the literal spellings PostgreSQL uses for the unbounded date/timestamp values, `infinity` and `-infinity`.

### `decode_date`

```mojo
def decode_date(s: StringSpan) -> Int32
```

Decode `date` text (`YYYY-MM-DD`, year may exceed 4 digits) to days since 1970-01-01. `infinity`/`-infinity` and a ` BC` suffix raise — read `Result.value()`'s raw text for those instead of the epoch encoding.

### `decode_time`

```mojo
def decode_time(s: StringSpan) -> Int64
```

Decode `time` text (`HH:MM:SS[.ffffff]`) to microseconds since midnight.

### `decode_timestamp`

```mojo
def decode_timestamp(s: StringSpan) -> Int64
```

Decode naive `timestamp` text (`YYYY-MM-DD HH:MM:SS[.ffffff]`, no zone) to microseconds since 1970-01-01 00:00:00.

### `decode_timestamptz`

```mojo
def decode_timestamptz(s: StringSpan) -> Int64
```

Decode zoned `timestamptz` text (`YYYY-MM-DD HH:MM:SS[.ffffff]` plus an offset `+HH`, `+HH:MM`, or `+HH:MM:SS`, either sign) to UTC microseconds since 1970-01-01 00:00:00.

### `encode_date`

```mojo
def encode_date(days: Int32) -> String
```

### `encode_time`

```mojo
def encode_time(micros: Int64) -> String
```

### `encode_timestamp`

```mojo
def encode_timestamp(micros: Int64) -> String
```

Always 6 fractional digits: `YYYY-MM-DD HH:MM:SS.ffffff`. Negative `micros` (dates before 1970) work because `//`/`%` are floor division/modulo.

### `encode_timestamptz`

```mojo
def encode_timestamptz(micros: Int64) -> String
```

Same as `encode_timestamp`, with a `+00` suffix — `micros` is already UTC, so there is no original offset to restore.

### `decode_text`

```mojo
def decode_text(s: StringSpan) -> String
```

Identity decode, so a dispatch-by-OID table can call every decoder uniformly.

## Aliases

### `OID_BOOL`

```mojo
comptime OID_BOOL = UInt32(16)
```

### `OID_BYTEA`

```mojo
comptime OID_BYTEA = UInt32(17)
```

### `OID_INT8`

```mojo
comptime OID_INT8 = UInt32(20)
```

### `OID_INT2`

```mojo
comptime OID_INT2 = UInt32(21)
```

### `OID_INT4`

```mojo
comptime OID_INT4 = UInt32(23)
```

### `OID_TEXT`

```mojo
comptime OID_TEXT = UInt32(25)
```

### `OID_JSON`

```mojo
comptime OID_JSON = UInt32(114)
```

### `OID_FLOAT4`

```mojo
comptime OID_FLOAT4 = UInt32(700)
```

### `OID_FLOAT8`

```mojo
comptime OID_FLOAT8 = UInt32(701)
```

### `OID_BPCHAR`

```mojo
comptime OID_BPCHAR = UInt32(1042)
```

### `OID_VARCHAR`

```mojo
comptime OID_VARCHAR = UInt32(1043)
```

### `OID_DATE`

```mojo
comptime OID_DATE = UInt32(1082)
```

### `OID_TIME`

```mojo
comptime OID_TIME = UInt32(1083)
```

### `OID_TIMESTAMP`

```mojo
comptime OID_TIMESTAMP = UInt32(1114)
```

### `OID_TIMESTAMPTZ`

```mojo
comptime OID_TIMESTAMPTZ = UInt32(1184)
```

### `OID_NUMERIC`

```mojo
comptime OID_NUMERIC = UInt32(1700)
```

### `OID_UUID`

```mojo
comptime OID_UUID = UInt32(2950)
```

### `OID_JSONB`

```mojo
comptime OID_JSONB = UInt32(3802)
```
