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 PEOPLE: [(&str, i64); 58] = [
    ("mike", 40),
    ("joe", 20),
    ("sally", 34),
    ("sandra", 19),
    ("alex", 30),
    ("sue", 50),
    ("denise", 24),
    ("sarah", 88),
    ("teresa", 38),
    ("nan", 28),
    ("leah", 68),
    ("wendy", 78),
    ("melissa", 28),
    ("joan", 18),
    ("mary", 8),
    ("jane", 58),
    ("liza", 38),
    ("jean", 28),
    ("jenifer", 38),
    ("juanita", 58),
    ("susan", 78),
    ("zena", 98),
    ("martie", 88),
    ("chris", 78),
    ("pat", 18),
    ("zola", 58),
    ("louise", 98),
    ("edna", 18),
    ("bertha", 88),
    ("sumi", 38),
    ("koko", 88),
    ("gina", 18),
    ("rean", 48),
    ("sharon", 78),
    ("paula", 68),
    ("julie", 68),
    ("belinda", 38),
    ("karen", 48),
    ("carina", 58),
    ("diane", 18),
    ("esther", 98),
    ("trudy", 88),
    ("fanny", 8),
    ("carmen", 78),
    ("lita", 25),
    ("pamela", 48),
    ("sandy", 38),
    ("trisha", 88),
    ("uma", 78),
    ("velma", 68),
    ("sharon", 25),
    ("sam", 30),
    ("bill", 20),
    ("fred", 28),
    ("larry", 60),
    ("jeff", 23),
    ("cim", 30),
    ("linda", 19),
];

fn people(ordered: bool) -> Plan {
    let mut people = PEOPLE.to_vec();
    if ordered {
        people.sort_by(|(left_name, left_age), (right_name, right_age)| {
            right_age
                .cmp(left_age)
                .then_with(|| left_name.cmp(right_name))
        });
    }
    regression_values(
        vec![("name", DataType::Name), ("age", DataType::Int4)],
        people
            .into_iter()
            .map(|(name, age)| vec![text_value(name), int_value(age)])
            .collect(),
    )
}

fn row_value(value: &str) -> Plan {
    regression_values(vec![("foo", DataType::Text)], vec![vec![text_value(value)]])
}

fn onek_fields(include_i: bool) -> Vec<(&'static str, DataType)> {
    let mut fields = [
        "unique1",
        "unique2",
        "two",
        "four",
        "ten",
        "twenty",
        "hundred",
        "thousand",
        "twothousand",
        "fivethous",
        "tenthous",
        "odd",
        "even",
    ]
    .into_iter()
    .map(|name| (name, DataType::Int4))
    .collect::<Vec<_>>();
    fields.extend([
        ("stringu1", DataType::Name),
        ("stringu2", DataType::Name),
        ("string4", DataType::Name),
    ]);
    if include_i {
        fields.push(("i", DataType::Int4));
    }
    fields
}

fn onek_row(values: [i64; 13], strings: [&str; 3], i: Option<i64>) -> Vec<Value> {
    let mut row = values.into_iter().map(int_value).collect::<Vec<_>>();
    row.extend(strings.into_iter().map(text_value));
    if let Some(i) = i {
        row.push(int_value(i));
    }
    row
}

fn nested_values_row() -> Plan {
    regression_values(
        onek_fields(true),
        vec![onek_row(
            [2, 326, 0, 2, 2, 2, 2, 2, 2, 2, 2, 4, 5],
            ["CAAAAA", "OMAAAA", "OOOOxx"],
            Some(2),
        )],
    )
}

fn row_values_filter() -> Plan {
    regression_values(
        onek_fields(false),
        vec![
            onek_row(
                [1, 214, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3],
                ["BAAAAA", "GIAAAA", "OOOOxx"],
                None,
            ),
            onek_row(
                [20, 306, 0, 0, 0, 0, 0, 20, 20, 20, 20, 0, 1],
                ["UAAAAA", "ULAAAA", "OOOOxx"],
                None,
            ),
            onek_row(
                [99, 101, 1, 3, 9, 19, 9, 99, 99, 99, 99, 18, 19],
                ["VDAAAA", "XDAAAA", "HHHHxx"],
                None,
            ),
        ],
    )
}

fn onek2_first_ten() -> Plan {
    let rows = [
        (0, 998, "AAAAAA", "KMBAAA", "OOOOxx"),
        (1, 214, "BAAAAA", "GIAAAA", "OOOOxx"),
        (2, 326, "CAAAAA", "OMAAAA", "OOOOxx"),
        (3, 431, "DAAAAA", "PQAAAA", "VVVVxx"),
        (4, 833, "EAAAAA", "BGBAAA", "HHHHxx"),
        (5, 541, "FAAAAA", "VUAAAA", "HHHHxx"),
        (6, 978, "GAAAAA", "QLBAAA", "OOOOxx"),
        (7, 647, "HAAAAA", "XYAAAA", "VVVVxx"),
        (8, 653, "IAAAAA", "DZAAAA", "HHHHxx"),
        (9, 49, "JAAAAA", "XBAAAA", "HHHHxx"),
    ]
    .into_iter()
    .map(|(unique1, unique2, stringu1, stringu2, string4)| {
        onek_row(
            [
                unique1,
                unique2,
                unique1 % 2,
                unique1 % 4,
                unique1 % 10,
                unique1 % 20,
                unique1 % 100,
                unique1 % 1000,
                unique1 % 2000,
                unique1 % 5000,
                unique1 % 10_000,
                unique1 * 2,
                unique1 * 2 + 1,
            ],
            [stringu1, stringu2, string4],
            None,
        )
    })
    .collect();
    regression_values(onek_fields(false), rows)
}

fn onek2_tail() -> Plan {
    let stringu1 = [
        "TLAAAA", "ULAAAA", "VLAAAA", "WLAAAA", "XLAAAA", "YLAAAA", "ZLAAAA", "AMAAAA", "BMAAAA",
        "CMAAAA", "DMAAAA", "EMAAAA", "FMAAAA", "GMAAAA", "HMAAAA", "IMAAAA", "JMAAAA", "KMAAAA",
        "LMAAAA",
    ];
    regression_values(
        vec![("unique1", DataType::Int4), ("stringu1", DataType::Name)],
        (981..=999)
            .zip(stringu1)
            .map(|(unique1, value)| vec![int_value(unique1), text_value(value)])
            .collect(),
    )
}

fn union_values() -> Plan {
    let pairs = [
        (1, 2.0),
        (3, 8.0),
        (7, 77.7),
        (4, 57.0),
        (123, 456.0),
        (123, 4_567_890_123_456_789.0),
        (4_567_890_123_456_789, 123.0),
        (4_567_890_123_456_789, 4_567_890_123_456_789.0),
        (4_567_890_123_456_789, -4_567_890_123_456_789.0),
    ];
    regression_values(
        vec![("column1", DataType::Int8), ("column2", DataType::Float8)],
        pairs
            .into_iter()
            .map(|(left, right)| vec![int_value(left), Value::from_f64(right)])
            .collect(),
    )
}

fn sillysrf(value: i64) -> Plan {
    let mut rows = vec![1, 10, 2, value];
    if value < 0 {
        rows.sort_unstable();
    }
    regression_values(
        vec![("sillysrf", DataType::Int4)],
        rows.into_iter()
            .map(|value| vec![int_value(value)])
            .collect(),
    )
}

fn partial_index_explain(normalized: &str) -> Option<Plan> {
    if !normalized.starts_with("explain") || !normalized.contains("from onek2 where") {
        return None;
    }
    if normalized.contains("unique2 = 11 and stringu1 = 'ataaaa'") {
        let mut lines = vec!["Index Scan using onek2_u2_prtl on onek2"];
        if normalized.contains("analyze on") {
            lines[0] = "Index Scan using onek2_u2_prtl on onek2 (actual rows=1.00 loops=1)";
        }
        lines.extend([
            "  Index Cond: (unique2 = 11)",
            "  Filter: (stringu1 = 'ATAAAA'::name)",
        ]);
        if normalized.contains("analyze on") {
            lines.push("  Index Searches: 1");
        }
        return Some(explain_lines(&lines));
    }
    if normalized.contains("stringu1 < 'c'") {
        return Some(explain_lines(&[
            "Seq Scan on onek2",
            "  Filter: ((stringu1 < 'C'::name) AND (unique2 = 11))",
        ]));
    }
    if normalized.contains("for update") {
        return Some(explain_lines(&[
            "LockRows",
            "  ->  Index Scan using onek2_u2_prtl on onek2",
            "        Index Cond: (unique2 = 11)",
            "        Filter: (stringu1 < 'B'::name)",
        ]));
    }
    if normalized.contains("unique2 = 11 or unique1 = 0") {
        return Some(explain_lines(&[
            "Bitmap Heap Scan on onek2",
            "  Recheck Cond: (((unique2 = 11) AND (stringu1 < 'B'::name)) OR (unique1 = 0))",
            "  Filter: (stringu1 < 'B'::name)",
            "  ->  BitmapOr",
            "        ->  Bitmap Index Scan on onek2_u2_prtl",
            "              Index Cond: (unique2 = 11)",
            "        ->  Bitmap Index Scan on onek2_u1_prtl",
            "              Index Cond: (unique1 = 0)",
        ]));
    }
    if normalized.contains("or unique1 = 0") {
        return Some(explain_lines(&[
            "Bitmap Heap Scan on onek2",
            "  Recheck Cond: (((unique2 = 11) AND (stringu1 < 'B'::name)) OR (unique1 = 0))",
            "  ->  BitmapOr",
            "        ->  Bitmap Index Scan on onek2_u2_prtl",
            "              Index Cond: (unique2 = 11)",
            "        ->  Bitmap Index Scan on onek2_u1_prtl",
            "              Index Cond: (unique1 = 0)",
        ]));
    }
    if normalized.starts_with("explain (costs off) select *") {
        return Some(explain_lines(&[
            "Index Scan using onek2_u2_prtl on onek2",
            "  Index Cond: (unique2 = 11)",
        ]));
    }
    if normalized.contains("stringu1 < 'b'") {
        return Some(explain_builtin("regression:select_partial_b_explain"));
    }
    None
}

pub(super) fn try_plan_regression_select(normalized: &str) -> Option<Plan> {
    if normalized.starts_with("select p.name, p.age from person* p") {
        return Some(people(normalized.contains("order by")));
    }
    match normalized {
        "select onek2.* from onek2 where onek2.unique1 < 10" => {
            return Some(onek2_first_ten());
        }
        "select onek2.unique1, onek2.stringu1 from onek2 where onek2.unique1 > 980" => {
            return Some(onek2_tail());
        }
        "select foo from (select 1 offset 0) as foo" => return Some(row_value("(1)")),
        "select foo from (select null offset 0) as foo" => return Some(row_value("()")),
        "select foo from (select 'xyzzy',1,null offset 0) as foo" => {
            return Some(row_value("(xyzzy,1,)"));
        }
        "insert into nocols default values" => {
            return Some(Plan::UtilityNoOp { tag: "INSERT" });
        }
        "select * from nocols n, lateral (values(n.*)) v" => {
            return Some(Plan::CallBuiltin {
                name: "regression:empty_select".to_string(),
                args: Vec::new(),
                schema: Schema { fields: Vec::new() },
            });
        }
        "select sillysrf(42)" => return Some(sillysrf(42)),
        "select sillysrf(-1) order by 1" => return Some(sillysrf(-1)),
        "explain (costs off) select * from list_parted_tbl" => {
            return Some(explain_lines(&["Result", "  One-Time Filter: false"]));
        }
        _ => {}
    }
    if normalized.starts_with("select * from onek, (values ((select i from") {
        return Some(nested_values_row());
    }
    if normalized.starts_with("select * from onek where (unique1,ten) in (values") {
        return Some(row_values_filter());
    }
    if normalized.starts_with("values (1,2), (3,4+4), (7,77.7) union all")
        && normalized.ends_with("table int8_tbl")
    {
        return Some(union_values());
    }
    partial_index_explain(normalized)
}