macro_rules! impl_embedded_query_helpers {
    (
        statement = $statement:ty,
        params_trait = $params_trait:path,
        row = $row:ty,
        error = $error:ty,
        params_from_iter = $params_from_iter:path,
        catalog_params = $catalog_params:path
    ) => {
        fn collect_rows<T, P, F>(stmt: &mut $statement, params: P, f: F) -> $crate::Result<Vec<T>>
        where
            P: $params_trait,
            F: FnMut(&$row) -> std::result::Result<T, $error>,
        {
            stmt.query_map(params, f)
                .map_err(sql_err)?
                .collect::<std::result::Result<Vec<_>, _>>()
                .map_err(sql_err)
        }

        fn collect_shared_rows<T, P, F>(
            stmt: &mut $statement,
            params: P,
            mut map: F,
        ) -> $crate::Result<Vec<T>>
        where
            P: $params_trait,
            F: FnMut(&dyn $crate::embedded_rows::EmbeddedRow) -> $crate::Result<T>,
        {
            let mut rows = stmt.query(params).map_err(sql_err)?;
            let mut out = Vec::new();
            while let Some(row) = rows.next().map_err(sql_err)? {
                out.push(map(row)?);
            }
            Ok(out)
        }

        fn run_read_query<T>(
            conn: &Connection,
            query: &$crate::embedded_reads::ReadQuery<T>,
        ) -> $crate::Result<Vec<T>> {
            let sql = query.render_embedded();
            let mut stmt = conn.prepare(&sql).map_err(sql_err)?;
            collect_shared_rows(
                &mut stmt,
                $params_from_iter(query.params.iter().copied()),
                query.map,
            )
        }

        fn run_catalog_query<T>(
            conn: &Connection,
            query: &$crate::catalog_queries::CatalogQuery,
            map: fn(&dyn $crate::embedded_rows::EmbeddedRow) -> $crate::Result<T>,
        ) -> $crate::Result<Vec<T>> {
            let sql = query.render_embedded();
            let params = $catalog_params(query.params());
            let mut stmt = conn.prepare(&sql).map_err(sql_err)?;
            collect_shared_rows(&mut stmt, $params_from_iter(params), map)
        }

        fn execute_catalog_command(
            conn: &Connection,
            query: &$crate::catalog_queries::CatalogQuery,
        ) -> $crate::Result<usize> {
            let sql = query.render_embedded();
            let params = $catalog_params(query.params());
            conn.execute(&sql, $params_from_iter(params))
                .map_err(sql_err)
        }

        fn global_metadata_value(conn: &Connection, key: &str) -> $crate::Result<Option<String>> {
            Ok(run_catalog_query(
                conn,
                &$crate::catalog_queries::global_metadata_value(key),
                $crate::catalog_queries::opt_string_value,
            )?
            .into_iter()
            .next()
            .flatten())
        }
    };
}

macro_rules! impl_embedded_catalog_params {
    (
        fn $name:ident -> $value:ty {
            null = $null:expr,
            i64 = |$i64_value:ident| $i64_expr:expr,
            string = |$string_value:ident| $string_expr:expr,
            bool = |$bool_value:ident| $bool_expr:expr $(,)?
        }
    ) => {
        fn $name(params: &[$crate::catalog_queries::CatalogParam]) -> Vec<$value> {
            params
                .iter()
                .map(|param| match param {
                    $crate::catalog_queries::CatalogParam::I64(value) => {
                        let $i64_value = *value;
                        $i64_expr
                    }
                    $crate::catalog_queries::CatalogParam::String(value) => {
                        let $string_value = value.clone();
                        $string_expr
                    }
                    $crate::catalog_queries::CatalogParam::OptI64(Some(value)) => {
                        let $i64_value = *value;
                        $i64_expr
                    }
                    $crate::catalog_queries::CatalogParam::OptI64(None) => $null,
                    $crate::catalog_queries::CatalogParam::OptString(Some(value)) => {
                        let $string_value = value.clone();
                        $string_expr
                    }
                    $crate::catalog_queries::CatalogParam::OptString(None) => $null,
                    $crate::catalog_queries::CatalogParam::OptBool(Some(value)) => {
                        let $bool_value = *value;
                        $bool_expr
                    }
                    $crate::catalog_queries::CatalogParam::OptBool(None) => $null,
                    $crate::catalog_queries::CatalogParam::Bool(value) => {
                        let $bool_value = *value;
                        $bool_expr
                    }
                })
                .collect()
        }
    };
}

macro_rules! impl_embedded_sql_err {
    (fn $name:ident, error = $error:ty $(,)?) => {
        fn $name(err: $error) -> $crate::StorageError {
            $crate::StorageError::Backend(err.to_string())
        }
    };
}
