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}