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