### Description

The IS NULL/IS NOT NULL only care about the nullability not the value of an expression.
Given the expression x + 1 / 2, the actual result does not matter only if it's null or not - that is, it only matters if x is null or not.
So x + 1 / 2 IS NULL becomes x IS NULL - which can be opportunistically pushed down or evaluated.
x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/optimizer/OptimizerRulesTests.java
FieldAttribute fa = getFieldAttribute();
        IsNull isNull = new IsNull(EMPTY, fa);
        And nestedAnd = new And(EMPTY, lessThanOf(getFieldAttribute("b"), ONE), lessThanOf(getFieldAttribute("c"), ONE));
        And and = new And(EMPTY, isNull, nestedAnd);
        And top = new And(EMPTY, and, lessThanOf(fa, ONE));
        Expression optimized = new PropagateNullable().rule(top);
        Expression expected = new And(EMPTY, and, nullOf(BOOLEAN));
        assertEquals(Predicates.splitAnd(expected), Predicates.splitAnd(optimized));
    }
    
    public void testIsNullAndDeeplyNestedExpression() throws Exception {
        FieldAttribute fa = getFieldAttribute();
        IsNull isNull = new IsNull(EMPTY, fa);
        Expression nullified = new And(
            EMPTY,
            greaterThanOf(new Div(EMPTY, new Add(EMPTY, fa, ONE), TWO), ONE),
            greaterThanOf(new Add(EMPTY, fa, TWO), ONE)
        );
        Expression kept = new And(EMPTY, isNull, lessThanOf(getFieldAttribute("b"), THREE));
        And and = new And(EMPTY, nullified, kept);
        Expression optimized = new PropagateNullable().rule(and);
        Expression expected = new And(EMPTY, new And(EMPTY, nullOf(BOOLEAN), nullOf(BOOLEAN)), kept);
        assertEquals(Predicates.splitAnd(expected), Predicates.splitAnd(optimized));
    }
    
    
    public void testIsNullInDisjunction() throws Exception {
        FieldAttribute fa = getFieldAttribute();
        Or or = new Or(EMPTY, new IsNull(EMPTY, fa), new IsNotNull(EMPTY, fa));
        Filter dummy = new Filter(EMPTY, relation(), or);