extern crate alloc;
mod generated {
#![allow(missing_docs)]
include!(concat!(env!("OUT_DIR"), "/limits_generated.rs"));
}
pub use generated::{Limit, LimitRow, LIMIT_ROWS};
impl Limit {
pub fn row(self) -> &'static LimitRow {
match LIMIT_ROWS.iter().find(|row| row.limit == self) {
Some(row) => row,
None => &UNRECOGNISED_LIMIT_ROW,
}
}
pub fn default_value(self) -> i64 {
self.row().default
}
pub fn hard_max(self) -> i64 {
self.row().hard_max
}
pub fn minimum(self) -> i64 {
self.row().minimum
}
pub fn c_name(self) -> &'static str {
self.row().c_name
}
pub fn clamp(self, requested: i64) -> i64 {
requested.clamp(self.minimum(), self.hard_max())
}
}
static UNRECOGNISED_LIMIT_ROW: LimitRow = LimitRow {
limit: Limit::Length,
c_name: "SQLITE_LIMIT_LENGTH",
default: 1_000_000_000,
hard_max: 2_147_483_645,
minimum: 0,
description: "Maximum size of any string, BLOB, or table row in bytes.",
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Limits {
values: alloc::sync::Arc<[i64]>,
}
impl Default for Limits {
fn default() -> Limits {
Limits {
values: LIMIT_ROWS
.iter()
.map(|row| row.default)
.collect::<Vec<i64>>()
.into(),
}
}
}
impl Limits {
pub fn get(&self, limit: Limit) -> i64 {
match self
.index_of(limit)
.and_then(|index| self.values.get(index))
{
Some(value) => *value,
None => limit.default_value(),
}
}
pub fn set(&mut self, limit: Limit, requested: i64) -> i64 {
let previous = self.get(limit);
if requested < 0 {
return previous;
}
if let Some(index) = self.index_of(limit) {
let mut owned = self.values.to_vec();
if let Some(slot) = owned.get_mut(index) {
*slot = limit.clamp(requested);
}
self.values = owned.into();
}
previous
}
pub fn permits_length(&self, length: u64) -> bool {
length <= self.get(Limit::Length).max(0) as u64
}
fn index_of(&self, limit: Limit) -> Option<usize> {
LIMIT_ROWS.iter().position(|row| row.limit == limit)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_match_the_reference_build() {
let limits = Limits::default();
assert_eq!(limits.get(Limit::Length), 1_000_000_000);
assert_eq!(limits.get(Limit::Column), 2000);
assert_eq!(limits.get(Limit::VariableNumber), 32_766);
assert_eq!(limits.get(Limit::Attached), 10);
assert_eq!(limits.get(Limit::FunctionArg), 1000);
}
#[test]
fn setting_returns_the_previous_value_and_clamps() {
let mut limits = Limits::default();
assert_eq!(limits.set(Limit::Column, 50), 2000);
assert_eq!(limits.get(Limit::Column), 50);
assert_eq!(limits.set(Limit::Column, 1_000_000), 50);
assert_eq!(limits.get(Limit::Column), Limit::Column.hard_max());
assert_eq!(limits.set(Limit::Column, 0), Limit::Column.hard_max());
assert_eq!(limits.get(Limit::Column), Limit::Column.minimum());
}
#[test]
fn a_negative_request_only_queries() {
let mut limits = Limits::default();
assert_eq!(limits.set(Limit::Attached, -1), 10);
assert_eq!(limits.get(Limit::Attached), 10);
}
#[test]
fn every_row_has_a_coherent_range() {
for row in LIMIT_ROWS.iter() {
assert!(row.minimum <= row.hard_max, "{}", row.c_name);
assert!(row.default >= row.minimum, "{}", row.c_name);
assert!(row.default <= row.hard_max, "{}", row.c_name);
assert_eq!(row.limit.c_name(), row.c_name);
}
}
}