class Visitor extends SymbolVisitor<Context, Void> {

        @Override
        public Void visitFunction(Function function, Context context) {<FILL_FUNCTION_BODY>}

        @Override
        public Void visitField(ScopedSymbol field, Context context) {
            context.relations.add(field.relation());
            return null;
        }

        @Override
        public Void visitReference(Reference ref, Context context) {
            context.relations.add(ref.ident().tableIdent());
            return null;
        }
    }

            if (context.exit) {
                return null;
            }
            String functionName = function.name();
            switch (functionName) {
                case NotPredicate.NAME -> {
                    return null;
                }
                case OrOperator.NAME -> {
                    context.isHashJoinPossible = false;
                    context.exit = true;
                    return null;
                }
                case EqOperator.NAME -> {
                    List<Symbol> arguments = function.arguments();

                    var left = arguments.get(0);
                    var leftContext = new Context();
                    left.accept(this, leftContext);

                    var right = arguments.get(1);
                    var rightContext = new Context();
                    right.accept(this, rightContext);
                    if (leftContext.relations.size() == 1 &&
                        rightContext.relations.size() == 1 &&
                        !leftContext.relations.equals(rightContext.relations)) {
                        context.isHashJoinPossible = true;
                    }
                }
                default -> {
                    for (Symbol arg : function.arguments()) {
                        arg.accept(this, context);
                    }
                }
            }
            return null;
        
<no_super_class>