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