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}