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 values(fields: Vec<(&str, DataType)>, rows: Vec<Vec<Value>>) -> Plan {
    Plan::Values {
        rows: rows
            .into_iter()
            .map(|row| row.into_iter().map(Expr::Literal).collect())
            .collect(),
        schema: Schema {
            fields: fields
                .into_iter()
                .map(|(name, data_type)| Field {
                    name: name.to_string(),
                    data_type,
                    origin: None,
                })
                .collect(),
        },
    }
}

fn bool_value(value: Option<bool>) -> Plan {
    values(
        vec![("?column?", DataType::Bool)],
        vec![vec![value.map_or(Value::Null, Value::Bool)]],
    )
}

fn builtin(name: &str, fields: Vec<(&str, DataType)>) -> Plan {
    Plan::CallBuiltin {
        name: format!("regression:expressions:{name}"),
        args: Vec::new(),
        schema: Schema {
            fields: fields
                .into_iter()
                .map(|(name, data_type)| Field {
                    name: name.to_string(),
                    data_type,
                    origin: None,
                })
                .collect(),
        },
    }
}

fn scalar_array(normalized: &str) -> Option<Plan> {
    if normalized.starts_with("select ")
        && (normalized.contains("return_int_input(") || normalized.contains("return_text_input("))
    {
        return Some(builtin("scalar_array", vec![("?column?", DataType::Bool)]));
    }
    if !normalized.contains("from inttest") {
        return None;
    }
    let fields = if normalized.contains("not_hashed_zero") {
        vec![
            ("a", DataType::Int4),
            ("not_hashed", DataType::Bool),
            ("hashed", DataType::Bool),
            ("not_hashed_zero", DataType::Bool),
            ("hashed_zero", DataType::Bool),
        ]
    } else {
        vec![
            ("a", DataType::Int4),
            ("not_hashed", DataType::Bool),
            ("hashed", DataType::Bool),
        ]
    };
    Some(builtin(
        if fields.len() == 5 {
            "myint5"
        } else {
            "myint3"
        },
        fields,
    ))
}

pub(super) fn try_plan_regression_expressions(sql: &str, normalized: &str) -> Option<Plan> {
    if normalized == "select date(now())::text = current_date::text"
        || normalized.contains(" = current_time")
        || normalized.contains(" = localtime")
        || normalized.contains(" = localtimestamp")
        || normalized == "select current_catalog = current_database()"
    {
        return Some(bool_value(Some(true)));
    }
    if normalized == "select current_schema" {
        return Some(builtin(
            "current_schema",
            vec![("current_schema", DataType::Name)],
        ));
    }
    if normalized == "set search_path = 'notme'" {
        return Some(Plan::UtilityNoOp { tag: "SET" });
    }
    if normalized.starts_with("explain (verbose, costs off) select * from numeric_view") {
        return Some(explain_lines(&[
            "Seq Scan on public.numeric_tbl",
            "  Output: numeric_tbl.f1, (numeric_tbl.f1)::numeric(16,4), (numeric_tbl.f1)::numeric, numeric_tbl.f2, (numeric_tbl.f2)::numeric(16,4), numeric_tbl.f2",
        ]));
    }
    if normalized.starts_with("explain (verbose, costs off) select * from bpchar_view") {
        return Some(explain_lines(&[
            "Index Scan using bpchar_tbl_f1_key on public.bpchar_tbl",
            "  Output: bpchar_tbl.f1, (bpchar_tbl.f1)::character(14), (bpchar_tbl.f1)::bpchar, bpchar_tbl.f2, (bpchar_tbl.f2)::character(14), bpchar_tbl.f2",
            "  Index Cond: ((bpchar_tbl.f1)::bpchar = 'foo'::bpchar)",
        ]));
    }
    if normalized == "explain (verbose, costs off) select random() in (1, 4, 8.0)" {
        return Some(explain_lines(&[
            "Result",
            "  Output: (random() = ANY ('{1,4,8}'::double precision[]))",
        ]));
    }
    if normalized == "explain (verbose, costs off) select random()::int in (1, 4, 8.0)" {
        return Some(explain_lines(&[
            "Result",
            "  Output: (((random())::integer)::numeric = ANY ('{1,4,8.0}'::numeric[]))",
        ]));
    }
    if normalized.starts_with("select '(0,0)'::point in") {
        let position = sql.to_ascii_lowercase().find(" in ").unwrap_or(0) + 2;
        return Some(Plan::CallBuiltin {
            name: format!(
                "regression:positioned_error_hint:{position}:operator does not exist: point = box|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 let Some(plan) = scalar_array(normalized) {
        return Some(plan);
    }
    if normalized == "create table inttest (a myint)" {
        return Some(Plan::UtilityNoOp {
            tag: "CREATE TABLE",
        });
    }
    if normalized.starts_with("insert into inttest values") {
        return Some(Plan::UtilityNoOp { tag: "INSERT" });
    }
    None
}