001package gudusoft.gsqlparser.ir.semantic.joinanalysis; 002 003import gudusoft.gsqlparser.ir.semantic.ColumnRef; 004import gudusoft.gsqlparser.ir.semantic.SourceSpan; 005 006import java.util.Objects; 007 008/** 009 * One operand of a {@link Predicate}. Discriminated by 010 * {@link PredicateOperandKind}: 011 * 012 * <ul> 013 * <li>{@link PredicateOperandKind#COLUMN} — carries a resolved 014 * {@link ColumnRef} (whose own resolution/span is populated by 015 * slices 164/165).</li> 016 * <li>{@link PredicateOperandKind#LITERAL} / 017 * {@link PredicateOperandKind#CALL} / 018 * {@link PredicateOperandKind#COMPLEX} — carry no column; the 019 * optional verbatim {@code text} (a SQL substring derived from 020 * {@code sourceSpan}, never reformatted) preserves the shape.</li> 021 * </ul> 022 * 023 * <p>The {@code sourceSpan} is the source of truth for operand text; it 024 * is excluded from neither equality here (these objects live in ordered 025 * lists, not dedupe sets). {@code text}, when present, is the verbatim 026 * substring only. 027 * 028 * <p>Immutable. Introduced by join-analysis slice 162 (S1). 029 */ 030public final class PredicateOperand { 031 032 private final PredicateOperandKind kind; 033 private final ColumnRef column; 034 private final String text; 035 private final SourceSpan sourceSpan; 036 037 private PredicateOperand(PredicateOperandKind kind, ColumnRef column, 038 String text, SourceSpan sourceSpan) { 039 if (kind == null) { 040 throw new IllegalArgumentException("kind must be non-null"); 041 } 042 if (kind == PredicateOperandKind.COLUMN && column == null) { 043 throw new IllegalArgumentException("COLUMN operand requires a non-null ColumnRef"); 044 } 045 if (kind != PredicateOperandKind.COLUMN && column != null) { 046 throw new IllegalArgumentException("non-COLUMN operand must not carry a ColumnRef"); 047 } 048 this.kind = kind; 049 this.column = column; 050 this.text = text; 051 this.sourceSpan = sourceSpan; 052 } 053 054 public static PredicateOperand column(ColumnRef column, SourceSpan span) { 055 return new PredicateOperand(PredicateOperandKind.COLUMN, column, null, span); 056 } 057 058 public static PredicateOperand literal(String text, SourceSpan span) { 059 return new PredicateOperand(PredicateOperandKind.LITERAL, null, text, span); 060 } 061 062 public static PredicateOperand call(String text, SourceSpan span) { 063 return new PredicateOperand(PredicateOperandKind.CALL, null, text, span); 064 } 065 066 public static PredicateOperand complex(String text, SourceSpan span) { 067 return new PredicateOperand(PredicateOperandKind.COMPLEX, null, text, span); 068 } 069 070 public PredicateOperandKind getKind() { 071 return kind; 072 } 073 074 /** Non-null only for {@link PredicateOperandKind#COLUMN}. */ 075 public ColumnRef getColumn() { 076 return column; 077 } 078 079 /** Optional verbatim SQL substring; may be null. */ 080 public String getText() { 081 return text; 082 } 083 084 /** Optional; null when the parser cannot anchor this operand. */ 085 public SourceSpan getSourceSpan() { 086 return sourceSpan; 087 } 088 089 @Override 090 public boolean equals(Object o) { 091 if (this == o) return true; 092 if (!(o instanceof PredicateOperand)) return false; 093 PredicateOperand that = (PredicateOperand) o; 094 return kind == that.kind 095 && Objects.equals(column, that.column) 096 && Objects.equals(text, that.text) 097 && Objects.equals(sourceSpan, that.sourceSpan); 098 } 099 100 @Override 101 public int hashCode() { 102 return Objects.hash(kind, column, text, sourceSpan); 103 } 104 105 @Override 106 public String toString() { 107 switch (kind) { 108 case COLUMN: 109 return column.toString(); 110 default: 111 return kind + (text != null ? "(" + text + ")" : ""); 112 } 113 } 114}