use crate::toast::type_maximum_size;
const BLOCK: i64 = 8192;
const PAGE_HEADER: i64 = 24;
const HEAP_OVERHEAD: i64 = 28;
const MIN_PAGES: f64 = 10.0;
const HASH_FILLFACTOR: i64 = 75;
const HASH_ITEM: i64 = 20;
const HASH_MAX_BUCKETS: u32 = 0x4000_0000;
const ONE_PHASE_GROUPS: u32 = 10;
const PHASE_BITS: u32 = 2;
const PHASE_MASK: u32 = (1 << PHASE_BITS) - 1;
const BPCHAR: u32 = 1042;
const HASH_AM: u32 = 405;
const GIN_AM: u32 = 2742;
const SPGIST_AM: u32 = 4000;
const BRIN_AM: u32 = 3580;
pub fn typavgwidth(len: i16, ty: u32, typmod: i32) -> i32 {
if len > 0 {
return i32::from(len);
}
match type_maximum_size(ty, typmod) {
Some(max) if max > 0 => {
if ty == BPCHAR || max <= 32 {
max
} else if max < 1000 {
32 + (max - 32) / 2
} else {
32 + (1000 - 32) / 2
}
}
_ => 32,
}
}
pub fn new_table_tuples(widths: impl IntoIterator<Item = i32>, fillfactor: i32) -> f64 {
let width = widths.into_iter().map(i64::from).sum::<i64>() + HEAP_OVERHEAD;
let density = (BLOCK - PAGE_HEADER) * i64::from(fillfactor) / 100 / width;
(density.max(1) as f64 * MIN_PAGES).round()
}
pub fn empty_index_pages(method: u32, tuples: f64) -> i32 {
match method {
HASH_AM => i32::try_from(hash_buckets(tuples) + 2).unwrap_or(i32::MAX),
GIN_AM => 2,
SPGIST_AM | BRIN_AM => 3,
_ => 1,
}
}
fn hash_buckets(tuples: f64) -> u32 {
let ffactor = (BLOCK * HASH_FILLFACTOR / 100 / HASH_ITEM).max(10);
let buckets = tuples / ffactor as f64;
if buckets <= 2.0 {
2
} else if buckets >= f64::from(HASH_MAX_BUCKETS) {
HASH_MAX_BUCKETS
} else {
total_buckets(spare_index(buckets as u32))
}
}
fn spare_index(buckets: u32) -> u32 {
let group = if buckets < 2 { 0 } else { 32 - (buckets - 1).leading_zeros() };
if group < ONE_PHASE_GROUPS {
return group;
}
ONE_PHASE_GROUPS
+ ((group - ONE_PHASE_GROUPS) << PHASE_BITS)
+ (((buckets - 1) >> (group - (PHASE_BITS + 1))) & PHASE_MASK)
}
fn total_buckets(phase: u32) -> u32 {
if phase < ONE_PHASE_GROUPS {
return 1 << phase;
}
let group = ONE_PHASE_GROUPS + ((phase - ONE_PHASE_GROUPS) >> PHASE_BITS);
let within = ((phase - ONE_PHASE_GROUPS) & PHASE_MASK) + 1;
(1 << (group - 1)) + ((1 << (group - 1)) >> PHASE_BITS) * within
}