// Row metadata and typed row access helpers.
///|
/// Metadata describing one result column.
///
/// A `Column` combines protocol-level row-description fields with the driver's
/// resolved `Type` descriptor so higher-level code can inspect both raw wire
/// details and semantic type information.
pub struct Column {
/// Column label chosen by the query.
name : String
/// Source table OID, or `0` when PostgreSQL cannot provide one.
table_oid : @proto.Oid
/// Source column number inside the table, or `0` when unavailable.
column_id : Int
/// Resolved PostgreSQL type descriptor.
type_ : Type
/// Storage size reported by PostgreSQL, or `-1` for variable-width types.
type_size : Int
/// Type modifier reported by PostgreSQL.
type_modifier : Int
/// Wire format used for this column in the current result stream.
format : WireFormat
} derive(Debug, Eq)
///|
/// One fully materialized row from an extended query.
///
/// The row stores its column metadata alongside the raw field payloads so each
/// `get` call can validate the target type, decode on demand, and still support
/// low-level access through `get_raw`.
pub struct Row {
/// Column metadata for each position.
columns : Array[Column]
/// Raw field payloads. `None` represents SQL `NULL`.
values : Array[Bytes?]
} derive(Debug, Eq)
///|
/// Return the number of values in this row.
pub fn Row::len(self : Row) -> Int {
self.values.length()
}
///|
/// Return the index of the named column, if present.
///
/// Column matching is exact and scans from left to right, so the first matching
/// name wins when a query produces duplicate column labels.
pub fn Row::index_of(self : Row, name : String) -> Int? {
for index, column in self.columns {
if column.name == name {
return Some(index)
}
}
None
}
///|
/// Return the raw field bytes at `index`.
///
/// This is the escape hatch for callers that want to perform custom decoding or
/// inspect values that do not yet have a `FromSql` implementation.
pub fn Row::get_raw(self : Row, index : Int) -> Bytes? {
self.values[index]
}
///|
/// Return the raw field bytes for the named column.
pub fn Row::get_raw_name(self : Row, name : String) -> Bytes? raise {
match self.index_of(name) {
None => raise ClientError::ColumnNotFound(name)
Some(index) => self.get_raw(index)
}
}
///|
/// Decode the field at `index` as `T`.
///
/// The compatibility check happens before any decoder code runs, so a custom
/// `FromSql` implementation can rely on receiving a PostgreSQL type it claimed
/// to support.
pub fn[T : FromSql] Row::get(self : Row, index : Int) -> T raise {
let column = self.columns[index]
guard T::accepts(column.type_) else {
raise wrong_type_error(T::moonbit_type_name(), column.type_)
}
match self.values[index] {
None => FromSql::from_sql_null(column.type_, column.format)
Some(value) => FromSql::from_sql(column.type_, column.format, value[:])
}
}
///|
/// Decode the named column as `T`.
pub fn[T : FromSql] Row::get_name(self : Row, name : String) -> T raise {
match self.index_of(name) {
None => raise ClientError::ColumnNotFound(name)
Some(index) => self.get(index)
}
}
///|
/// Decode the field at `index` as `T`, returning `None` when out of range.
pub fn[T : FromSql] Row::try_get(self : Row, index : Int) -> T? raise {
if index < 0 || index >= self.len() {
return None
}
Some(self.get(index))
}
///|
/// Decode the named column as `T`, returning `None` when the column is absent.
pub fn[T : FromSql] Row::try_get_name(self : Row, name : String) -> T? raise {
match self.index_of(name) {
None => None
Some(index) => Some(self.get(index))
}
}
///|
/// Decode the field at `index` as nullable text.
///
/// This is shorthand for `self.get[String?](index)` and therefore preserves SQL
/// `NULL` as `None`.
pub fn Row::get_text(self : Row, index : Int) -> String? raise {
self.get(index)
}
///|
/// Decode the named field as nullable text.
pub fn Row::get_text_name(self : Row, name : String) -> String? raise {
self.get_name(name)
}