const BPCHAR: u32 = 1042;
const VARCHAR: u32 = 1043;
const NUMERIC: u32 = 1700;
const BIT: u32 = 1560;
const VARBIT: u32 = 1562;
const VARHDRSZ: i32 = 4;
const MAX_CHAR_BYTES: i32 = 4;
const TUPLE_HEADER: i32 = 23;
const TOAST_TUPLE_THRESHOLD: i32 = 2032;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Shape {
pub len: i16,
pub align: u8,
pub storage: u8,
pub ty: u32,
pub typmod: i32,
}
pub fn type_maximum_size(ty: u32, typmod: i32) -> Option<i32> {
if typmod < 0 {
return None;
}
match ty {
BPCHAR | VARCHAR => Some((typmod - VARHDRSZ) * MAX_CHAR_BYTES + VARHDRSZ),
NUMERIC => {
if typmod < VARHDRSZ {
return None;
}
let precision = ((typmod - VARHDRSZ) >> 16) & 0xffff;
let digits = (precision + 2 * 3) / 4;
Some(8 + digits * 2)
}
BIT | VARBIT => Some((typmod + 7) / 8 + 8),
_ => None,
}
}
fn align_to(length: i32, align: u8) -> i32 {
let to = match align {
b'd' => 8,
b'i' => 4,
b's' => 2,
_ => 1,
};
(length + to - 1) / to * to
}
fn max_align(length: i32) -> i32 {
align_to(length, b'd')
}
pub fn needs_toast(columns: &[Shape]) -> bool {
let mut data_length = 0;
let mut length_unknown = false;
let mut toastable = false;
for column in columns {
data_length = align_to(data_length, column.align);
if column.len > 0 {
data_length += i32::from(column.len);
continue;
}
match type_maximum_size(column.ty, column.typmod) {
Some(size) => data_length += size,
None => length_unknown = true,
}
if column.storage != b'p' {
toastable = true;
}
}
if !toastable {
return false;
}
if length_unknown {
return true;
}
let natts = i32::try_from(columns.len()).unwrap_or(i32::MAX);
let tuple_length = max_align(TUPLE_HEADER + (natts + 7) / 8) + max_align(data_length);
tuple_length > TOAST_TUPLE_THRESHOLD
}