Source:
<code>public JoinNode(@JsonProperty("id") PlanNodeId id, @JsonProperty("type") Type type, @JsonProperty("left") PlanNode left, @JsonProperty("right") PlanNode right, @JsonProperty("criteria") List<EquiJoinClause> criteria, @JsonProperty("outputSymbols") List<Symbol> outputSymbols, @JsonProperty("filter") Optional<Expression> filter, @JsonProperty("leftHashSymbol") Optional<Symbol> leftHashSymbol, @JsonProperty("rightHashSymbol") Optional<Symbol> rightHashSymbol, @JsonProperty("distributionType") Optional<DistributionType> distributionType) { super(id); requireNonNull(type, "type is null"); requireNonNull(left, "left is null"); requireNonNull(right, "right is null"); requireNonNull(criteria, "criteria is null"); requireNonNull(outputSymbols, "outputSymbols is null"); requireNonNull(filter, "filter is null"); requireNonNull(leftHashSymbol, "leftHashSymbol is null"); requireNonNull(rightHashSymbol, "rightHashSymbol is null"); requireNonNull(distributionType, "distributionType is null"); this.type = type; this.left = left; this.right = right; this.criteria = ImmutableList.copyOf(criteria); this.outputSymbols = ImmutableList.copyOf(outputSymbols); this.filter = filter; this.leftHashSymbol = leftHashSymbol; this.rightHashSymbol = rightHashSymbol; this.distributionType = distributionType; List<Symbol> inputSymbols = ImmutableList.<Symbol>builder() .addAll(left.getOutputSymbols()) .addAll(right.getOutputSymbols()) .build(); checkArgument(inputSymbols.containsAll(outputSymbols), "Left and right join inputs do not contain all output symbols"); checkArgument(!isCrossJoin() || inputSymbols.equals(outputSymbols), "Cross join does not support output symbols pruning or reordering"); checkArgument((!criteria.isEmpty()) || !leftHashSymbol.isPresent(), "Left hash symbol is only valid in an equijoin"); <START> checkArgument((!criteria.isEmpty()) || !rightHashSymbol.isPresent(), "Right hash symbol is only valid in an equijoin"); <END> }</code><technical_language>positive conditions easier understand, I write: criteria.isEmpty && rightHashSymbols.isPresent()</technical_language>

Target:
public JoinNode(@JsonProperty("id") PlanNodeId id, @JsonProperty("type") Type type, @JsonProperty("left") PlanNode left, @JsonProperty("right") PlanNode right, @JsonProperty("criteria") List<EquiJoinClause> criteria, @JsonProperty("outputSymbols") List<Symbol> outputSymbols, @JsonProperty("filter") Optional<Expression> filter, @JsonProperty("leftHashSymbol") Optional<Symbol> leftHashSymbol, @JsonProperty("rightHashSymbol") Optional<Symbol> rightHashSymbol, @JsonProperty("distributionType") Optional<DistributionType> distributionType) { super(id); requireNonNull(type, "type is null"); requireNonNull(left, "left is null"); requireNonNull(right, "right is null"); requireNonNull(criteria, "criteria is null"); requireNonNull(outputSymbols, "outputSymbols is null"); requireNonNull(filter, "filter is null"); requireNonNull(leftHashSymbol, "leftHashSymbol is null"); requireNonNull(rightHashSymbol, "rightHashSymbol is null"); requireNonNull(distributionType, "distributionType is null"); this.type = type; this.left = left; this.right = right; this.criteria = ImmutableList.copyOf(criteria); this.outputSymbols = ImmutableList.copyOf(outputSymbols); this.filter = filter; this.leftHashSymbol = leftHashSymbol; this.rightHashSymbol = rightHashSymbol; this.distributionType = distributionType; List<Symbol> inputSymbols = ImmutableList.<Symbol>builder() .addAll(left.getOutputSymbols()) .addAll(right.getOutputSymbols()) .build(); checkArgument(inputSymbols.containsAll(outputSymbols), "Left and right join inputs do not contain all output symbols"); checkArgument(!isCrossJoin() || inputSymbols.equals(outputSymbols), "Cross join does not support output symbols pruning or reordering"); checkArgument(!(criteria.isEmpty() && leftHashSymbol.isPresent()), "Left hash symbol is only valid in an equijoin"); checkArgument(!(criteria.isEmpty() && rightHashSymbol.isPresent()), "Right hash symbol is only valid in an equijoin"); }