People often want different variants of planner rules. An example is FilterJoinRule, which has a 'boolean smart’ parameter, a predicate (which returns whether to pull up filter conditions), operands (which determine the precise sub-classes of RelNode that the rule should match) and a RelBuilderFactory (which controls the type of RelNode created by this rule).
Suppose you have an instance of FilterJoinRule and you want to change smart from true to false. The smart parameter is immutable (good!) but you can’t easily create a clone of the rule because you don’t know the values of the other parameters. Your instance might even be (unbeknownst to you) a sub-class with extra parameters and a private constructor.
So, my proposal is to put all of the config information of a RelOptRule into a single config parameter that contains all relevant properties. Each sub-class of RelOptRule would have one constructor with just a ‘config’ parameter. Each config knows which sub-class of RelOptRule to create. Therefore it is easy to copy a config, change one or more properties, and create a new rule instance.
Adding a property to a rule’s config does not require us to add or deprecate any constructors.
The operands are part of the config, so if you have a rule that matches a EnumerableFilter on an EnumerableJoin and you want to make it match an EnumerableFilter on an EnumerableNestedLoopJoin, you can easily create one with one changed operand.
The config is immutable and self-describing, so we can use it to automatically generate a unique description for each rule instance.
(See the email thread [DISCUSS] Refactor how planner rules are parameterized.)