Skip to content

proposal: support casting between PgNodes? #1046

@skyzh

Description

@skyzh

As I'm working on a foreign data wrapper, I'll need to traverse the plan tree, and de-parse it to extract the information inside it. Currently, I'm manually implementing casting for the types I use:

pub trait PgNodeExt: PgNode {
    unsafe fn as_restrict_info(&self) -> &pg_sys::RestrictInfo;
    unsafe fn as_op_expr(&self) -> &pg_sys::OpExpr;
    unsafe fn as_var(&self) -> &pg_sys::Var;
    unsafe fn as_const(&self) -> &pg_sys::Const;
    unsafe fn as_relabel_type(&self) -> &pg_sys::RelabelType;
    unsafe fn as_bool_expr(&self) -> &pg_sys::BoolExpr;
}


impl<T: PgNode> PgNodeExt for T {
    unsafe fn as_restrict_info(&self) -> &pg_sys::RestrictInfo {
        if is_type(self, pg_sys::NodeTag_T_RestrictInfo) {
            unsafe { mem::transmute::<_, &pg_sys::RestrictInfo>(self) }
        } else {
            panic!("Not a RestrictInfo");
        }
    }

    unsafe fn as_op_expr(&self) -> &pg_sys::OpExpr {
        if is_type(self, pg_sys::NodeTag_T_OpExpr) {
            unsafe { mem::transmute::<_, &pg_sys::OpExpr>(self) }
        } else {
            panic!("Not a opexpr");
        }
    }

    unsafe fn as_bool_expr(&self) -> &pg_sys::BoolExpr {
        if is_type(self, pg_sys::NodeTag_T_BoolExpr) {
            unsafe { mem::transmute::<_, &pg_sys::BoolExpr>(self) }
        } else {
            panic!("Not a boolexpr");
        }
    }

    unsafe fn as_var(&self) -> &pg_sys::Var {
        if is_type(self, pg_sys::NodeTag_T_Var) {
            unsafe { mem::transmute::<_, &pg_sys::Var>(self) }
        } else {
            panic!("Not a var");
        }
    }

    unsafe fn as_const(&self) -> &pg_sys::Const {
        if is_type(self, pg_sys::NodeTag_T_Const) {
            unsafe { mem::transmute::<_, &pg_sys::Const>(self) }
        } else {
            panic!("Not a const");
        }
    }

    unsafe fn as_relabel_type(&self) -> &pg_sys::RelabelType {
        if is_type(self, pg_sys::NodeTag_T_RelabelType) {
            unsafe { mem::transmute::<_, &pg_sys::RelabelType>(self) }
        } else {
            panic!("Not a relabeltype");
        }
    }
}

I'm wondering if it would be good to build this inside pg_sys? In the build.rs stage we can generate something like:

pub trait PgNodeExt: PgNode {
    unsafe fn as_<something>(&self) -> &pg_sys::Something; // for all PgNode types, panic if type mismatch
    unsafe fn as_<something>_mut(&mut self) -> &mut pg_sys::Something;
    unsafe fn try_as_<something>(&self) -> Option<&pg_sys::Something>; // return None if type mismatch
    unsafe fn try_as_<something>_mut(&mut self) -> Option<&mut pg_sys::Something>;
}

Or we can implement it as From and TryFrom.

impl <T: PgNode> From<&T> for <SomeNode> {} // repeat for all pgnodes
impl <T: PgNode> TryFrom<&T> for <SomeNode> {} // repeat for all pgnodes

I can have a try if this sounds like a good idea.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions