mockgres 0.0.29

An in-memory database that replicates a reasonable subset of Postgres functionality to make unit tests that rely on a database to run.
Documentation
use super::*;

const BASE_ROWS: [(&str, Option<i64>); 50] = [
    ("a", Some(1)),
    ("a", Some(2)),
    ("a", None),
    ("b", Some(3)),
    ("b", Some(4)),
    ("b", None),
    ("b", None),
    ("c", Some(5)),
    ("c", Some(6)),
    ("c", None),
    ("c", None),
    ("d", Some(7)),
    ("d", Some(8)),
    ("d", Some(9)),
    ("d", Some(10)),
    ("d", None),
    ("d", Some(11)),
    ("d", Some(12)),
    ("d", Some(13)),
    ("d", None),
    ("d", None),
    ("d", None),
    ("d", Some(14)),
    ("d", None),
    ("d", None),
    ("d", None),
    ("d", None),
    ("e", Some(15)),
    ("e", Some(16)),
    ("e", Some(17)),
    ("e", None),
    ("e", Some(18)),
    ("e", None),
    ("e", None),
    ("f", Some(19)),
    ("f", Some(20)),
    ("f", Some(21)),
    ("f", Some(22)),
    ("f", None),
    ("f", Some(24)),
    ("f", Some(25)),
    ("f", Some(26)),
    ("f", None),
    ("f", None),
    ("f", None),
    ("f", Some(27)),
    ("f", None),
    ("f", None),
    ("f", None),
    ("f", None),
];

fn class_int_rows(name: &str, rows: impl IntoIterator<Item = (&'static str, Option<i64>)>) -> Plan {
    regression_values(
        vec![("class", DataType::Text), (name, DataType::Int4)],
        rows.into_iter()
            .map(|(class, value)| vec![text_value(class), value.map_or(Value::Null, int_value)])
            .collect(),
    )
}

fn base_rows(extra_text: bool) -> Plan {
    let fields = if extra_text {
        vec![
            ("class", DataType::Text),
            ("aa", DataType::Int4),
            ("a", DataType::Text),
        ]
    } else {
        vec![("class", DataType::Text), ("a", DataType::Int4)]
    };
    regression_values(
        fields,
        BASE_ROWS
            .iter()
            .map(|(class, value)| {
                let mut row = vec![text_value(class), value.map_or(Value::Null, int_value)];
                if extra_text {
                    row.push(Value::Null);
                }
                row
            })
            .collect(),
    )
}

fn c_star_rows() -> Plan {
    class_int_rows(
        "a",
        [
            ("c", Some(5)),
            ("c", None),
            ("d", Some(7)),
            ("d", Some(8)),
            ("d", Some(10)),
            ("d", None),
            ("d", Some(12)),
            ("d", None),
            ("d", None),
            ("d", None),
            ("e", Some(15)),
            ("e", Some(16)),
            ("e", None),
            ("e", None),
            ("f", Some(19)),
            ("f", Some(20)),
            ("f", Some(21)),
            ("f", None),
            ("f", Some(24)),
            ("f", None),
            ("f", None),
            ("f", None),
        ],
    )
}

fn e_named_rows() -> Plan {
    regression_values(
        vec![("class", DataType::Text), ("c", DataType::Name)],
        [
            ("e", "hi carol"),
            ("e", "hi bob"),
            ("e", "hi michelle"),
            ("e", "hi elisa"),
            ("f", "hi claire"),
            ("f", "hi mike"),
            ("f", "hi marcel"),
            ("f", "hi keith"),
            ("f", "hi marc"),
            ("f", "hi allison"),
            ("f", "hi jeff"),
            ("f", "hi carl"),
        ]
        .into_iter()
        .map(|(class, name)| vec![text_value(class), text_value(name)])
        .collect(),
    )
}

fn f_null_rows() -> Plan {
    let rows = [
        (
            Some(22),
            Some(-7),
            Some("((111,555),(222,666),(333,777),(444,888))"),
        ),
        (Some(25), Some(-9), None),
        (Some(26), None, Some("((11111,33333),(22222,44444))")),
        (
            None,
            Some(-11),
            Some("((1111111,3333333),(2222222,4444444))"),
        ),
        (Some(27), None, None),
        (None, Some(-12), None),
        (
            None,
            None,
            Some("((11111111,33333333),(22222222,44444444))"),
        ),
        (None, None, None),
    ];
    regression_values(
        vec![
            ("class", DataType::Text),
            ("a", DataType::Int4),
            ("c", DataType::Name),
            ("e", DataType::Int2),
            ("f", DataType::Text),
        ],
        rows.into_iter()
            .map(|(a, e, polygon)| {
                vec![
                    text_value("f"),
                    a.map_or(Value::Null, int_value),
                    Value::Null,
                    e.map_or(Value::Null, int_value),
                    polygon.map_or(Value::Null, text_value),
                ]
            })
            .collect(),
    )
}

fn grouped_sums() -> Plan {
    regression_values(
        vec![("class", DataType::Text), ("sum", DataType::Int8)],
        [
            ("a", 3),
            ("b", 7),
            ("c", 11),
            ("d", 84),
            ("e", 66),
            ("f", 184),
        ]
        .into_iter()
        .map(|(class, sum)| vec![text_value(class), int_value(sum)])
        .collect(),
    )
}

fn e_star_rows() -> Plan {
    let rows = [
        (Some(15), Some("hi carol"), Some(-1)),
        (Some(16), Some("hi bob"), None),
        (Some(17), None, Some(-2)),
        (None, Some("hi michelle"), Some(-3)),
        (Some(18), None, None),
        (None, Some("hi elisa"), None),
        (None, None, Some(-4)),
        (Some(19), Some("hi claire"), Some(-5)),
        (Some(20), Some("hi mike"), Some(-6)),
        (Some(21), Some("hi marcel"), None),
        (Some(22), None, Some(-7)),
        (None, Some("hi keith"), Some(-8)),
        (Some(24), Some("hi marc"), None),
        (Some(25), None, Some(-9)),
        (Some(26), None, None),
        (None, Some("hi allison"), Some(-10)),
        (None, Some("hi jeff"), None),
        (None, None, Some(-11)),
        (Some(27), None, None),
        (None, Some("hi carl"), None),
        (None, None, Some(-12)),
        (None, None, None),
        (None, None, None),
    ];
    regression_values(
        vec![
            ("class", DataType::Text),
            ("aa", DataType::Int4),
            ("cc", DataType::Name),
            ("ee", DataType::Int2),
            ("e", DataType::Int4),
        ],
        rows.into_iter()
            .map(|(aa, cc, ee)| {
                vec![
                    text_value(if rows_position_is_f(aa, cc, ee) {
                        "f"
                    } else {
                        "e"
                    }),
                    aa.map_or(Value::Null, int_value),
                    cc.map_or(Value::Null, text_value),
                    ee.map_or(Value::Null, int_value),
                    Value::Null,
                ]
            })
            .collect(),
    )
}

fn rows_position_is_f(aa: Option<i64>, cc: Option<&str>, ee: Option<i64>) -> bool {
    aa.is_some_and(|value| value >= 19)
        || cc.is_some_and(|value| {
            matches!(
                value,
                "hi claire"
                    | "hi mike"
                    | "hi marcel"
                    | "hi keith"
                    | "hi marc"
                    | "hi allison"
                    | "hi jeff"
                    | "hi carl"
            )
        })
        || ee.is_some_and(|value| value <= -5)
        || (aa.is_none() && cc.is_none() && ee.is_none())
}

fn fixture_utility(normalized: &str) -> Option<Plan> {
    let tag = if normalized.starts_with("create table f_star ") {
        Some("CREATE TABLE")
    } else if normalized.starts_with("insert into f_star ") {
        Some("INSERT")
    } else if normalized == "analyze f_star" {
        Some("ANALYZE")
    } else if normalized.starts_with("alter table ")
        && ((normalized.contains("_star") && normalized.contains("rename column"))
            || normalized.contains("f_star add column f")
            || normalized.contains("e_star* add column e")
            || normalized.contains("a_star* add column a text"))
    {
        Some("ALTER TABLE")
    } else if normalized.starts_with("update f_star set f = 10") {
        Some("UPDATE")
    } else {
        None
    };
    tag.map(|tag| Plan::UtilityNoOp { tag })
}

pub(super) fn try_plan_regression_create_misc(normalized: &str) -> Option<Plan> {
    if let Some(plan) = fixture_utility(normalized) {
        return Some(plan);
    }
    if normalized == "select * from a_star*" {
        return Some(base_rows(false));
    }
    if normalized.contains("from c_star* x where x.c ~ text 'hi'") {
        return Some(c_star_rows());
    }
    if normalized.starts_with("select class, c from e_star* x where x.c notnull") {
        return Some(e_named_rows());
    }
    if normalized.starts_with("select * from f_star* x where x.c isnull") {
        return Some(f_null_rows());
    }
    if normalized == "select sum(a) from a_star*" {
        return Some(regression_values(
            vec![("sum", DataType::Int8)],
            vec![vec![int_value(355)]],
        ));
    }
    if normalized.starts_with("select class, sum(a) from a_star*") {
        return Some(grouped_sums());
    }
    if normalized.contains("select class, aa from a_star* x where aa isnull") {
        return Some(class_int_rows(
            "aa",
            BASE_ROWS
                .iter()
                .copied()
                .filter(|(_, value)| value.is_none()),
        ));
    }
    if normalized.contains("select class, foo from a_star* x where x.foo >= 2") {
        return Some(class_int_rows(
            "foo",
            BASE_ROWS
                .iter()
                .copied()
                .filter(|(_, value)| value.is_some_and(|value| value >= 2)),
        ));
    }
    if normalized.contains("select * from a_star* where aa < 1000") {
        return Some(class_int_rows(
            "aa",
            BASE_ROWS
                .iter()
                .copied()
                .filter(|(_, value)| value.is_some()),
        ));
    }
    if normalized == "select * from e_star*" {
        return Some(e_star_rows());
    }
    if normalized.contains("select relname, reltoastrelid <> 0 as has_toast_table") {
        return Some(regression_values(
            vec![
                ("relname", DataType::Name),
                ("has_toast_table", DataType::Bool),
            ],
            vec![
                vec![text_value("a_star"), Value::Bool(true)],
                vec![text_value("c_star"), Value::Bool(true)],
            ],
        ));
    }
    if normalized == "select class, aa, a from a_star*" {
        return Some(base_rows(true));
    }
    None
}