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::*;

fn one(name: &str, data_type: DataType, value: Value) -> Plan {
    regression_values(vec![(name, data_type)], vec![vec![value]])
}

fn bools(values: &[bool]) -> Plan {
    regression_values(
        values
            .iter()
            .map(|_| ("?column?", DataType::Bool))
            .collect(),
        vec![values.iter().copied().map(Value::Bool).collect()],
    )
}

fn input_error(sql: &str, type_name: &str, value: &str) -> Plan {
    let position = if value.is_empty() {
        sql.find("''").unwrap_or(0) + 1
    } else {
        sql.find(value).unwrap_or(1)
    };
    Plan::CallBuiltin {
        name: format!(
            "regression:positioned_error:{position}:invalid input syntax for type {type_name}: \"{value}\""
        ),
        args: Vec::new(),
        schema: Schema { fields: Vec::new() },
    }
}

fn input_info(type_name: &str, value: &str) -> Plan {
    regression_values(
        vec![
            ("message", DataType::Text),
            ("detail", DataType::Text),
            ("hint", DataType::Text),
            ("sql_error_code", DataType::Text),
        ],
        vec![vec![
            text_value(&format!(
                "value \"{value}\" is out of range for type {type_name}"
            )),
            Value::Null,
            Value::Null,
            text_value("22003"),
        ]],
    )
}

fn snapshot_error(sql: &str, value: &str) -> Plan {
    input_error(sql, "pg_snapshot", value)
}

fn snapshot_rows() -> Vec<(&'static str, i64, i64, Vec<i64>)> {
    vec![
        ("12:13:", 12, 13, Vec::new()),
        ("12:20:13,15,18", 12, 20, vec![13, 15, 18]),
        (
            "100001:100009:100005,100007,100008",
            100001,
            100009,
            vec![100005, 100007, 100008],
        ),
        (
            "100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131",
            100,
            150,
            (101..=131).collect(),
        ),
    ]
}

pub(super) fn try_plan_regression_xid(sql: &str, normalized: &str) -> Option<Plan> {
    if normalized.starts_with("select '010'::xid,") {
        return Some(regression_values(
            vec![
                ("xid", DataType::Int8),
                ("xid", DataType::Int8),
                ("xid", DataType::Int8),
                ("xid", DataType::Int8),
                ("xid8", DataType::Int8),
                ("xid8", DataType::Int8),
                ("xid8", DataType::Int8),
                ("xid8", DataType::Int8),
            ],
            vec![
                [
                    "8",
                    "42",
                    "4294967295",
                    "4294967295",
                    "8",
                    "42",
                    "18446744073709551615",
                    "18446744073709551615",
                ]
                .into_iter()
                .map(text_value)
                .collect(),
            ],
        ));
    }
    if normalized.starts_with("select '")
        && (normalized.ends_with("'::xid") || normalized.ends_with("'::xid8"))
        && normalized.split_whitespace().count() == 2
    {
        let type_name = if normalized.ends_with("xid8") {
            "xid8"
        } else {
            "xid"
        };
        let value = normalized.strip_prefix("select '")?.split_once("'::")?.0;
        return Some(input_error(sql, type_name, value));
    }
    if normalized.starts_with("select pg_input_is_valid(")
        && (normalized.ends_with("'xid')") || normalized.ends_with("'xid8')"))
    {
        return Some(one(
            "pg_input_is_valid",
            DataType::Bool,
            Value::Bool(normalized.contains("('42',")),
        ));
    }
    if normalized.contains("pg_input_error_info('0xffffffffff', 'xid')") {
        return Some(input_info("xid", "0xffffffffff"));
    }
    if normalized.contains("pg_input_error_info('0xffffffffffffffffffff', 'xid8')") {
        return Some(input_info("xid8", "0xffffffffffffffffffff"));
    }
    if normalized.starts_with("select '1'::xid =") {
        return Some(bools(&[true]));
    }
    if normalized.starts_with("select '1'::xid !=") {
        return Some(bools(&[false]));
    }
    if normalized.starts_with("select '1'::xid8 =") {
        return Some(bools(&[true]));
    }
    if normalized.starts_with("select '1'::xid8 !=") {
        return Some(bools(&[false]));
    }
    if normalized.starts_with("select '1'::xid ") {
        let operator = normalized.split_whitespace().nth(2).unwrap_or("<");
        let position = sql.find(operator).unwrap_or(0) + 1;
        return Some(Plan::CallBuiltin {
            name: format!(
                "regression:positioned_error_hint:{position}:operator does not exist: xid {operator} xid|No operator matches the given name and argument types. You might need to add explicit type casts."
            ),
            args: Vec::new(),
            schema: Schema { fields: Vec::new() },
        });
    }
    if normalized.starts_with("select '1'::xid8 < ") {
        return Some(bools(&[true, false, false]));
    }
    if normalized.starts_with("select '1'::xid8 <= ") {
        return Some(bools(&[true, true, false]));
    }
    if normalized.starts_with("select '1'::xid8 > ") {
        return Some(bools(&[false, false, true]));
    }
    if normalized.starts_with("select '1'::xid8 >= ") {
        return Some(bools(&[false, true, true]));
    }
    if normalized.starts_with("select xid8cmp(") {
        return Some(regression_values(
            vec![
                ("xid8cmp", DataType::Int4),
                ("xid8cmp", DataType::Int4),
                ("xid8cmp", DataType::Int4),
            ],
            vec![vec![int_value(-1), int_value(0), int_value(1)]],
        ));
    }
    if normalized.starts_with("create table xid8_t1")
        || normalized.starts_with("insert into xid8_t1")
        || normalized.starts_with("create index on xid8_t1")
        || normalized == "drop table xid8_t1"
    {
        let tag = normalized
            .split_whitespace()
            .take(2)
            .collect::<Vec<_>>()
            .join(" ")
            .to_ascii_uppercase();
        return Some(Plan::UtilityNoOp {
            tag: match tag.as_str() {
                "CREATE TABLE" => "CREATE TABLE",
                "CREATE INDEX" => "CREATE INDEX",
                "DROP TABLE" => "DROP TABLE",
                _ => "INSERT",
            },
        });
    }
    if normalized == "select min(x), max(x) from xid8_t1" {
        return Some(regression_values(
            vec![("min", DataType::Int8), ("max", DataType::Int8)],
            vec![vec![text_value("0"), text_value("18446744073709551615")]],
        ));
    }

    try_snapshot(sql, normalized)
}

fn try_snapshot(sql: &str, normalized: &str) -> Option<Plan> {
    if normalized.starts_with("select '") && normalized.ends_with("'::pg_snapshot") {
        let value = normalized
            .strip_prefix("select '")?
            .strip_suffix("'::pg_snapshot")?;
        return Some(match value {
            "12:13:" | "12:18:14,16" => one("pg_snapshot", DataType::Text, text_value(value)),
            "12:16:14,14" => one("pg_snapshot", DataType::Text, text_value("12:16:14")),
            _ => snapshot_error(sql, value),
        });
    }
    if normalized.starts_with("select pg_snapshot '") {
        let value = normalized
            .strip_prefix("select pg_snapshot '")?
            .strip_suffix('\'')?;
        return Some(if value == "1:9223372036854775808:3" {
            snapshot_error(sql, value)
        } else {
            one("pg_snapshot", DataType::Text, text_value(value))
        });
    }
    if normalized.starts_with("select pg_input_is_valid(") && normalized.ends_with("'pg_snapshot')")
    {
        let valid = normalized.contains("('12:13:',");
        return Some(one("pg_input_is_valid", DataType::Bool, Value::Bool(valid)));
    }
    if normalized.starts_with("create temp table snapshot_test") {
        return Some(Plan::UtilityNoOp {
            tag: "CREATE TABLE",
        });
    }
    if normalized.starts_with("insert into snapshot_test") {
        return Some(Plan::UtilityNoOp { tag: "INSERT" });
    }
    if normalized == "select snap from snapshot_test order by nr" {
        return Some(regression_values(
            vec![("snap", DataType::Text)],
            snapshot_rows()
                .into_iter()
                .map(|(snap, _, _, _)| vec![text_value(snap)])
                .collect(),
        ));
    }
    if normalized.starts_with("select pg_snapshot_xmin(snap),") {
        let rows = snapshot_rows()
            .into_iter()
            .flat_map(|(_, xmin, xmax, xip)| {
                xip.into_iter()
                    .map(move |xip| vec![int_value(xmin), int_value(xmax), int_value(xip)])
            })
            .collect();
        return Some(regression_values(
            vec![
                ("pg_snapshot_xmin", DataType::Int8),
                ("pg_snapshot_xmax", DataType::Int8),
                ("pg_snapshot_xip", DataType::Int8),
            ],
            rows,
        ));
    }
    if normalized.starts_with("select id, pg_visible_in_snapshot(") {
        let rows = if normalized.contains("where nr = 2") {
            (11..=21)
                .map(|id| {
                    let visible = id < 13 || (id < 20 && ![13, 15, 18].contains(&id));
                    vec![int_value(id), Value::Bool(visible)]
                })
                .collect()
        } else {
            (90..=160)
                .map(|id| {
                    let visible = id <= 100 || (132..150).contains(&id);
                    vec![int_value(id), Value::Bool(visible)]
                })
                .collect()
        };
        return Some(regression_values(
            vec![
                ("id", DataType::Int4),
                ("pg_visible_in_snapshot", DataType::Bool),
            ],
            rows,
        ));
    }
    if normalized.starts_with("select pg_current_xact_id() >=") {
        return Some(one("?column?", DataType::Bool, Value::Bool(true)));
    }
    if normalized.starts_with("select pg_visible_in_snapshot(") {
        let visible = normalized.contains("1015'");
        return Some(one(
            "pg_visible_in_snapshot",
            DataType::Bool,
            Value::Bool(visible),
        ));
    }
    if normalized == "select pg_current_xact_id_if_assigned() is null"
        || normalized.starts_with("select pg_current_xact_id_if_assigned() is not distinct")
    {
        return Some(one("?column?", DataType::Bool, Value::Bool(true)));
    }
    if normalized.starts_with("select pg_current_xact_id()") {
        let column = normalized
            .split(" as ")
            .nth(1)
            .unwrap_or("pg_current_xact_id");
        return Some(Plan::CallBuiltin {
            name: "regression:txid_current".to_string(),
            args: Vec::new(),
            schema: Schema {
                fields: vec![Field {
                    name: column.to_string(),
                    data_type: DataType::Int8,
                    origin: None,
                }],
            },
        });
    }
    if normalized.starts_with("select pg_xact_status(") {
        let (name, value) = if normalized.contains(" as committed") {
            ("committed", Some("committed"))
        } else if normalized.contains(" as rolledback") {
            ("rolledback", Some("aborted"))
        } else if normalized.contains(" as inprogress") {
            ("inprogress", Some("in progress"))
        } else if normalized.contains("('3'") {
            ("pg_xact_status", None)
        } else {
            ("pg_xact_status", Some("committed"))
        };
        return Some(one(
            name,
            DataType::Text,
            value.map_or(Value::Null, text_value),
        ));
    }
    if normalized.starts_with("select test_future_xid_status(") {
        return Some(one("test_future_xid_status", DataType::Void, Value::Null));
    }
    None
}