Skip to main content

rupg_func/
tsm.rs

1//! The sample methods `system` and `bernoulli` of `TABLESAMPLE`, from `tsm_system.c` and `tsm_bernoulli.c`. The rows of rupg have no pages, so both methods choose rows as `bernoulli` does, with a hash of the number of the row and the seed. So a sample with `REPEATABLE` keeps other rows than PostgreSQL keeps, because those rows depend on the pages of the table.
2
3use rupg_common::{Error, Result, SqlState};
4
5use crate::{Session, hash, random};
6
7/// `BeginSampleScan` of the two methods and the choice of the rows: the numbers of the rows, of `count`, that the sample keeps. `seed` is the value of `REPEATABLE`. With `None`, the seed comes from `pg_global_prng_state`, as in `tablesample_init`.
8///
9/// # Errors
10///
11/// `2202H` when the percentage is not between 0 and 100.
12pub fn sample_rows(
13    percent: f32,
14    seed: Option<f64>,
15    count: usize,
16    session: &dyn Session,
17) -> Result<Vec<usize>> {
18    if percent.is_nan() || !(0.0..=100.0).contains(&percent) {
19        return Err(Error::new(
20            SqlState::INVALID_TABLESAMPLE_ARGUMENT,
21            "sample percentage must be between 0 and 100",
22        ));
23    }
24    let seed = match seed {
25        Some(value) => hash::float8_hash(value),
26        None => {
27            let value = random::global_range(session, 0, u64::from(u32::MAX));
28            u32::try_from(value).unwrap_or(u32::MAX)
29        }
30    };
31    // A row stays when its hash is less than `rint((PG_UINT32_MAX + 1) * percent / 100)`.
32    #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
33    let cutoff =
34        ((f64::from(u32::MAX) + 1.0) * f64::from(percent) / 100.0).round_ties_even() as u64;
35    Ok((0..count).filter(|&row| u64::from(hash::sample_hash(row as u64, seed)) < cutoff).collect())
36}