001package gudusoft.gsqlparser.ir.semantic.joinanalysis; 002 003import gudusoft.gsqlparser.ir.semantic.SourceSpan; 004 005import java.util.Objects; 006 007/** 008 * A single conjunct extracted from an ON or WHERE condition (GAP 2). A 009 * predicate is the binary shape {@code left <operator> right}, except 010 * for {@link PredicateKind#NULL_CHECK} (which has only a left operand) 011 * and {@link PredicateKind#COMPLEX} (which preserves an undecomposed 012 * sub-tree, typically with a single operand). 013 * 014 * <ul> 015 * <li>{@code operator} is the verbatim comparison token 016 * ({@code =}, {@code <>}, {@code >}, {@code IS NULL}, 017 * {@code IS NOT NULL}, {@code BETWEEN}, ...). May be null for a 018 * {@link PredicateKind#COMPLEX} predicate.</li> 019 * <li>{@code leftOperand} is non-null.</li> 020 * <li>{@code rightOperand} is null for {@link PredicateKind#NULL_CHECK} 021 * and may be null for {@link PredicateKind#COMPLEX}.</li> 022 * <li>{@code sourceSpan} covers the whole predicate; null when the 023 * parser cannot anchor it.</li> 024 * </ul> 025 * 026 * <p>Immutable. Introduced by join-analysis slice 162 (S1); produced by 027 * {@code PredicateTreeBuilder} in slice 166 (S5). 028 */ 029public final class Predicate { 030 031 private final PredicateKind kind; 032 private final String operator; 033 private final PredicateOperand leftOperand; 034 private final PredicateOperand rightOperand; 035 private final SourceSpan sourceSpan; 036 037 public Predicate(PredicateKind kind, String operator, 038 PredicateOperand leftOperand, PredicateOperand rightOperand, 039 SourceSpan sourceSpan) { 040 if (kind == null) { 041 throw new IllegalArgumentException("kind must be non-null"); 042 } 043 if (leftOperand == null) { 044 throw new IllegalArgumentException("leftOperand must be non-null"); 045 } 046 if (kind == PredicateKind.NULL_CHECK && rightOperand != null) { 047 throw new IllegalArgumentException("NULL_CHECK predicate must not have a right operand"); 048 } 049 this.kind = kind; 050 this.operator = operator; 051 this.leftOperand = leftOperand; 052 this.rightOperand = rightOperand; 053 this.sourceSpan = sourceSpan; 054 } 055 056 public PredicateKind getKind() { 057 return kind; 058 } 059 060 /** Verbatim comparison token; may be null for COMPLEX. */ 061 public String getOperator() { 062 return operator; 063 } 064 065 public PredicateOperand getLeftOperand() { 066 return leftOperand; 067 } 068 069 /** Null for NULL_CHECK; may be null for COMPLEX. */ 070 public PredicateOperand getRightOperand() { 071 return rightOperand; 072 } 073 074 /** Optional; null when the parser cannot anchor this predicate. */ 075 public SourceSpan getSourceSpan() { 076 return sourceSpan; 077 } 078 079 @Override 080 public boolean equals(Object o) { 081 if (this == o) return true; 082 if (!(o instanceof Predicate)) return false; 083 Predicate that = (Predicate) o; 084 return kind == that.kind 085 && Objects.equals(operator, that.operator) 086 && Objects.equals(leftOperand, that.leftOperand) 087 && Objects.equals(rightOperand, that.rightOperand) 088 && Objects.equals(sourceSpan, that.sourceSpan); 089 } 090 091 @Override 092 public int hashCode() { 093 return Objects.hash(kind, operator, leftOperand, rightOperand, sourceSpan); 094 } 095 096 @Override 097 public String toString() { 098 StringBuilder sb = new StringBuilder(); 099 sb.append('(').append(leftOperand); 100 if (operator != null) sb.append(' ').append(operator); 101 if (rightOperand != null) sb.append(' ').append(rightOperand); 102 sb.append(") ").append(kind); 103 return sb.toString(); 104 } 105}