This document describes the PostgreSQL data types that sqlift supports and how they map to target language types.
| PostgreSQL Type | Python Type | Import Required |
|---|---|---|
smallint, int2 |
int |
- |
integer, int, int4 |
int |
- |
bigint, int8 |
int |
- |
boolean, bool |
bool |
- |
text |
str |
- |
varchar(n), character varying(n) |
str |
- |
char(n), character(n) |
str |
- |
real, float4 |
float |
- |
double precision, float8 |
float |
- |
numeric, decimal |
Decimal |
from decimal import Decimal |
timestamp, timestamp without time zone |
datetime |
from datetime import datetime |
timestamptz, timestamp with time zone |
datetime |
from datetime import datetime |
date |
date |
from datetime import date |
time, time without time zone |
time |
from datetime import time |
timetz, time with time zone |
time |
from datetime import time |
uuid |
UUID |
from uuid import UUID |
json |
dict[str, Any] |
from typing import Any |
jsonb |
dict[str, Any] |
from typing import Any |
bytea |
bytes |
- |
Arrays (e.g., integer[], text[]) |
list[T] |
(depends on element type) |
| Custom enum types | Generated Enum class |
from enum import Enum |
- Nullable columns use Python's union syntax:
T | None - Non-nullable columns use the base type directly
Example:
@dataclass
class UserRecord:
id: int # NOT NULL
email: str # NOT NULL
nickname: str | None # NULL allowedFor update and upsert functions, parameters use the _Unset sentinel type to support partial updates:
| Column Type | Record Field Type | Update Parameter Type |
|---|---|---|
NOT NULL |
T |
T | _Unset |
NULL |
T | None |
T | None | _Unset |
This allows distinguishing between:
- Not passing a parameter (field unchanged) - uses
UNSETdefault - Passing
None(set field to NULL) - for nullable columns - Passing a value (update the field)
Example:
def update_user(
conn: Connection,
id: int, # PK - required
email: str | _Unset = UNSET, # Non-nullable field
nickname: str | None | _Unset = UNSET, # Nullable field
) -> UserRecord | None:
...
# Usage:
update_user(conn, id=1, email="new@example.com") # Only update email
update_user(conn, id=1, nickname=None) # Set nickname to NULLPostgreSQL arrays are mapped to Python list[T] where T is the element type:
| PostgreSQL Array | Python Type |
|---|---|
integer[] |
list[int] |
text[] |
list[str] |
uuid[] |
list[UUID] |
varchar(255)[] |
list[str] |
PostgreSQL enum types are introspected and generated as Python Enum classes that inherit from both str and Enum:
-- PostgreSQL
CREATE TYPE order_status AS ENUM ('pending', 'confirmed', 'shipped', 'delivered');# Generated Python
class OrderStatus(str, Enum):
PENDING = "pending"
CONFIRMED = "confirmed"
SHIPPED = "shipped"
DELIVERED = "delivered"The str inheritance allows the enum values to be used directly in SQL queries without conversion.
The following PostgreSQL types are not currently supported. If encountered, they will be treated as custom enum types (which will likely cause issues):
money
interval- Duration type
cidr- IPv4 or IPv6 networkinet- IPv4 or IPv6 host addressmacaddr- MAC addressmacaddr8- MAC address (EUI-64 format)
pointlinelsegboxpathpolygoncircle
tsvectortsquery
bit(n)bit varying(n)
xmlpg_lsn- Log sequence numbertxid_snapshot- Transaction snapshot
- User-defined composite types
int4range,int8rangenumrangetsrange,tstzrangedaterange- Custom range types
- User-defined domain types
Tables without primary keys only generate get_all and insert functions. The following operations require a primary key and are skipped:
get_by_*- No unique identifier to look up byupdate- No PK for WHERE clausedelete- No PK for WHERE clauseupsert- No PK for ON CONFLICT
When all columns have defaults or are auto-generated (e.g., a table with only id SERIAL and created_at TIMESTAMP DEFAULT NOW()), the insert function takes no parameters and uses INSERT INTO table_name DEFAULT VALUES RETURNING *.
# Example: audit_log table with id SERIAL + created_at DEFAULT NOW()
def insert_audit_log(conn: Connection) -> AuditLogRecord:
"""Insert a new audit_log record."""
cursor = conn.execute(
"INSERT INTO audit_log DEFAULT VALUES RETURNING *",
)
row = cursor.fetchone()
return AuditLogRecord(**dict(row))To add support for a new PostgreSQL type:
- Add a new variant to the
DataTypeenum insrc/schema.rs - Update the
parse_data_typefunction insrc/introspect/postgres.rs - Add the Python type mapping in
src/codegen/python/mod.rs:- Update
python_type()function - Update
collect_type_imports()if an import is needed
- Update
- Update this documentation
numeric(p, s)anddecimal(p, s)precision/scale parameters are recognized during parsing but not used in the Python type (always maps toDecimal)varchar(n)length is captured but not enforced in Python types
- Both
timestampandtimestamptzmap to Python'sdatetime - Both
timeandtimetzmap to Python'stime - Time zone handling is left to the application layer
- Both
jsonandjsonbmap todict[str, Any] - The difference (storage format, indexing) is a PostgreSQL implementation detail