001package gudusoft.gsqlparser.resolver2.iterative;
002
003import gudusoft.gsqlparser.resolver2.model.ResolutionStatistics;
004
005/**
006 * Represents a single pass of iterative resolution.
007 *
008 * <p>During iterative resolution, the resolver may need multiple passes
009 * to fully resolve all columns. Each pass attempts to resolve more columns
010 * based on information discovered in previous passes.
011 *
012 * <p>Example scenario requiring multiple passes:
013 * <pre>
014 * WITH cte1 AS (SELECT * FROM t1),         -- Pass 1: Can't fully resolve *
015 *      cte2 AS (SELECT c1, c2 FROM cte1)   -- Pass 1: References cte1.c1, c1.c2
016 * SELECT * FROM cte2                       -- Pass 2: Can now resolve based on cte2
017 * </pre>
018 *
019 * <p>Each pass tracks:
020 * - Pass number (1, 2, 3, ...)
021 * - Columns resolved in this pass
022 * - Columns still unresolved
023 * - Whether progress was made
024 * - Time taken
025 */
026public class ResolutionPass {
027
028    /** Pass number (1-based) */
029    private final int passNumber;
030
031    /** Statistics before this pass */
032    private final ResolutionStatistics beforeStats;
033
034    /** Statistics after this pass */
035    private ResolutionStatistics afterStats;
036
037    /** Number of columns resolved in this pass */
038    private int columnsResolvedInPass;
039
040    /** Number of new inferences made in this pass */
041    private int newInferences;
042
043    /** Time taken for this pass (milliseconds) */
044    private long timeTaken;
045
046    /** Start time of this pass */
047    private final long startTime;
048
049    /** Whether this pass made progress */
050    private boolean madeProgress;
051
052    /** Reason for stopping (if this was the last pass) */
053    private String stopReason;
054
055    public ResolutionPass(int passNumber, ResolutionStatistics beforeStats) {
056        this.passNumber = passNumber;
057        this.beforeStats = beforeStats;
058        this.startTime = System.currentTimeMillis();
059        this.madeProgress = false;
060    }
061
062    /**
063     * Complete this pass with final statistics.
064     *
065     * @param afterStats statistics after this pass
066     */
067    public void complete(ResolutionStatistics afterStats) {
068        this.afterStats = afterStats;
069        this.timeTaken = System.currentTimeMillis() - startTime;
070
071        // Calculate progress
072        if (beforeStats != null && afterStats != null) {
073            int beforeResolved = beforeStats.getExactMatches();
074            int afterResolved = afterStats.getExactMatches();
075            this.columnsResolvedInPass = afterResolved - beforeResolved;
076            this.madeProgress = this.columnsResolvedInPass > 0;
077        }
078    }
079
080    /**
081     * Set the number of new inferences made in this pass.
082     *
083     * @param count inference count
084     */
085    public void setNewInferences(int count) {
086        this.newInferences = count;
087        if (count > 0) {
088            this.madeProgress = true;
089        }
090    }
091
092    /**
093     * Set the reason why iteration stopped after this pass.
094     *
095     * @param reason stop reason
096     */
097    public void setStopReason(String reason) {
098        this.stopReason = reason;
099    }
100
101    public int getPassNumber() {
102        return passNumber;
103    }
104
105    public ResolutionStatistics getBeforeStats() {
106        return beforeStats;
107    }
108
109    public ResolutionStatistics getAfterStats() {
110        return afterStats;
111    }
112
113    public int getColumnsResolvedInPass() {
114        return columnsResolvedInPass;
115    }
116
117    public int getNewInferences() {
118        return newInferences;
119    }
120
121    public long getTimeTaken() {
122        return timeTaken;
123    }
124
125    public boolean madeProgress() {
126        return madeProgress;
127    }
128
129    public String getStopReason() {
130        return stopReason;
131    }
132
133    /**
134     * Get a summary of this pass.
135     *
136     * @return summary string
137     */
138    public String getSummary() {
139        StringBuilder sb = new StringBuilder();
140        sb.append("Pass ").append(passNumber).append(": ");
141
142        if (afterStats != null) {
143            sb.append("resolved ").append(columnsResolvedInPass).append(" columns");
144            if (newInferences > 0) {
145                sb.append(", made ").append(newInferences).append(" inferences");
146            }
147            sb.append(" (").append(timeTaken).append("ms)");
148        } else {
149            sb.append("incomplete");
150        }
151
152        if (stopReason != null) {
153            sb.append(" - ").append(stopReason);
154        }
155
156        return sb.toString();
157    }
158
159    @Override
160    public String toString() {
161        return getSummary();
162    }
163}