A custom scan provider will typically add paths for a base relation by setting the following hook, which is called after the core code has generated all the access paths it can for the relation (except for Gather and Gather Merge paths, which are made after this call so that they can use partial paths added by the hook):
typedef void (*set_rel_pathlist_hook_type) (PlannerInfo *root,
                                            RelOptInfo *rel,
                                            Index rti,
                                            RangeTblEntry *rte);
extern PGDLLIMPORT set_rel_pathlist_hook_type set_rel_pathlist_hook;
    Although this hook function can be used to examine, modify, or remove
    paths generated by the core system, a custom scan provider will typically
    confine itself to generating CustomPath objects and adding
    them to rel using add_path, or
    add_partial_path if they are partial paths.  The
    custom scan provider is responsible for initializing the
    CustomPath object, which is declared like this:
typedef struct CustomPath
{
    Path      path;
    uint32    flags;
    List     *custom_paths;
    List     *custom_restrictinfo;
    List     *custom_private;
    const CustomPathMethods *methods;
} CustomPath;
    path must be initialized as for any other path, including
    the row-count estimate, start and total cost, and sort ordering provided
    by this path.  flags is a bit mask, which
    specifies whether the scan provider can support certain optional
    capabilities.  flags should include
    CUSTOMPATH_SUPPORT_BACKWARD_SCAN if the custom path can support
    a backward scan, CUSTOMPATH_SUPPORT_MARK_RESTORE if it
    can support mark and restore,
    and CUSTOMPATH_SUPPORT_PROJECTION if it can perform
    projections.  (If CUSTOMPATH_SUPPORT_PROJECTION is not
    set, the scan node will only be asked to produce Vars of the scanned
    relation; while if that flag is set, the scan node must be able to
    evaluate scalar expressions over these Vars.)
    An optional custom_paths is a list of Path
    nodes used by this custom-path node; these will be transformed into
    Plan nodes by planner.
    As described below, custom paths can be created for join relations as
    well.  In such a case, custom_restrictinfo
    should be used to store the set of join clauses to apply to the join the
    custom path replaces.  Otherwise it should be NIL.
    custom_private can be used to store the custom path's
    private data.  Private data should be stored in a form that can be handled
    by nodeToString, so that debugging routines that attempt to
    print the custom path will work as designed.  methods must
    point to a (usually statically allocated) object implementing the required
    custom path methods, which are further detailed below.
  
   A custom scan provider can also provide join paths.  Just as for base
   relations, such a path must produce the same output as would normally be
   produced by the join it replaces.  To do this, the join provider should
   set the following hook, and then within the hook function,
   create CustomPath path(s) for the join relation.
typedef void (*set_join_pathlist_hook_type) (PlannerInfo *root,
                                             RelOptInfo *joinrel,
                                             RelOptInfo *outerrel,
                                             RelOptInfo *innerrel,
                                             JoinType jointype,
                                             JoinPathExtraData *extra);
extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook;
This hook will be invoked repeatedly for the same join relation, with different combinations of inner and outer relations; it is the responsibility of the hook to minimize duplicated work.
   Note also that the set of join clauses to apply to the join,
   which is passed as extra->restrictlist, varies
   depending on the combination of inner and outer relations.  A
   CustomPath path generated for the
   joinrel must contain the set of join clauses it uses,
   which will be used by the planner to convert the
   CustomPath path into a plan, if it is selected
   by the planner as the best path for the joinrel.
  
Plan *(*PlanCustomPath) (PlannerInfo *root,
                         RelOptInfo *rel,
                         CustomPath *best_path,
                         List *tlist,
                         List *clauses,
                         List *custom_plans);
    Convert a custom path to a finished plan.  The return value will generally
    be a CustomScan object, which the callback must allocate and
    initialize.  See Section 59.2 for more details.
   
List *(*ReparameterizeCustomPathByChild) (PlannerInfo *root,
                                          List *custom_private,
                                          RelOptInfo *child_rel);
    This callback is called while converting a path parameterized by the
    top-most parent of the given child relation child_rel
    to be parameterized by the child relation.  The callback is used to
    reparameterize any paths or translate any expression nodes saved in the
    given custom_private member of a
    CustomPath.  The callback may use
    reparameterize_path_by_child,
    adjust_appendrel_attrs or
    adjust_appendrel_attrs_multilevel as required.