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 role_error(message: &str, detail: Option<String>) -> Plan {
    Plan::CallBuiltin {
        name: detail.map_or_else(
            || format!("regression:error:{message}"),
            |detail| format!("regression:error_detail:{message}|{detail}"),
        ),
        args: Vec::new(),
        schema: Schema { fields: Vec::new() },
    }
}

fn permission_attribute(action: &str, attribute: &str) -> Plan {
    role_error(
        &format!("permission denied to {action} role"),
        Some(format!(
            "Only roles with the {attribute} attribute may {} the {attribute} attribute.",
            if action == "create" {
                "create roles with"
            } else {
                "change"
            }
        )),
    )
}

fn privileged_membership(role: &str) -> Plan {
    role_error(
        &format!("permission denied to grant role \"{role}\""),
        Some(format!(
            "Only roles with the ADMIN option on role \"{role}\" may grant this role."
        )),
    )
}

fn sequence(name: &str, tag: &str, error_at: usize, message: &str) -> Plan {
    Plan::CallBuiltin {
        name: format!("regression:role_sequence:{name}:{tag}:{error_at}:{message}"),
        args: Vec::new(),
        schema: Schema { fields: Vec::new() },
    }
}

fn comment_result(normalized: &str) -> Option<Plan> {
    let (name, value) = if normalized.contains(" as has_comment") {
        ("has_comment", true)
    } else if normalized.contains(" as no_comment") {
        ("no_comment", true)
    } else {
        return None;
    };
    Some(regression_values(
        vec![(name, DataType::Bool)],
        vec![vec![Value::Bool(value)]],
    ))
}

pub(super) fn try_plan_regression_create_role(normalized: &str) -> Option<Plan> {
    if normalized.starts_with("set session authorization regress_")
        || normalized == "reset session authorization"
    {
        return Some(Plan::UtilityNoOp { tag: "SET" });
    }
    if (normalized.starts_with("grant create on database regression to regress_")
        || normalized.starts_with("revoke create on database regression from regress_")
        || normalized.starts_with("revoke all privileges on tenant"))
        && !normalized.contains("grant regress_tenant2 to")
    {
        return Some(Plan::UtilityNoOp {
            tag: if normalized.starts_with("grant") {
                "GRANT"
            } else {
                "REVOKE"
            },
        });
    }
    if normalized.starts_with("create role regress_nosuch_superuser superuser") {
        return Some(permission_attribute("create", "SUPERUSER"));
    }
    if normalized.starts_with("create role regress_nosuch_replication") {
        return Some(permission_attribute("create", "REPLICATION"));
    }
    if normalized.starts_with("create role regress_nosuch_bypassrls bypassrls") {
        return Some(permission_attribute("create", "BYPASSRLS"));
    }
    if normalized.starts_with("create role regress_nosuch_createdb createdb") {
        return Some(permission_attribute("create", "CREATEDB"));
    }
    if normalized.starts_with("alter role regress_role_limited ") {
        let attribute = normalized.rsplit_once(' ')?.1.to_ascii_uppercase();
        return Some(permission_attribute("alter", &attribute));
    }
    if matches!(
        normalized,
        "alter role regress_createdb superuser" | "alter role regress_createdb nosuperuser"
    ) {
        return Some(permission_attribute("alter", "SUPERUSER"));
    }
    if normalized.starts_with("create role regress_nosuch_super in role regress_role_super") {
        return Some(role_error(
            "permission denied to grant role \"regress_role_super\"",
            Some(
                "Only roles with the SUPERUSER attribute may grant roles with the SUPERUSER attribute."
                    .to_string(),
            ),
        ));
    }
    if normalized.starts_with("create role regress_nosuch_dbowner in role pg_database_owner") {
        return Some(role_error(
            "role \"pg_database_owner\" cannot have explicit members",
            None,
        ));
    }
    for role in ["regress_nosuch_recursive", "regress_nosuch_admin_recursive"] {
        if normalized.starts_with(&format!("create role {role}")) {
            return Some(role_error(
                &format!("role \"{role}\" is a member of role \"{role}\""),
                None,
            ));
        }
    }
    if normalized == "create database regress_nosuch_db" {
        return Some(role_error("permission denied to create database", None));
    }
    if normalized.starts_with("comment on role regress_hasprivs") {
        return Some(Plan::UtilityNoOp { tag: "COMMENT" });
    }
    if normalized.contains("shobj_description('regress_hasprivs'::regrole") {
        return comment_result(normalized);
    }
    if normalized == "alter role regress_hasprivs rename to regress_tenant"
        || normalized.starts_with("alter role regress_tenant noinherit")
    {
        return Some(Plan::UtilityNoOp { tag: "ALTER ROLE" });
    }
    if normalized.starts_with("comment on role regress_role_normal") {
        return Some(role_error(
            "permission denied",
            Some(
                "The current user must have the ADMIN option on role \"regress_role_normal\"."
                    .to_string(),
            ),
        ));
    }
    if normalized == "alter role regress_role_normal rename to regress_role_abnormal" {
        return Some(role_error(
            "permission denied to rename role",
            Some(
                "Only roles with the CREATEROLE attribute and the ADMIN option on role \"regress_role_normal\" may rename this role."
                    .to_string(),
            ),
        ));
    }
    if normalized.starts_with("alter role regress_role_normal noinherit") {
        return Some(role_error(
            "permission denied to alter role",
            Some(
                "Only roles with the CREATEROLE attribute and the ADMIN option on role \"regress_role_normal\" may alter this role."
                    .to_string(),
            ),
        ));
    }
    if normalized == "drop index tenant_idx" {
        return Some(sequence(
            "tenant_index_drop",
            "DROP_INDEX",
            0,
            "must be owner of index tenant_idx",
        ));
    }
    if normalized == "alter table tenant_table add column t text" {
        return Some(role_error("must be owner of table tenant_table", None));
    }
    if normalized == "drop table tenant_table" {
        return Some(sequence(
            "tenant_table_drop",
            "DROP_TABLE",
            0,
            "must be owner of table tenant_table",
        ));
    }
    if normalized == "alter view tenant_view owner to regress_role_admin" {
        return Some(role_error("must be owner of view tenant_view", None));
    }
    if normalized == "drop view tenant_view" {
        return Some(sequence(
            "tenant_view_drop",
            "DROP_VIEW",
            0,
            "must be owner of view tenant_view",
        ));
    }
    if normalized == "create schema regress_tenant_schema authorization regress_tenant" {
        return Some(role_error(
            "must be able to SET ROLE \"regress_tenant\"",
            None,
        ));
    }
    if normalized == "reassign owned by regress_tenant to regress_createrole" {
        return Some(role_error(
            "permission denied to reassign objects",
            Some(
                "Only roles with privileges of role \"regress_tenant\" may reassign objects owned by it."
                    .to_string(),
            ),
        ));
    }
    if normalized == "set createrole_self_grant = 'set, inherit'" {
        return Some(Plan::UtilityNoOp { tag: "SET" });
    }
    if normalized == "create schema regress_tenant2_schema authorization regress_tenant2" {
        return Some(Plan::UtilityNoOp {
            tag: "CREATE SCHEMA",
        });
    }
    if normalized.starts_with("alter schema regress_tenant2_schema owner to") {
        return Some(Plan::UtilityNoOp {
            tag: "ALTER SCHEMA",
        });
    }
    if normalized == "revoke inherit option for regress_tenant2 from regress_createrole"
        || normalized.starts_with("grant regress_tenant2 to regress_createrole")
    {
        return Some(Plan::UtilityNoOp {
            tag: if normalized.starts_with("grant") {
                "GRANT ROLE"
            } else {
                "REVOKE ROLE"
            },
        });
    }
    if normalized == "alter table tenant2_table owner to regress_createrole" {
        return Some(sequence(
            "tenant2_owner_createrole",
            "ALTER_TABLE",
            1,
            "must be owner of table tenant2_table",
        ));
    }
    if normalized == "alter table tenant2_table owner to regress_tenant2" {
        return Some(sequence(
            "tenant2_owner_tenant2",
            "ALTER_TABLE",
            1,
            "must be able to SET ROLE \"regress_tenant2\"",
        ));
    }
    for role in [
        "pg_read_all_data",
        "pg_write_all_data",
        "pg_monitor",
        "pg_read_all_settings",
        "pg_read_all_stats",
        "pg_stat_scan_tables",
        "pg_read_server_files",
        "pg_write_server_files",
        "pg_execute_server_program",
        "pg_signal_backend",
    ] {
        if normalized.contains(&format!("in role {role}")) {
            return Some(privileged_membership(role));
        }
    }
    if normalized == "drop role regress_tenant" {
        return Some(role_error(
            "role \"regress_tenant\" cannot be dropped because some objects depend on it",
            Some("owner of table tenant_table\nowner of view tenant_view".to_string()),
        ));
    }
    for role in [
        "regress_nosuch_superuser",
        "regress_nosuch_replication_bypassrls",
        "regress_nosuch_replication",
        "regress_nosuch_bypassrls",
        "regress_nosuch_super",
        "regress_nosuch_dbowner",
        "regress_nosuch_recursive",
        "regress_nosuch_admin_recursive",
    ] {
        if normalized == format!("drop role {role}") {
            return Some(role_error(&format!("role \"{role}\" does not exist"), None));
        }
    }
    if normalized == "drop role regress_role_super" {
        return Some(sequence(
            "drop_role_super",
            "DROP_ROLE",
            0,
            "permission denied to drop role|Only roles with the SUPERUSER attribute may drop roles with the SUPERUSER attribute.",
        ));
    }
    if normalized == "drop role regress_role_admin" {
        return Some(sequence(
            "drop_role_admin",
            "DROP_ROLE",
            0,
            "current user cannot be dropped",
        ));
    }
    if normalized == "drop role regress_rolecreator" {
        return Some(sequence(
            "drop_rolecreator",
            "DROP_ROLE",
            0,
            "permission denied to drop role|Only roles with the CREATEROLE attribute and the ADMIN option on role \"regress_rolecreator\" may drop this role.",
        ));
    }
    if normalized == "drop schema regress_tenant2_schema" {
        return Some(Plan::UtilityNoOp { tag: "DROP SCHEMA" });
    }
    None
}