001package gudusoft.gsqlparser.ir.builder.mssql;
002
003import gudusoft.gsqlparser.TCustomSqlStatement;
004import gudusoft.gsqlparser.TStatementList;
005import gudusoft.gsqlparser.analyzer.v2.AnalyzerV2Config;
006import gudusoft.gsqlparser.ir.bound.BoundProgram;
007import gudusoft.gsqlparser.ir.bound.BoundScope;
008import gudusoft.gsqlparser.ir.bound.EScopeKind;
009import gudusoft.gsqlparser.ir.builder.IBoundIRBuilder;
010
011/**
012 * SQL Server (T-SQL) implementation of {@link IBoundIRBuilder}.
013 * <p>
014 * Orchestrates two internal phases:
015 * <ol>
016 *   <li>Create global scope</li>
017 *   <li>Run {@link TsqlSymbolCollector} (visitor) to extract routines, variables, calls, table refs</li>
018 * </ol>
019 * <p>
020 * <b>Important</b>: Unlike the Oracle builder, this builder does NOT invoke the
021 * {@link MssqlRoutineRefResolver} inside {@code build()}. For multi-script analysis,
022 * the resolver must run after merging all partial BoundPrograms so that cross-file
023 * references can be resolved against the full routine index.
024 * <p>
025 * For single-file analysis, the caller should invoke
026 * {@code MssqlRoutineRefResolver.resolve(program)} after {@code build()}.
027 */
028public class MssqlBoundIRBuilder implements IBoundIRBuilder {
029
030    @Override
031    public BoundProgram build(TStatementList stmts, AnalyzerV2Config config) {
032        return build(stmts, config, "");
033    }
034
035    /**
036     * Builds a partial BoundProgram from the given statements with a file identifier.
037     * <p>
038     * All {@code BoundRoutineRef} instances in the returned program are in
039     * {@code UNRESOLVED_SOFT} state. The caller is responsible for running
040     * {@link MssqlRoutineRefResolver#resolve(BoundProgram)} after merging.
041     *
042     * @param stmts  parsed SQL statements
043     * @param config analyzer configuration (may be null)
044     * @param fileId file identifier for cross-script traceability
045     * @return partial BoundProgram (refs unresolved)
046     */
047    public BoundProgram build(TStatementList stmts, AnalyzerV2Config config, String fileId) {
048        BoundProgram program = new BoundProgram();
049
050        // Phase 1: Create global scope
051        BoundScope globalScope = new BoundScope(EScopeKind.GLOBAL, null, null);
052        program.addScope(globalScope);
053
054        if (stmts == null || stmts.size() == 0) {
055            return program;
056        }
057
058        // Phase 2: Walk AST with TsqlSymbolCollector (only collection, no resolve)
059        TsqlSymbolCollector collector = new TsqlSymbolCollector(program, globalScope, config, fileId);
060        for (int i = 0; i < stmts.size(); i++) {
061            TCustomSqlStatement stmt = stmts.get(i);
062            if (stmt != null) {
063                stmt.acceptChildren(collector);
064            }
065        }
066
067        // Phase 3 (resolve) is NOT executed here.
068        // For single-file: caller invokes MssqlRoutineRefResolver.resolve(program)
069        // For multi-file: caller merges programs first, then resolves.
070
071        return program;
072    }
073}