001package gudusoft.gsqlparser.parser; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.IMetaDatabase; 005import gudusoft.gsqlparser.ISQLStatementHandle; 006import gudusoft.gsqlparser.ITokenHandle; 007import gudusoft.gsqlparser.ITokenListHandle; 008import gudusoft.gsqlparser.compiler.TFrame; 009import gudusoft.gsqlparser.sqlenv.TSQLEnv; 010import gudusoft.gsqlparser.stmt.teradata.utilities.TeradataUtilityType; 011 012import java.io.InputStream; 013import java.util.Stack; 014 015/** 016 * Immutable context carrying all parser inputs and settings. 017 * 018 * <p>This value object encapsulates all inputs required for SQL parsing, 019 * providing clean separation between input configuration and parser implementation. 020 * Once built, a ParserContext instance cannot be modified (immutable). 021 * 022 * <p><b>Design Pattern:</b> Value Object + Builder 023 * <ul> 024 * <li>Immutable: Thread-safe, no defensive copying needed</li> 025 * <li>Builder: Flexible construction with optional parameters</li> 026 * <li>Clean boundaries: Input/Output separation</li> 027 * </ul> 028 * 029 * <p><b>Usage Example:</b> 030 * <pre> 031 * ParserContext context = new ParserContext.Builder(EDbVendor.dbvoracle) 032 * .sqlText("SELECT * FROM employees WHERE salary > 50000") 033 * .enablePartialParsing(true) 034 * .metaDatabase(myMetaDatabase) 035 * .build(); 036 * </pre> 037 * 038 * @see SqlParser 039 * @see SqlParseResult 040 * @since 3.2.0.0 041 */ 042public final class ParserContext { 043 044 // ========== Input Source ========== 045 private final EDbVendor vendor; 046 private final String sqlText; 047 private final String sqlFilename; 048 private final InputStream sqlInputStream; 049 private final String sqlCharset; 050 051 // ========== Parser Options ========== 052 private final boolean enablePartialParsing; 053 private final boolean singlePLBlock; 054 private final boolean onlyNeedRawParseTree; 055 private final TeradataUtilityType teradataUtilityType; 056 057 // ========== Callbacks and Hooks ========== 058 private final ISQLStatementHandle sqlStatementHandle; 059 private final ITokenHandle tokenHandle; 060 private final ITokenListHandle tokenListHandle; 061 062 // ========== Metadata and Environment ========== 063 private final IMetaDatabase metaDatabase; 064 private final TSQLEnv sqlEnv; 065 066 // ========== Diagnostic Settings ========== 067 private final boolean dumpResolverLog; 068 private final boolean enableTimeLogging; 069 070 // ========== State (for special cases) ========== 071 private final Stack<TFrame> frameStack; 072 073 // ========== Parser Reference (for backfilling) ========== 074 /** 075 * Reference to TGSqlParser facade for backfilling metadata. 076 * This allows parsed statements to access parser's vendor and other properties. 077 */ 078 private final Object gsqlparser; // Object type to avoid circular dependency 079 080 /** 081 * Private constructor - use Builder to create instances. 082 */ 083 private ParserContext(Builder builder) { 084 // Input source 085 this.vendor = builder.vendor; 086 this.sqlText = builder.sqlText; 087 this.sqlFilename = builder.sqlFilename; 088 this.sqlInputStream = builder.sqlInputStream; 089 this.sqlCharset = builder.sqlCharset; 090 091 // Parser options 092 this.enablePartialParsing = builder.enablePartialParsing; 093 this.singlePLBlock = builder.singlePLBlock; 094 this.onlyNeedRawParseTree = builder.onlyNeedRawParseTree; 095 this.teradataUtilityType = builder.teradataUtilityType; 096 097 // Callbacks 098 this.sqlStatementHandle = builder.sqlStatementHandle; 099 this.tokenHandle = builder.tokenHandle; 100 this.tokenListHandle = builder.tokenListHandle; 101 102 // Metadata 103 this.metaDatabase = builder.metaDatabase; 104 this.sqlEnv = builder.sqlEnv; 105 106 // Diagnostics 107 this.dumpResolverLog = builder.dumpResolverLog; 108 this.enableTimeLogging = builder.enableTimeLogging; 109 110 // State 111 this.frameStack = builder.frameStack; 112 113 // Parser reference 114 this.gsqlparser = builder.gsqlparser; 115 } 116 117 // ========== Getters (Read-only access) ========== 118 119 public EDbVendor getVendor() { 120 return vendor; 121 } 122 123 public String getSqlText() { 124 return sqlText; 125 } 126 127 public String getSqlFilename() { 128 return sqlFilename; 129 } 130 131 public InputStream getSqlInputStream() { 132 return sqlInputStream; 133 } 134 135 public String getSqlCharset() { 136 return sqlCharset; 137 } 138 139 public boolean isEnablePartialParsing() { 140 return enablePartialParsing; 141 } 142 143 public boolean isSinglePLBlock() { 144 return singlePLBlock; 145 } 146 147 public boolean isOnlyNeedRawParseTree() { 148 return onlyNeedRawParseTree; 149 } 150 151 public TeradataUtilityType getTeradataUtilityType() { 152 return teradataUtilityType; 153 } 154 155 public ISQLStatementHandle getSqlStatementHandle() { 156 return sqlStatementHandle; 157 } 158 159 public ITokenHandle getTokenHandle() { 160 return tokenHandle; 161 } 162 163 public ITokenListHandle getTokenListHandle() { 164 return tokenListHandle; 165 } 166 167 public IMetaDatabase getMetaDatabase() { 168 return metaDatabase; 169 } 170 171 public TSQLEnv getSqlEnv() { 172 return sqlEnv; 173 } 174 175 public boolean isDumpResolverLog() { 176 return dumpResolverLog; 177 } 178 179 public boolean isEnableTimeLogging() { 180 return enableTimeLogging; 181 } 182 183 public Stack<TFrame> getFrameStack() { 184 return frameStack; 185 } 186 187 /** 188 * Get reference to TGSqlParser facade for backfilling statement metadata. 189 * @return TGSqlParser instance or null if not set 190 */ 191 public Object getGsqlparser() { 192 return gsqlparser; 193 } 194 195 /** 196 * Builder for constructing ParserContext instances. 197 * 198 * <p>The Builder pattern provides: 199 * <ul> 200 * <li>Flexible construction with optional parameters</li> 201 * <li>Readable, fluent API</li> 202 * <li>Default values for optional parameters</li> 203 * <li>Immutability of final ParserContext object</li> 204 * </ul> 205 * 206 * <p><b>Example:</b> 207 * <pre> 208 * ParserContext ctx = new ParserContext.Builder(EDbVendor.dbvoracle) 209 * .sqlText("SELECT * FROM dual") 210 * .enablePartialParsing(true) 211 * .metaDatabase(myMetaDb) 212 * .build(); 213 * </pre> 214 */ 215 public static class Builder { 216 // Required parameters 217 private final EDbVendor vendor; 218 219 // Optional parameters with defaults 220 private String sqlText = null; 221 private String sqlFilename = ""; 222 private InputStream sqlInputStream = null; 223 private String sqlCharset = null; 224 225 private boolean enablePartialParsing = true; 226 private boolean singlePLBlock = false; 227 private boolean onlyNeedRawParseTree = false; 228 private TeradataUtilityType teradataUtilityType = null; 229 230 private ISQLStatementHandle sqlStatementHandle = null; 231 private ITokenHandle tokenHandle = null; 232 private ITokenListHandle tokenListHandle = null; 233 234 private IMetaDatabase metaDatabase = null; 235 private TSQLEnv sqlEnv = null; 236 237 private boolean dumpResolverLog = false; 238 private boolean enableTimeLogging = false; 239 240 private Stack<TFrame> frameStack = null; 241 242 private Object gsqlparser = null; 243 244 /** 245 * Create a builder with required vendor parameter. 246 * 247 * @param vendor the database vendor (required) 248 */ 249 public Builder(EDbVendor vendor) { 250 if (vendor == null) { 251 throw new IllegalArgumentException("vendor cannot be null"); 252 } 253 this.vendor = vendor; 254 } 255 256 /** 257 * Set SQL text to parse. 258 * 259 * @param sqlText the SQL text 260 * @return this builder for method chaining 261 */ 262 public Builder sqlText(String sqlText) { 263 this.sqlText = sqlText != null ? sqlText : ""; 264 return this; 265 } 266 267 /** 268 * Set SQL filename to read from. 269 * 270 * @param sqlFilename the SQL filename 271 * @return this builder for method chaining 272 */ 273 public Builder sqlFilename(String sqlFilename) { 274 this.sqlFilename = sqlFilename != null ? sqlFilename : ""; 275 return this; 276 } 277 278 /** 279 * Set SQL input stream to read from. 280 * 281 * @param sqlInputStream the SQL input stream 282 * @return this builder for method chaining 283 */ 284 public Builder sqlInputStream(InputStream sqlInputStream) { 285 this.sqlInputStream = sqlInputStream; 286 return this; 287 } 288 289 /** 290 * Set SQL character set for file reading. 291 * 292 * @param sqlCharset the character set name 293 * @return this builder for method chaining 294 */ 295 public Builder sqlCharset(String sqlCharset) { 296 this.sqlCharset = sqlCharset; 297 return this; 298 } 299 300 /** 301 * Enable/disable partial parsing. 302 * 303 * @param enablePartialParsing true to enable partial parsing 304 * @return this builder for method chaining 305 */ 306 public Builder enablePartialParsing(boolean enablePartialParsing) { 307 this.enablePartialParsing = enablePartialParsing; 308 return this; 309 } 310 311 /** 312 * Set single PL block mode. 313 * 314 * @param singlePLBlock true for single PL block 315 * @return this builder for method chaining 316 */ 317 public Builder singlePLBlock(boolean singlePLBlock) { 318 this.singlePLBlock = singlePLBlock; 319 return this; 320 } 321 322 /** 323 * Set if only raw parse tree is needed. 324 * 325 * @param onlyNeedRawParseTree true if only raw parse tree needed 326 * @return this builder for method chaining 327 */ 328 public Builder onlyNeedRawParseTree(boolean onlyNeedRawParseTree) { 329 this.onlyNeedRawParseTree = onlyNeedRawParseTree; 330 return this; 331 } 332 333 /** 334 * Set Teradata utility type. 335 * 336 * @param teradataUtilityType the Teradata utility type 337 * @return this builder for method chaining 338 */ 339 public Builder teradataUtilityType(TeradataUtilityType teradataUtilityType) { 340 this.teradataUtilityType = teradataUtilityType; 341 return this; 342 } 343 344 /** 345 * Set SQL statement handle callback. 346 * 347 * @param sqlStatementHandle the statement handle callback 348 * @return this builder for method chaining 349 */ 350 public Builder sqlStatementHandle(ISQLStatementHandle sqlStatementHandle) { 351 this.sqlStatementHandle = sqlStatementHandle; 352 return this; 353 } 354 355 /** 356 * Set token handle callback. 357 * 358 * @param tokenHandle the token handle callback 359 * @return this builder for method chaining 360 */ 361 public Builder tokenHandle(ITokenHandle tokenHandle) { 362 this.tokenHandle = tokenHandle; 363 return this; 364 } 365 366 /** 367 * Set token list handle callback. 368 * 369 * @param tokenListHandle the token list handle callback 370 * @return this builder for method chaining 371 */ 372 public Builder tokenListHandle(ITokenListHandle tokenListHandle) { 373 this.tokenListHandle = tokenListHandle; 374 return this; 375 } 376 377 /** 378 * Set metadata database for column/table resolution. 379 * 380 * @param metaDatabase the metadata database 381 * @return this builder for method chaining 382 */ 383 public Builder metaDatabase(IMetaDatabase metaDatabase) { 384 this.metaDatabase = metaDatabase; 385 return this; 386 } 387 388 /** 389 * Set SQL environment. 390 * 391 * @param sqlEnv the SQL environment 392 * @return this builder for method chaining 393 */ 394 public Builder sqlEnv(TSQLEnv sqlEnv) { 395 this.sqlEnv = sqlEnv; 396 return this; 397 } 398 399 /** 400 * Enable/disable resolver log dumping. 401 * 402 * @param dumpResolverLog true to dump resolver log 403 * @return this builder for method chaining 404 */ 405 public Builder dumpResolverLog(boolean dumpResolverLog) { 406 this.dumpResolverLog = dumpResolverLog; 407 return this; 408 } 409 410 /** 411 * Enable/disable time logging. 412 * 413 * @param enableTimeLogging true to enable time logging 414 * @return this builder for method chaining 415 */ 416 public Builder enableTimeLogging(boolean enableTimeLogging) { 417 this.enableTimeLogging = enableTimeLogging; 418 return this; 419 } 420 421 /** 422 * Set frame stack for compiler context. 423 * 424 * @param frameStack the frame stack 425 * @return this builder for method chaining 426 */ 427 public Builder frameStack(Stack<TFrame> frameStack) { 428 this.frameStack = frameStack; 429 return this; 430 } 431 432 /** 433 * Set reference to TGSqlParser facade for backfilling metadata. 434 * @param gsqlparser TGSqlParser instance 435 * @return this builder 436 */ 437 public Builder gsqlparser(Object gsqlparser) { 438 this.gsqlparser = gsqlparser; 439 return this; 440 } 441 442 /** 443 * Build the immutable ParserContext. 444 * 445 * @return a new immutable ParserContext instance 446 */ 447 public ParserContext build() { 448 return new ParserContext(this); 449 } 450 } 451 452 @Override 453 public String toString() { 454 return "ParserContext{" + 455 "vendor=" + vendor + 456 ", sqlText=" + (sqlText != null && sqlText.length() > 50 ? 457 sqlText.substring(0, 50) + "..." : sqlText) + 458 ", sqlFilename='" + sqlFilename + '\'' + 459 ", hasInputStream=" + (sqlInputStream != null) + 460 '}'; 461 } 462}