code: func deriveOtherConditions(p *LogicalJoin, deriveLeft bool, deriveRight bool) (leftCond []expression.Expression,
  	rightCond []expression.Expression) {
  	leftPlan, rightPlan := p.children[0], p.children[1]
  	isOuterSemi := (p.JoinType == LeftOuterSemiJoin) || (p.JoinType == AntiLeftOuterSemiJoin)
  	for _, expr := range p.OtherConditions {
  		if deriveLeft {
  			leftRelaxedCond := expression.DeriveRelaxedFiltersFromDNF(expr, leftPlan.Schema())
  			if leftRelaxedCond != nil {
  				leftCond = append(leftCond, leftRelaxedCond)
  			}
  			notNullExpr := deriveNotNullExpr(expr, leftPlan.Schema())
  			if notNullExpr != nil {
  				leftCond = append(leftCond, notNullExpr)
  			}
  		}
  		if deriveRight {
  			rightRelaxedCond := expression.DeriveRelaxedFiltersFromDNF(expr, rightPlan.Schema())
  			if rightRelaxedCond != nil {
  				rightCond = append(rightCond, rightRelaxedCond)
  			}
  			// For LeftOuterSemiJoin and AntiLeftOuterSemiJoin, we can actually generate
  			// `col is not null` according to expressions in `OtherConditions` now, but we
  			// are putting column equal condition converted from `in (subq)` into
  			// `OtherConditions`(@sa https://github.com/pingcap/tidb/pull/9051), then it would
  			// cause wrong results, so we disable this optimization for outer semi joins now.
  			// TODO enable this optimization for outer semi joins later by checking whether
  			// condition in `OtherConditions` is converted from `in (subq)`.
  			if isOuterSemi {
  				continue
  			}
  			notNullExpr := deriveNotNullExpr(expr, rightPlan.Schema())
  			if notNullExpr != nil {
  				rightCond = append(rightCond, notNullExpr)
  			}
  		}
  	}
  	return
  }
docs: // deriveOtherConditions given a LogicalJoin, check the OtherConditions to see if we can derive more
  // conditions for left/right child pushdown.