001package gudusoft.gsqlparser.ir.semantic; 002 003import gudusoft.gsqlparser.ir.semantic.binding.RelationBinding; 004 005/** 006 * One visible relation source in a statement's scope. {@link #alias} is 007 * non-null and defaults to the table name when the SQL omits an alias, so 008 * consumers always have a stable local handle to use in 009 * {@link ColumnRef#getRelationAlias()}. 010 * 011 * <p>For most kinds (TABLE / CTE / SUBQUERY / UNION) the source is a 012 * FROM-clause entry on this statement. For 013 * {@link RelationKind#OUTER_REFERENCE} (slice 14) the source is 014 * synthesised by the builder to record an alias that actually belongs to 015 * an enclosing scope's FROM clause; the inner statement carries it so 016 * {@link ColumnRef#getRelationAlias()} resolves uniformly without needing 017 * an explicit "is-outer" flag. 018 * 019 * <p>Join-analysis slice 164 (S3) adds an optional {@link SourceSpan} 020 * pointing at the FROM-clause table reference (incl. alias, e.g. 021 * {@code t1 a}) this relation was read from; the bare table name remains 022 * available via the binding. Existing constructors default it to 023 * {@code null}. {@code RelationSource} uses reference identity (no 024 * {@code equals}/{@code hashCode} override), so the additive span cannot 025 * affect dedupe. 026 */ 027public final class RelationSource { 028 029 /** Sentinel for "no stable instance id assigned" (slice 179). */ 030 public static final int NO_INSTANCE_ID = -1; 031 032 private final String alias; 033 private final RelationBinding binding; 034 private final SourceSpan sourceSpan; 035 private final int instanceId; 036 037 public RelationSource(String alias, RelationBinding binding) { 038 this(alias, binding, null, NO_INSTANCE_ID); 039 } 040 041 public RelationSource(String alias, RelationBinding binding, SourceSpan sourceSpan) { 042 this(alias, binding, sourceSpan, NO_INSTANCE_ID); 043 } 044 045 /** 046 * Full constructor (join-analysis slice 179). {@code sourceSpan} is 047 * optional (null when the parser cannot anchor the relation). 048 * {@code instanceId} is a stable, per-statement-block ordinal (FROM 049 * order: driver first, then each join-item right table left-to-right) 050 * that a {@code JoinEndpoint} of kind RELATION carries too, so an 051 * endpoint links to its exact relation instance without relying on 052 * alias uniqueness / case-folding (R4). {@link #NO_INSTANCE_ID} when 053 * not assigned (e.g. DML / non-join relation lists). 054 */ 055 public RelationSource(String alias, RelationBinding binding, SourceSpan sourceSpan, 056 int instanceId) { 057 if (alias == null || alias.isEmpty()) { 058 throw new IllegalArgumentException("alias must be non-empty"); 059 } 060 if (binding == null) { 061 throw new IllegalArgumentException("binding must not be null"); 062 } 063 this.alias = alias; 064 this.binding = binding; 065 this.sourceSpan = sourceSpan; 066 this.instanceId = instanceId; 067 } 068 069 public String getAlias() { 070 return alias; 071 } 072 073 public RelationBinding getBinding() { 074 return binding; 075 } 076 077 /** 078 * Optional source-text span of this relation reference (join-analysis 079 * slice 164). Null when the parser cannot anchor it. 080 */ 081 public SourceSpan getSourceSpan() { 082 return sourceSpan; 083 } 084 085 /** 086 * Stable per-block FROM-order ordinal (join-analysis slice 179, R4), or 087 * {@link #NO_INSTANCE_ID} when not assigned. A RELATION-kind 088 * {@code JoinEndpoint} carries the same value. 089 */ 090 public int getInstanceId() { 091 return instanceId; 092 } 093}