001package gudusoft.gsqlparser; 002 003import gudusoft.gsqlparser.compiler.TASTEvaluator; 004import gudusoft.gsqlparser.compiler.TContext; 005import gudusoft.gsqlparser.compiler.TGlobalScope; 006import gudusoft.gsqlparser.compiler.TFrame; 007import gudusoft.gsqlparser.nodes.*; 008import gudusoft.gsqlparser.nodes.teradata.TTeradataHelper; 009import gudusoft.gsqlparser.resolver.*; 010import gudusoft.gsqlparser.resolver2.TSQLResolver2; 011import gudusoft.gsqlparser.resolver2.TSQLResolverConfig; 012import gudusoft.gsqlparser.resolver2.binding.BindingDiagnostic; 013import gudusoft.gsqlparser.resolver2.binding.BindingResult; 014import gudusoft.gsqlparser.sqlcmds.ISqlCmds; 015import gudusoft.gsqlparser.sqlcmds.SqlCmdsFactory; 016import gudusoft.gsqlparser.sqlenv.TSQLEnv; 017import gudusoft.gsqlparser.stmt.*; 018import gudusoft.gsqlparser.stmt.dax.TDaxEvaluateStmt; 019import gudusoft.gsqlparser.stmt.dax.TDaxExprStmt; 020import gudusoft.gsqlparser.stmt.greenplum.TSlashCommand; 021import gudusoft.gsqlparser.stmt.mssql.TMssqlBlock; 022import gudusoft.gsqlparser.stmt.mssql.TMssqlCreateProcedure; 023import gudusoft.gsqlparser.stmt.mssql.TMssqlExecute; 024import gudusoft.gsqlparser.stmt.mysql.TMySQLSource; 025import gudusoft.gsqlparser.stmt.oracle.TPlsqlCreatePackage; 026import gudusoft.gsqlparser.stmt.oracle.TSqlplusCmdStatement; 027import gudusoft.gsqlparser.stmt.snowflake.TCreateTaskStmt; 028import gudusoft.gsqlparser.stmt.teradata.TTeradataBTEQCmd; 029import gudusoft.gsqlparser.stmt.teradata.TTeradataFastExportCmd; 030import gudusoft.gsqlparser.stmt.teradata.TTeradataFastLoadCmd; 031import gudusoft.gsqlparser.stmt.teradata.TTeradataMultiLoadCmd; 032import gudusoft.gsqlparser.stmt.teradata.utilities.BteqCmdType; 033import gudusoft.gsqlparser.stmt.teradata.utilities.TeradataUtilityType; 034import gudusoft.gsqlparser.util.TSnowflakeParameterChecker; 035import gudusoft.gsqlparser.parser.SqlParser; 036import gudusoft.gsqlparser.parser.ParserContext; 037import gudusoft.gsqlparser.parser.SqlParseResult; 038import gudusoft.gsqlparser.parser.AbstractSqlParser; 039 040 041import java.io.*; 042import java.nio.charset.Charset; 043import java.security.MessageDigest; 044import java.security.NoSuchAlgorithmException; 045import java.text.DateFormat; 046import java.text.ParseException; 047import java.util.*; 048 049import static gudusoft.gsqlparser.ESqlStatementType.*; 050 051 052/** 053 * This is the first class people start to use this SQL parser library. 054 * This class includes a lexer and a parser. The lexer is used to tokenize the input SQL, turn the input SQL text 055 * into a list of source tokens. The parser use this list source token as input, using the grammar rule of the specified 056 * database vendor to check the syntax of the input SQL and build the parse tree of this SQL if there is no syntax error 057 * in the input SQL. 058 * <p></p> 059 * Creating a SQL parser by specifing {@link gudusoft.gsqlparser.EDbObjectType a database vendor}, 060 * then set SQL script text via {@link #setSqltext} method or reading the input SQL from a file via 061 * {@link #setSqlfilename} method. 062 * <p></p> 063 * After that, call one of the following methods to achieve what you need: 064 * <ul> 065 * <li>{@link #tokenizeSqltext}, turns the input SQL into a sequence of token which is the 066 * basic lexis element of SQL syntax. Token is categorized as keyword, identifier, 067 * number, operator, whitespace and other types. All source tokens can be fetched 068 * via the {@link #getSourcetokenlist()} method</li> 069 * 070 * <li>{@link #getrawsqlstatements}, separates the SQL statements in the input SQL script without 071 * doing syntax check, use the {@link #getSqlstatements()} method to get a list of SQL statements 072 * which is the sub-class of {@link TCustomSqlStatement}, get SQL statement type 073 * via the {@link TCustomSqlStatement#sqlstatementtype} field, and string representation of 074 * each SQL statement via the {@link TCustomSqlStatement#toString} method. All source tokens in this SQL statement 075 * is available by using {@link TCustomSqlStatement#sourcetokenlist} filed. 076 * Since no parse tree is built by calling this method, no further detailed information about the SQL statement is available. 077 * </li> 078 * 079 * <li>{@link #parse}, Check syntax of the input SQL, doing some kind of semantic analysis without connecting to 080 * a real database. 081 * This method will do a in-depth analysis of the input SQL such as building the link between table and columns. 082 * The parse tree of the input SQL is available after calling this method. 083 * </li> 084 * </ul> 085 * 086 * The parser checks the syntax of those SQL statements one by one. If syntax error is found in a SQL statement, 087 * an error will be logged, no parse tree will be built for this SQL statement, 088 * the error message can be fetched using the {@link #getErrormessage()} method. 089 * <p></p> 090 * The syntax error in one SQL statement doesn't prevent the parser continue to check the syntax of the next SQL statement. 091 * After checking syntax of all SQL statements, use the {@link #getErrorCount()} method to get the total number of errors. 092 * <p></p> 093 * A syntax error in a SQL stored procedure will cease this parser to check syntax of the rest SQL statements 094 * in this stored procedure. 095 * 096 * <p>Format SQL script can be done after calling {@link #parse()}. 097 * <code> 098 * 099 * int ret = sqlparser.parse(); 100 * if (ret == 0){ 101 * GFmtOpt option = GFmtOptFactory.newInstance(); 102 * String result = FormatterFactory.pp(sqlparser, option); 103 * System.out.println(result); 104 * }else{ 105 * System.out.println(sqlparser.getErrormessage()); 106 * } 107 * 108 * </code> 109 * 110 * <p> After paring SQL script, all parse tree nodes are available for use, some of use cases 111 * are: 112 * <ul> 113 * <li>Table/column impact analysis</li> 114 * <li>SQL rewriting</li> 115 * <li>SQL translate between different databases</li> 116 * <li>SQL migration analysis</li> 117 * <li>Help to anti SQL injection</li> 118 * <li><a href="http://support.sqlparser.com/">More use cases</a></li> 119 * </ul> 120 * 121 * <p>Typically, SQL parse tree nodes generated by this SQL Parser were closely related to SQL 122 * elements defined in database vendor's SQL reference book. here is a brief summary of some 123 * most used SQL elements and corresponding classes defined in this SQL parser. 124 * <ul> 125 * <li>SQL identifier: {@link gudusoft.gsqlparser.nodes.TObjectName}</li> 126 * <li>SQL literal: {@link gudusoft.gsqlparser.nodes.TConstant}</li> 127 * <li>SQL datatype: {@link gudusoft.gsqlparser.nodes.TTypeName}</li> 128 * <li>SQL function: {@link gudusoft.gsqlparser.nodes.TFunctionCall}</li> 129 * <li>SQL constraint: {@link gudusoft.gsqlparser.nodes.TConstraint}</li> 130 * <li>SQL expression/condition: {@link gudusoft.gsqlparser.nodes.TExpression}</li> 131 * <li>SQL select list item: {@link gudusoft.gsqlparser.nodes.TResultColumn}</li> 132 * <li>More: {@link gudusoft.gsqlparser.nodes}</li> 133 * </ul> 134 * 135 * <p> Some major SQL statements: 136 * <ul> 137 * <li>Select: {@link gudusoft.gsqlparser.stmt.TSelectSqlStatement}</li> 138 * <li>Delete: {@link gudusoft.gsqlparser.stmt.TDeleteSqlStatement}</li> 139 * <li>Insert: {@link gudusoft.gsqlparser.stmt.TInsertSqlStatement}</li> 140 * <li>Update: {@link gudusoft.gsqlparser.stmt.TUpdateSqlStatement}</li> 141 * <li>Create table: {@link gudusoft.gsqlparser.stmt.TCreateTableSqlStatement}</li> 142 * <li>More: {@link gudusoft.gsqlparser.stmt}</li> 143 * </ul> 144 * 145 * <p>Stored procedure</p> 146 * <ul> 147 * <li>Create function: {@link gudusoft.gsqlparser.stmt.db2.TDb2CreateFunction }, 148 * {@link gudusoft.gsqlparser.stmt.mssql.TMssqlCreateProcedure}, 149 * {@link gudusoft.gsqlparser.stmt.mysql.TMySQLCreateFunction}, 150 * {@link gudusoft.gsqlparser.stmt.oracle.TPlsqlCreateFunction}</li> 151 * <li>Create procedure: {@link gudusoft.gsqlparser.stmt.db2.TDb2CreateProcedure}, 152 * {@link gudusoft.gsqlparser.stmt.mssql.TMssqlCreateProcedure}, 153 * {@link gudusoft.gsqlparser.stmt.mysql.TMySQLCreateProcedure}, 154 * {@link gudusoft.gsqlparser.stmt.oracle.TPlsqlCreateProcedure}</li> 155 * <li>Create trigger: {@link gudusoft.gsqlparser.stmt.TCreateTriggerStmt}, 156 * {@link gudusoft.gsqlparser.stmt.oracle.TPlsqlCreateTrigger}</li> 157 * <li>Create package: {@link gudusoft.gsqlparser.stmt.oracle.TPlsqlCreatePackage}</li> 158 * </ul> 159 * 160 * For all available SQL parse tree node classes, please check the API reference. 161 * 162 * 163 */ 164public class TGSqlParser { 165 166 167 168 169 public void setSqlCharset(String sqlCharset) { 170 this.sqlCharset = sqlCharset; 171 } 172 173 public String getSqlCharset() { 174 return sqlCharset; 175 } 176 177 private String sqlCharset = null; 178 179 // Thread-local to ensure thread-safe per-thread vendor tracking 180 private static final ThreadLocal<EDbVendor> currentDBVendorThreadLocal = ThreadLocal.withInitial(() -> EDbVendor.dbvoracle); 181 182 /** 183 * @deprecated Use {@link #getCurrentDBVendor()} and {@link #setCurrentDBVendor(EDbVendor)} instead. 184 * Direct field access is not thread-safe. 185 */ 186 public static EDbVendor currentDBVendor = EDbVendor.dbvoracle; 187 188 public static EDbVendor getCurrentDBVendor() { 189 return currentDBVendorThreadLocal.get(); 190 } 191 192 public static void setCurrentDBVendor(EDbVendor vendor) { 193 currentDBVendorThreadLocal.set(vendor); 194 currentDBVendor = vendor; // maintain backward compatibility for single-threaded code 195 } 196 197 198 /** 199 * Turn the string name of database to dbvendor 200 * <ul> 201 * <li>access: EDbVendor.dbvaccess</li> 202 * <li>ansi: EDbVendor.dbvansi</li> 203 * <li>bigquery: EDbVendor.dbvbigquery</li> 204 * <li>couchbase: EDbVendor.dbvcouchbase</li> 205 * <li>dax: EDbVendor.dbvdax</li> 206 * <li>db2: EDbVendor.dbvdb2</li> 207 * <li>firebird: EDbVendor.dbvfirebird</li> 208 * <li>generic: EDbVendor.dbvgeneric</li> 209 * <li>greenplum: EDbVendor.dbvgreenplum</li> 210 * <li>hana: EDbVendor.dbvhana</li> 211 * <li>hive: EDbVendor.dbvhive</li> 212 * <li>impala: EDbVendor.dbvimpala</li> 213 * <li>informix: EDbVendor.dbvinformix</li> 214 * <li>mdx: EDbVendor.dbvmdx</li> 215 * <li>mssql or sqlserver: EDbVendor.dbvmssql</li> 216 * <li>mysql: EDbVendor.dbvmysql</li> 217 * <li>netezza: EDbVendor.dbvnetezza</li> 218 * <li>odbc: EDbVendor.dbvodbc</li> 219 * <li>openedge: EDbVendor.dbvopenedge</li> 220 * <li>oracle: EDbVendor.dbvoracle</li> 221 * <li>postgresql or postgres: EDbVendor.dbvpostgresql</li> 222 * <li>redshift: EDbVendor.dbvredshift</li> 223 * <li>snowflake: EDbVendor.dbvsnowflake</li> 224 * <li>sybase: EDbVendor.dbvsybase</li> 225 * <li>teradata: EDbVendor.dbvteradata</li> 226 * <li>vertica: EDbVendor.dbvvertica</li> 227 * </ul> 228 * @param dbVendorName 229 * @return dbvendor 230 */ 231 public static EDbVendor getDBVendorByName(String dbVendorName){ 232 return EDbVendor.valueOfWithDefault(dbVendorName); 233 } 234 235// public void teradataCmds(){ 236// // int cnt = 0; 237// //((TLexerTeradata)getFlexer()). 238//// for(int i=0;i<TLexerTeradata.bteqCmdList.size();i++){ 239//// for(int j=0;j<TLexerTeradata.multiLoadCmdList.size();j++){ 240//// if (TLexerTeradata.bteqCmdList.get(i).toString().equalsIgnoreCase(TLexerTeradata.multiLoadCmdList.get(j).toString())){ 241//// System.out.println("multiLoad: "+TLexerTeradata.bteqCmdList.get(i).toString()); 242//// } 243//// } 244//// for(int j=0;j<TLexerTeradata.fastExportCmdList.size();j++){ 245//// if (TLexerTeradata.bteqCmdList.get(i).toString().equalsIgnoreCase(TLexerTeradata.fastExportCmdList.get(j).toString())){ 246//// System.out.println("fastExport: "+TLexerTeradata.bteqCmdList.get(i).toString()); 247//// } 248//// } 249//// for(int j=0;j<TLexerTeradata.fastLoadCmdList.size();j++){ 250//// if (TLexerTeradata.bteqCmdList.get(i).toString().equalsIgnoreCase(TLexerTeradata.fastLoadCmdList.get(j).toString())){ 251//// System.out.println("FastLoad: "+TLexerTeradata.bteqCmdList.get(i).toString()); 252//// } 253//// } 254//// } //bteqCmdList 255// 256//// for(int i=0;i<TLexerTeradata.fastLoadCmdList.size();i++){ 257//// for(int j=0;j<TLexerTeradata.multiLoadCmdList.size();j++){ 258//// if (TLexerTeradata.fastLoadCmdList.get(i).toString().equalsIgnoreCase(TLexerTeradata.multiLoadCmdList.get(j).toString())){ 259//// System.out.println("multiLoad: "+TLexerTeradata.fastLoadCmdList.get(i).toString()); 260//// } 261//// } 262//// for(int j=0;j<TLexerTeradata.fastExportCmdList.size();j++){ 263//// if (TLexerTeradata.fastLoadCmdList.get(i).toString().equalsIgnoreCase(TLexerTeradata.fastExportCmdList.get(j).toString())){ 264//// System.out.println("fastExport: "+TLexerTeradata.fastLoadCmdList.get(i).toString()); 265//// } 266//// } 267//// 268//// } 269// 270// } 271 272 private Stack<TFrame> frameStack = null; 273 274 public Stack<TFrame> getFrameStack(){ 275 if (frameStack == null){ 276 frameStack = new Stack<TFrame>(); 277 } 278 279 return frameStack; 280 } 281 282 public void setFrameStack(Stack<TFrame> frameStack) { 283 this.frameStack = frameStack; 284 } 285 286 void closeFileStream(){ 287 if (streamFromSqlFile != null) { 288 try { 289 streamFromSqlFile.close(); 290 } catch (IOException e) { 291 e.printStackTrace(); 292 } 293 } 294 } 295 296 FileInputStream streamFromSqlFile = null; 297 InputStreamReader sqlStreamReader = null; 298 /** 299 * A sequence of source tokens created by the lexer after tokenize the input SQL 300 * 301 * @return a sequence of source tokens 302 */ 303 public TSourceTokenList getSourcetokenlist() { 304 return sourcetokenlist; 305 } 306 307 /** 308 * A list of SQL statements created by the parser. 309 * If this list is created after calling the {@link #getrawsqlstatements} method, the syntax of each SQL statement 310 * is not checked and the parse tree of each statement is not created. If the {@link #parse} method is called to build 311 * this SQL statement list, then every thing is ready. 312 * 313 * @return a list of SQL statement 314 */ 315 public TStatementList getSqlstatements() { 316 return sqlstatements; 317 } 318 319 enum stored_procedure_status {start,is_as,body,bodyend,end, cursor_declare}; 320 enum stored_procedure_type {function,procedure,package_spec,package_body, block_with_begin,block_with_declare, 321 create_trigger,create_library,cursor_in_package_spec,others}; 322 323 static final int stored_procedure_nested_level = 1024; 324 325 /** 326 ** The input SQL Text. 327 ** If {@link #sqlfilename} is specified, then this field will be ignored. 328 */ 329 public String sqltext; 330 331 /** 332 * set the input SQL text, If {@link #sqlfilename} is specified before this method, the parser will using 333 * the SQL text in this field instead of read SQL from {@link #sqlfilename}. 334 * 335 * @param sqltext the input SQL text 336 */ 337 public void setSqltext(String sqltext) { 338 this.sqltext = sqltext; 339 this.sqlfilename = ""; 340 this.sqlInputStream = null; 341 } 342 343 /** 344 * The SQL text that being processed. 345 * 346 * @return the SQL text that being processed 347 */ 348 public String getSqltext() { 349 return sqltext; 350 } 351 352 353 /** 354 ** The input SQL will be read from this file 355 * 356 ** If field is specified, then {@link #sqltext} will be ignored. 357 * This must be the full path to the file, relative path doesn't work. 358 */ 359 public String sqlfilename; 360 361 362 /** 363 * set the filename from which the input SQL will be read. 364 * 365 * @param sqlfilename the SQL file name from which the input SQL will be read 366 */ 367 public void setSqlfilename(String sqlfilename) { 368 this.sqlfilename = sqlfilename; 369 this.sqltext = ""; 370 this.sqlInputStream = null; 371 } 372 373 /** 374 * The input SQL filename. This parser can process the unicode encoded SQL file. 375 * 376 * @return the input SQL filename 377 */ 378 public String getSqlfilename() { 379 return sqlfilename; 380 } 381 382 /** 383 * set the InputStream from which SQL will be read. 384 * If this method is called, {@link #sqlfilename} and {@link #sqltext} will be ignored. 385 * 386 * @param sqlInputStream the InputStream from which SQL will be read 387 */ 388 public void setSqlInputStream(InputStream sqlInputStream) { 389 if (sqlInputStream instanceof BufferedInputStream){ 390 this.sqlInputStream = (BufferedInputStream)sqlInputStream; 391 }else{ 392 this.sqlInputStream = new BufferedInputStream(sqlInputStream); 393 } 394 395 this.sqlfilename = ""; 396 this.sqltext = ""; 397 } 398 399 private BufferedInputStream sqlInputStream; 400 401 /** 402 * the InputStream from which SQL will be read 403 * @return the InputStream from which SQL will be read 404 */ 405 public InputStream getSqlInputStream() { 406 return sqlInputStream; 407 } 408 409 /** 410 ** Tokens generated by lexer from the input SQL script. 411 * Tokens are always available even if there are syntax errors in input the SQL script. 412 */ 413 public TSourceTokenList sourcetokenlist; 414 415 /** 416 ** SQL statements generated by this parser from the input SQL script. 417 * statements are always available even if there are syntax errors in input SQL script. 418 * if there is no syntax error in a statement, you can access the parse tree of the statement to fetch more information 419 * such as tables, columns, etc. 420 */ 421 public TStatementList sqlstatements; 422 423 /** 424 * The TSQLResolver2 instance used for name resolution when ENABLE_RESOLVER2 is set. 425 * Unlike TSQLResolver which is created as a local variable, TSQLResolver2 is stored 426 * as a property so users can retrieve name resolution results after parsing. 427 * 428 * Usage: 429 * <pre> 430 * TBaseType.setEnableResolver(false); // Disable old resolver 431 * TBaseType.setEnableResolver2(true); // Enable new resolver 432 * 433 * TGSqlParser parser = new TGSqlParser(EDbVendor.dbvoracle); 434 * parser.sqltext = sqlContent; 435 * int ret = parser.parse(); 436 * 437 * // Access resolver2 results 438 * TSQLResolver2 resolver2 = parser.getResolver2(); 439 * if (resolver2 != null) { 440 * // Use TSQLResolver2ResultFormatter to generate output 441 * TSQLResolver2ResultFormatter formatter = new TSQLResolver2ResultFormatter(resolver2, config); 442 * String result = formatter.format(); 443 * } 444 * </pre> 445 */ 446 private TSQLResolver2 resolver2; 447 448 /** 449 * Get the TSQLResolver2 instance used for name resolution. 450 * Returns null if resolver2 was not used or if parsing failed. 451 * 452 * @return the TSQLResolver2 instance or null 453 */ 454 public TSQLResolver2 getResolver2() { 455 return resolver2; 456 } 457 458 /** 459 * The resolver type to use for name resolution. 460 * Default is EResolverType.DEFAULT which uses TBaseType settings. 461 */ 462 private EResolverType resolverType = EResolverType.DEFAULT; 463 464 /** 465 * Get the resolver type used for name resolution. 466 * 467 * @return the resolver type 468 */ 469 public EResolverType getResolverType() { 470 return resolverType; 471 } 472 473 /** 474 * Set the resolver type to use for name resolution. 475 * 476 * <p>This instance-level setting takes precedence over global TBaseType settings. 477 * When set to DEFAULT (the default value), behavior is determined by 478 * TBaseType.isEnableResolver() and TBaseType.isEnableResolver2().</p> 479 * 480 * <h3>Usage Example:</h3> 481 * <pre> 482 * TGSqlParser parser = new TGSqlParser(EDbVendor.dbvoracle); 483 * parser.setResolverType(EResolverType.RESOLVER2); // Use new resolver 484 * parser.sqltext = "SELECT * FROM employees"; 485 * parser.parse(); 486 * 487 * // Access resolver2 results 488 * TSQLResolver2 resolver = parser.getResolver2(); 489 * </pre> 490 * 491 * @param resolverType the resolver type to use 492 * @see EResolverType 493 */ 494 public void setResolverType(EResolverType resolverType) { 495 this.resolverType = resolverType != null ? resolverType : EResolverType.DEFAULT; 496 } 497 498 /** 499 * Optional configuration for TSQLResolver2. 500 * If null, a default configuration will be created during parsing. 501 */ 502 private TSQLResolverConfig resolver2Config; 503 504 /** 505 * Get the TSQLResolverConfig used for resolver2. 506 * Returns null if not explicitly set (default config will be used during parsing). 507 * 508 * @return the resolver2 config or null 509 */ 510 public TSQLResolverConfig getResolver2Config() { 511 return resolver2Config; 512 } 513 514 /** 515 * Set the TSQLResolverConfig to use for resolver2. 516 * 517 * <p>This allows customizing resolver2 behavior such as:</p> 518 * <ul> 519 * <li>guessColumnStrategy - how to handle ambiguous columns</li> 520 * <li>legacyCompatibilityEnabled - sync results to legacy structures</li> 521 * <li>maxIterations - maximum iterations for iterative resolution</li> 522 * </ul> 523 * 524 * <p>If not set, a default configuration will be created during parsing 525 * with the database vendor automatically set.</p> 526 * 527 * <h3>Usage Example:</h3> 528 * <pre> 529 * TGSqlParser parser = new TGSqlParser(EDbVendor.dbvoracle); 530 * parser.setResolverType(EResolverType.RESOLVER2); 531 * 532 * // Configure resolver2 to not pick ambiguous columns 533 * TSQLResolverConfig config = new TSQLResolverConfig(); 534 * config.setGuessColumnStrategy(TSQLResolverConfig.GUESS_COLUMN_STRATEGY_NOT_PICKUP); 535 * parser.setResolver2Config(config); 536 * 537 * parser.sqltext = "SELECT id FROM users, orders"; 538 * parser.parse(); 539 * </pre> 540 * 541 * @param config the resolver2 configuration, or null for default 542 * @see TSQLResolverConfig 543 */ 544 public void setResolver2Config(TSQLResolverConfig config) { 545 this.resolver2Config = config; 546 } 547 548 // ===== Binding Diagnostic API (plan §5.3, S2) ===== 549 550 /** 551 * Pass-through accessor for the resolver2 binding result. Slice S2 ships 552 * an empty stub; S5 wires the populated post-pass result. Always non-null 553 * — including when {@link #parse()} returns non-zero (syntax error, 554 * plan §5.6.9) or when resolver2 was not invoked. 555 * 556 * @return the binding result; never null 557 */ 558 public BindingResult getBindingResult() { 559 TSQLResolver2 r = this.resolver2; 560 if (r == null) { 561 return BindingResult.empty(); 562 } 563 return r.getBindingResult(); 564 } 565 566 /** 567 * Pass-through accessor for {@link 568 * gudusoft.gsqlparser.resolver2.binding.BindingResult#getDiagnostics()}. 569 * 570 * @return the diagnostics list; never null 571 */ 572 public List<BindingDiagnostic> getBindingDiagnostics() { 573 TSQLResolver2 r = this.resolver2; 574 if (r == null) { 575 return Collections.<BindingDiagnostic>emptyList(); 576 } 577 return r.getBindingDiagnostics(); 578 } 579 580 /** 581 * Pass-through accessor for {@link 582 * gudusoft.gsqlparser.resolver2.binding.BindingResult#hasErrors()}. 583 * 584 * @return whether any ERROR-severity binding diagnostic was emitted 585 */ 586 public boolean hasBindingErrors() { 587 TSQLResolver2 r = this.resolver2; 588 if (r == null) { 589 return false; 590 } 591 return r.hasBindingErrors(); 592 } 593 594 /** 595 * The array of syntax error generated by the parser during checking the syntax of the input SQL, 596 * element of this list is type of {@link TSyntaxError} 597 * 598 * @return the array of errors 599 */ 600 public ArrayList <TSyntaxError> getSyntaxErrors() { 601 return syntaxErrors; 602 } 603 604 private ArrayList <TSyntaxError> syntaxErrors; 605 606// public ArrayList<TSyntaxError> getSyntaxHints() { 607// return syntaxHints; 608// } 609 610 //private ArrayList <TSyntaxError> syntaxHints; 611 612 /** 613 * The database vendor specified when creating this parser. 614 * The grammar rule of this database vendor will be used to validate the syntax of the input SQL. 615 * 616 * @return the database vendor 617 */ 618 public EDbVendor getDbVendor() { 619 return dbVendor; 620 } 621 622 /** 623 * Get the OceanBase tenant compatibility mode for this parser. 624 * 625 * <p>Only meaningful when the parser is configured for 626 * {@link EDbVendor#dbvoceanbase}. For other vendors the value is ignored 627 * and defaults to {@link EOBTenantMode#MYSQL}. 628 * 629 * @return the current OceanBase tenant mode (never null) 630 * @since 4.0.1.4 631 */ 632 public EOBTenantMode getOBTenantMode() { 633 return oceanBaseTenantMode; 634 } 635 636 /** 637 * Set the OceanBase tenant compatibility mode for this parser. 638 * 639 * <p>This selects which delegate parser ({@code MySqlSqlParser}, 640 * {@code OracleSqlParser}, or in later phases the forked OceanBase 641 * grammar instances) is used when {@code dbvoceanbase} is the active 642 * vendor. The mode is permanent for the life of an OceanBase tenant on 643 * the server side; one mode per parser instance is the correct 644 * granularity. Do not switch modes mid-script. 645 * 646 * <p>Setting this invalidates any cached vendor parser instance so the 647 * next {@code parse()} call constructs the correct delegate. The mode is 648 * preserved across {@link #prepareForReuse()}. 649 * 650 * <p>{@code null} is coerced to {@link EOBTenantMode#MYSQL}. 651 * 652 * @param mode the desired tenant mode 653 * @since 4.0.1.4 654 */ 655 public void setOBTenantMode(EOBTenantMode mode) { 656 if (mode == null) { 657 mode = EOBTenantMode.MYSQL; 658 } 659 if (this.oceanBaseTenantMode != mode) { 660 this.oceanBaseTenantMode = mode; 661 // Force the next parse() call to recreate the vendor parser so the 662 // OceanBaseSqlParser adapter picks up the new mode on construction. 663 this.vendorParser = null; 664 665 // Mode-dependent delimiter and command-resolver selection for 666 // OceanBase only. ORACLE mode requires '/' for PL/SQL block 667 // termination and Oracle-style splitter rules so that anonymous 668 // BEGIN/END blocks are recognized as a single statement instead 669 // of being split on the embedded semicolons. MYSQL/SYSTEM modes 670 // use '$' and the MySQL splitter to mirror MySQL DELIMITER 671 // override semantics. This is a no-op for non-OceanBase parsers 672 // because they never call this setter. 673 if (this.dbVendor == EDbVendor.dbvoceanbase) { 674 if (mode == EOBTenantMode.ORACLE) { 675 this.delimiterchar = '/'; 676 this.defaultDelimiterStr = ";"; 677 // Re-bind the TGSqlParser-level splitter to Oracle rules 678 // so PL/SQL blocks survive raw extraction. The splitter's 679 // internal vendor field reports dbvoracle in this case; 680 // that does NOT affect AST node identity, which comes 681 // from TGSqlParser.dbVendor (still dbvoceanbase) via 682 // the NodeFactory back-reference fixup in 683 // doDelegatedRawParse(). 684 this.sqlcmds = SqlCmdsFactory.get(EDbVendor.dbvoracle); 685 } else { 686 this.delimiterchar = '$'; 687 this.defaultDelimiterStr = "$"; 688 // Restore the MySQL-family splitter (the default chosen 689 // at construction time when sqlcmds was first assigned). 690 this.sqlcmds = SqlCmdsFactory.get(EDbVendor.dbvoceanbase); 691 } 692 } 693 } 694 } 695 696 /** 697 * @deprecated As of v1.4.3.4 698 * enable GSP to parse the rest of sql statements inside stored procedure 699 * when a SQL statement in the stored procedure cannot be parsed 700 * 701 * <p>Available to parse sybase stored procedure currently. 702 * 703 * @param enablePartialParsing set true to enable this partial parsing, default is false 704 */ 705 public void setEnablePartialParsing(boolean enablePartialParsing) { 706 this.enablePartialParsing = enablePartialParsing; 707 } 708 709 /** 710 * enable GSP to parse the rest of sql statements inside stored procedure 711 * when a SQL statement in the stored procedure cannot be parsed 712 * 713 * <p>Available to parse sybase stored procedure currently. 714 * 715 * <p> default is false; 716 * 717 * @deprecated As of v1.4.3.4 718 */ 719 private boolean isEnablePartialParsing() { 720 721 return enablePartialParsing; 722 } 723 724 private boolean enablePartialParsing = false; 725 726 private boolean isSinglePLBlock = false; 727 728 public void setSinglePLBlock(boolean singlePLBlock) { 729 isSinglePLBlock = singlePLBlock; 730 } 731 732 private static String userName; 733 private static String machineId = null; 734 private static String licenseKey; 735 private static String licenseType; 736 private static boolean licenseOK = false; 737 private static String licenseMessage; 738 739 /** 740 * Not used. 741 * 742 * @return the user name 743 */ 744 public static String getUserName() { 745 return userName; 746 } 747 748 /** 749 * Not used. 750 * 751 * @return the machine id 752 */ 753 public static String getMachineId() { 754 755 return machineId; 756 } 757 758 /** 759 * Not used. 760 * 761 * @return the license message 762 */ 763 public static String getLicenseMessage() { 764 return licenseMessage; 765 } 766 767 768 static { 769 licenseOK = validateLicense(); 770 } 771 772 /** 773 * Not used. 774 * 775 * @return trial license or developer license or distribution license 776 */ 777 public static String getLicenseType() { 778 return licenseType; 779 } 780 781 private EDbVendor dbVendor; 782 private EOBTenantMode oceanBaseTenantMode = EOBTenantMode.MYSQL; 783 private String errormessage; 784 785 /** 786 * The lexer which is used to tokenize the input SQL. 787 * For delegated vendors (MSSQL), lazily creates the vendor parser to get its lexer. 788 * 789 * @return the lexer 790 */ 791 public TCustomLexer getFlexer() { 792 if (flexer == null) { 793 // Lazily create vendor parser to get its lexer 794 SqlParser vp = getOrCreateVendorParser(); 795 if (vp instanceof gudusoft.gsqlparser.parser.MssqlSqlParser) { 796 // Cache the flexer from vendor parser 797 flexer = ((gudusoft.gsqlparser.parser.MssqlSqlParser) vp).flexer; 798 } else if (vp instanceof gudusoft.gsqlparser.parser.MySqlSqlParser) { 799 // Cache the flexer from vendor parser 800 flexer = ((gudusoft.gsqlparser.parser.MySqlSqlParser) vp).flexer; 801 } else if (vp instanceof gudusoft.gsqlparser.parser.PostgreSqlParser) { 802 // Cache the flexer from vendor parser 803 flexer = ((gudusoft.gsqlparser.parser.PostgreSqlParser) vp).flexer; 804 } else if (vp instanceof gudusoft.gsqlparser.parser.DuckdbSqlParser) { 805 flexer = ((gudusoft.gsqlparser.parser.DuckdbSqlParser) vp).flexer; 806 } else if (vp instanceof gudusoft.gsqlparser.parser.OracleSqlParser) { 807 // Cache the flexer from vendor parser 808 flexer = ((gudusoft.gsqlparser.parser.OracleSqlParser) vp).flexer; 809 } else if (vp instanceof gudusoft.gsqlparser.parser.BigQuerySqlParser) { 810 // Cache the flexer from vendor parser 811 flexer = ((gudusoft.gsqlparser.parser.BigQuerySqlParser) vp).flexer; 812 } else if (vp instanceof gudusoft.gsqlparser.parser.AthenaSqlParser) { 813 // Cache the flexer from vendor parser 814 flexer = ((gudusoft.gsqlparser.parser.AthenaSqlParser) vp).flexer; 815 } else if (vp instanceof gudusoft.gsqlparser.parser.CouchbaseSqlParser) { 816 // Cache the flexer from vendor parser 817 flexer = ((gudusoft.gsqlparser.parser.CouchbaseSqlParser) vp).flexer; 818 } else if (vp instanceof gudusoft.gsqlparser.parser.DatabricksSqlParser) { 819 // Cache the flexer from vendor parser 820 flexer = ((gudusoft.gsqlparser.parser.DatabricksSqlParser) vp).flexer; 821 } else if (vp instanceof gudusoft.gsqlparser.parser.DaxSqlParser) { 822 // Cache the flexer from vendor parser 823 flexer = ((gudusoft.gsqlparser.parser.DaxSqlParser) vp).flexer; 824 } else if (vp instanceof gudusoft.gsqlparser.parser.PowerQuerySqlParser) { 825 // Cache the flexer from vendor parser 826 flexer = ((gudusoft.gsqlparser.parser.PowerQuerySqlParser) vp).flexer; 827 } else if (vp instanceof gudusoft.gsqlparser.parser.Db2SqlParser) { 828 // Cache the flexer from vendor parser 829 flexer = ((gudusoft.gsqlparser.parser.Db2SqlParser) vp).flexer; 830 } else if (vp instanceof gudusoft.gsqlparser.parser.GaussDbSqlParser) { 831 // Cache the flexer from vendor parser 832 flexer = ((gudusoft.gsqlparser.parser.GaussDbSqlParser) vp).flexer; 833 } else if (vp instanceof gudusoft.gsqlparser.parser.GreenplumSqlParser) { 834 // Cache the flexer from vendor parser 835 flexer = ((gudusoft.gsqlparser.parser.GreenplumSqlParser) vp).flexer; 836 } else if (vp instanceof gudusoft.gsqlparser.parser.HiveSqlParser) { 837 // Cache the flexer from vendor parser 838 flexer = ((gudusoft.gsqlparser.parser.HiveSqlParser) vp).flexer; 839 } else if (vp instanceof gudusoft.gsqlparser.parser.HanaSqlParser) { 840 // Cache the flexer from vendor parser 841 flexer = ((gudusoft.gsqlparser.parser.HanaSqlParser) vp).flexer; 842 } else if (vp instanceof gudusoft.gsqlparser.parser.ImpalaSqlParser) { 843 // Cache the flexer from vendor parser 844 flexer = ((gudusoft.gsqlparser.parser.ImpalaSqlParser) vp).flexer; 845 } else if (vp instanceof gudusoft.gsqlparser.parser.InformixSqlParser) { 846 // Cache the flexer from vendor parser 847 flexer = ((gudusoft.gsqlparser.parser.InformixSqlParser) vp).flexer; 848 } else if (vp instanceof gudusoft.gsqlparser.parser.MdxSqlParser) { 849 // Cache the flexer from vendor parser 850 flexer = ((gudusoft.gsqlparser.parser.MdxSqlParser) vp).flexer; 851 } else if (vp instanceof gudusoft.gsqlparser.parser.NetezzaSqlParser) { 852 // Cache the flexer from vendor parser 853 flexer = ((gudusoft.gsqlparser.parser.NetezzaSqlParser) vp).flexer; 854 } else if (vp instanceof gudusoft.gsqlparser.parser.OdbcSqlParser) { 855 // Cache the flexer from vendor parser 856 flexer = ((gudusoft.gsqlparser.parser.OdbcSqlParser) vp).flexer; 857 } else if (vp instanceof gudusoft.gsqlparser.parser.OpenEdgeSqlParser) { 858 // Cache the flexer from vendor parser 859 flexer = ((gudusoft.gsqlparser.parser.OpenEdgeSqlParser) vp).flexer; 860 } else if (vp instanceof gudusoft.gsqlparser.parser.PrestoSqlParser) { 861 // Cache the flexer from vendor parser 862 flexer = ((gudusoft.gsqlparser.parser.PrestoSqlParser) vp).flexer; 863 } else if (vp instanceof gudusoft.gsqlparser.parser.RedshiftSqlParser) { 864 // Cache the flexer from vendor parser 865 flexer = ((gudusoft.gsqlparser.parser.RedshiftSqlParser) vp).flexer; 866 } else if (vp instanceof gudusoft.gsqlparser.parser.SnowflakeSqlParser) { 867 // Cache the flexer from vendor parser 868 flexer = ((gudusoft.gsqlparser.parser.SnowflakeSqlParser) vp).flexer; 869 } else if (vp instanceof gudusoft.gsqlparser.parser.SqliteSqlParser) { 870 // Cache the flexer from vendor parser 871 flexer = ((gudusoft.gsqlparser.parser.SqliteSqlParser) vp).flexer; 872 } else if (vp instanceof gudusoft.gsqlparser.parser.SoqlSqlParser) { 873 // Cache the flexer from vendor parser 874 flexer = ((gudusoft.gsqlparser.parser.SoqlSqlParser) vp).flexer; 875 } else if (vp instanceof gudusoft.gsqlparser.parser.SparksqlSqlParser) { 876 // Cache the flexer from vendor parser 877 flexer = ((gudusoft.gsqlparser.parser.SparksqlSqlParser) vp).flexer; 878 } else if (vp instanceof gudusoft.gsqlparser.parser.SybaseSqlParser) { 879 // Cache the flexer from vendor parser 880 flexer = ((gudusoft.gsqlparser.parser.SybaseSqlParser) vp).flexer; 881 } else if (vp instanceof gudusoft.gsqlparser.parser.VerticaSqlParser) { 882 // Cache the flexer from vendor parser 883 flexer = ((gudusoft.gsqlparser.parser.VerticaSqlParser) vp).flexer; 884 } 885 } 886 return flexer; 887 } 888 889 private TCustomLexer flexer; 890 891 TCustomParser fparser,fplsqlparser; 892 893 // Cached vendor-specific parser for delegated parsing (MSSQL, etc.) 894 // Created lazily in getFlexer() and reused in parse() 895 private SqlParser vendorParser; 896 897 BufferedReader finputstream = null; //used by lexer 898 TCustomSqlStatement gcurrentsqlstatement,nextStmt; 899 // Vendor-specific SQL command resolver 900 ISqlCmds sqlcmds; 901 902 HashMap sqlpluskeywordList; 903 904 char delimiterchar; 905 String defaultDelimiterStr; 906 907 /** 908 * Returns the delimiter character used to separate SQL statements. 909 * Uses the flexer's delimiter if available (lexer-dependent), otherwise falls back to parser's value. 910 * @return the delimiter character 911 */ 912 public char getDelimiterChar() { 913 if (flexer != null) { 914 return flexer.delimiterchar; 915 } 916 return delimiterchar; 917 } 918 919 /** 920 * Returns the delimiter character for the given database vendor without creating a parser instance. 921 * This is a performance optimization for code that only needs the delimiter character. 922 * 923 * @param vendor the database vendor 924 * @return the delimiter character for the vendor 925 */ 926 public static char getDelimiterChar(EDbVendor vendor) { 927 switch(vendor){ 928 case dbvoracle: 929 case dbvteradata: 930 case dbvpostgresql: 931 case dbvduckdb: 932 case dbvredshift: 933 case dbvgreenplum: 934 return '/'; 935 case dbvdameng: 936 if (TBaseType.enterprise_edition || (!TBaseType.full_edition) || TBaseType.dameng_edition) { 937 return '/'; 938 } 939 return ';'; 940 case dbvdb2: 941 return '@'; 942 case dbvmysql: 943 case dbvoceanbase: 944 return '$'; 945 case dbvdoris: 946 if (TBaseType.enterprise_edition || (!TBaseType.full_edition) || TBaseType.doris_edition) { 947 return ';'; 948 } 949 return ';'; 950 case dbvstarrocks: 951 if (TBaseType.enterprise_edition || (!TBaseType.full_edition) || TBaseType.starrocks_edition) { 952 return ';'; 953 } 954 return ';'; 955 default: 956 return ';'; 957 } 958 } 959 960 private ISQLStatementHandle sqlStatementHandle = null; 961 962 public void setSqlStatementHandle(ISQLStatementHandle sqlStatementHandle) { 963 this.sqlStatementHandle = sqlStatementHandle; 964 } 965 966 private ITokenHandle tokenHandle = null; 967 968 /** 969 * Set an event handler which will be fired when a new source token is created by the lexer during tokenize the 970 * input SQL. 971 * 972 * @param tokenHandle the event handler to process the new created source token 973 */ 974 public void setTokenHandle(ITokenHandle tokenHandle) { 975 this.tokenHandle = tokenHandle; 976 } 977 978 private ITokenListHandle tokenListHandle = null; 979 980 public void setTokenListHandle(ITokenListHandle tokenListHandle) { 981 this.tokenListHandle = tokenListHandle; 982 } 983 984 private IMetaDatabase metaDatabase = null; 985 986 /** 987 * @deprecated As of v2.0.3.1, please use {@link #getSqlEnv()} instead 988 * 989 * set an instance of a class which implement the interface: {@link IMetaDatabase}. 990 * The parser will call {@link IMetaDatabase#checkColumn} method when it needs to know 991 * whether a column is belonged to a table. 992 * <p></p> 993 * The class that implements the interface: {@link IMetaDatabase} usually fetch the metadata from the database 994 * by connecting to a database instance. 995 * <p></p> 996 * If the class is not provided, the parser has to guess the relationship between a un-qualified column and table 997 * in the input SQL which may lead to a un-determined result between the column and table. 998 * 999 * @param metaDatabase a new instance of the class which implements the {@link IMetaDatabase} interface 1000 * @see IMetaDatabase 1001 */ 1002 public void setMetaDatabase(IMetaDatabase metaDatabase) { 1003 this.metaDatabase = metaDatabase; 1004 } 1005 1006 /** 1007 * @deprecated As of v2.0.3.1, please use {@link #getSqlEnv()} instead 1008 * 1009 * a new instance of the class which implements the {@link IMetaDatabase} interface 1010 * 1011 * @return a new instance of the class which implements the {@link IMetaDatabase} interface 1012 * @see #setMetaDatabase 1013 */ 1014 public IMetaDatabase getMetaDatabase() { 1015 1016 return metaDatabase; 1017 } 1018 1019 /** 1020 * Not used. 1021 * 1022 */ 1023 public void freeParseTable() { 1024 flexer.yystack = null; 1025 flexer.yytextbuf = null; 1026 flexer.buf = null; 1027 1028// TLexerOracle.yyk = null; 1029// TLexerOracle.yykl = null; 1030// TLexerOracle.yykh = null; 1031// TLexerOracle.yym = null; 1032// TLexerOracle.yyml = null; 1033// TLexerOracle.yymh = null; 1034// TLexerOracle.yyt = null; 1035// TLexerOracle.yytl = null; 1036// TLexerOracle.yyth = null; 1037// TParserOracleSql.yyah = null; 1038// TParserOracleSql.yyal = null; 1039// TParserOracleSql.yygh = null; 1040// TParserOracleSql.yygl = null; 1041// TParserOracleSql.yyd = null; 1042// TParserOracleSql.yya_sym= null; 1043// TParserOracleSql.yya_act= null; 1044// TParserOracleSql.yyr_len= null; 1045// TParserOracleSql.yyr_sym= null; 1046// TParserOracleSql.yyg_sym= null; 1047// TParserOracleSql.yyg_act= null; 1048// 1049// TParserOraclePLSql.yyah = null; 1050// TParserOraclePLSql.yyal = null; 1051// TParserOraclePLSql.yygh = null; 1052// TParserOraclePLSql.yygl = null; 1053// TParserOraclePLSql.yyd = null; 1054// TParserOraclePLSql.yya_sym= null; 1055// TParserOraclePLSql.yya_act= null; 1056// TParserOraclePLSql.yyr_len= null; 1057// TParserOraclePLSql.yyr_sym= null; 1058// TParserOraclePLSql.yyg_sym= null; 1059// TParserOraclePLSql.yyg_act= null; 1060// 1061// TLexerMssql.yyk = null; 1062// TLexerMssql.yykl = null; 1063// TLexerMssql.yykh = null; 1064// TLexerMssql.yym = null; 1065// TLexerMssql.yyml = null; 1066// TLexerMssql.yymh = null; 1067// TLexerMssql.yyt = null; 1068// TLexerMssql.yytl = null; 1069// TLexerMssql.yyth = null; 1070// TParserMssqlSql.yyah = null; 1071// TParserMssqlSql.yyal = null; 1072// TParserMssqlSql.yygh = null; 1073// TParserMssqlSql.yygl = null; 1074// TParserMssqlSql.yyd = null; 1075// TParserMssqlSql.yya_sym= null; 1076// TParserMssqlSql.yya_act= null; 1077// TParserMssqlSql.yyr_len= null; 1078// TParserMssqlSql.yyr_sym= null; 1079// TParserMssqlSql.yyg_sym= null; 1080// TParserMssqlSql.yyg_act= null; 1081// 1082// TLexerMysql.yyk = null; 1083// TLexerMysql.yykl = null; 1084// TLexerMysql.yykh = null; 1085// TLexerMysql.yym = null; 1086// TLexerMysql.yyml = null; 1087// TLexerMysql.yymh = null; 1088// TLexerMysql.yyt = null; 1089// TLexerMysql.yytl = null; 1090// TLexerMysql.yyth = null; 1091// TParserMysqlSql.yyah = null; 1092// TParserMysqlSql.yyal = null; 1093// TParserMysqlSql.yygh = null; 1094// TParserMysqlSql.yygl = null; 1095// TParserMysqlSql.yyd = null; 1096// TParserMysqlSql.yya_sym= null; 1097// TParserMysqlSql.yya_act= null; 1098// TParserMysqlSql.yyr_len= null; 1099// TParserMysqlSql.yyr_sym= null; 1100// TParserMysqlSql.yyg_sym= null; 1101// TParserMysqlSql.yyg_act= null; 1102 } 1103 1104 /** 1105 * Class constructor, create a new instance of the parser by setting the database vendor 1106 * 1107 * @param pdbvendor the database vendor whose grammar rule will be used to validate the syntax of the input SQL 1108 */ 1109 public TGSqlParser(EDbVendor pdbvendor) { 1110 dbVendor = pdbvendor; 1111 sqltext = ""; 1112 sqlfilename = ""; 1113 1114 // Use static method to get delimiter char for consistency 1115 delimiterchar = getDelimiterChar(pdbvendor); 1116 1117 // Set default delimiter string based on delimiter char 1118 if (delimiterchar == '$') { 1119 defaultDelimiterStr = "$"; 1120 } else { 1121 defaultDelimiterStr = ";"; 1122 } 1123 1124 // Special handling for Azure SQL - maps to MSSQL 1125 if (pdbvendor == EDbVendor.dbvazuresql) { 1126 dbVendor = EDbVendor.dbvmssql; 1127 } 1128 1129 // fparser is null here - it will be set later in doDelegatedRawParse() from vendor parser result 1130 // All vendors are now delegated to vendor-specific parsers via getOrCreateVendorParser() 1131 1132 sourcetokenlist = new TSourceTokenList(); 1133 sourcetokenlist.setGsqlparser(this); 1134 sqlstatements = new TStatementList(); 1135 // Inject vendor-specific command resolver 1136 sqlcmds = SqlCmdsFactory.get(dbVendor); 1137 sqlpluskeywordList = new HashMap(); 1138 syntaxErrors = new ArrayList(); 1139 1140 errormessage = ""; 1141 1142 if (TBaseType.license_expired_check){ 1143 if (!check_license_time()) { 1144 flexer = null; 1145 fparser = null; 1146 } 1147 } 1148 1149 TGSqlParser.setCurrentDBVendor(dbVendor); 1150 } 1151 1152 TContext globalContext; 1153 1154 1155 /** 1156 * Create a select statement object from the parameter: subquery 1157 * 1158 * @param subquery a plain text select statement which need to be converted to a {@link gudusoft.gsqlparser.stmt.TSelectSqlStatement} object 1159 * @return a select statement object, return null if the input select string is syntax invalid. 1160 */ 1161 public TSelectSqlStatement parseSubquery(String subquery){ 1162 return parseSubquery(this.dbVendor,subquery); 1163 } 1164 1165 /** 1166 * this method is thread safe. 1167 * 1168 * C:\prg\gsp_java\gsp_java_core\src\test\java\gudusoft\gsqlparser\ParseSubqueryThreadSafetyTest.java 1169 * 1170 */ 1171 public static TSelectSqlStatement parseSubquery(EDbVendor dbVendor, String subquery){ 1172// TGSqlParser localParser = new TGSqlParser(dbVendor); 1173// localParser.sqltext = subquery; 1174// int iRet = localParser.doparse(); 1175// if (iRet != 0) { 1176// return null; 1177// } 1178 1179 TSingletonParser singletonParser = TSingletonParser.getInstance(); 1180 TStatementList statements = singletonParser.getStmts(dbVendor,subquery); 1181 if (statements.size() == 0) return null; 1182 1183 return (TSelectSqlStatement)statements.get(0); 1184 } 1185 1186 /** 1187 * Create an expression object from the parameter: expr 1188 * 1189 * @param expr a plain text expression which will be converted to {@link gudusoft.gsqlparser.nodes.TExpression} object 1190 * @return an expression object, return null if the input expression string is not syntax valid. 1191 */ 1192 public TExpression parseExpression(String expr){ 1193 return parseExpression(this.dbVendor,expr); 1194 } 1195 1196 /* 1197 * this method is thread safe. 1198 * 1199 * getStmts() is synchronized - only one thread can execute it at a time 1200 * getParser() is also synchronized 1201 * Each thread gets a new TStatementList instance 1202 1203 * this is the test case for this method. 1204 * C:\prg\gsp_java\gsp_java_core\src\test\java\gudusoft\gsqlparser\ParseExpressionThreadSafetyTest.java 1205 */ 1206 public static TExpression parseExpression(EDbVendor dbVendor, String expr){ 1207 TSingletonParser singletonParser = TSingletonParser.getInstance(); 1208 1209 boolean e = TBaseType.isEnableResolver(); 1210 TBaseType.setEnableResolver(false); 1211 TStatementList statements; 1212 try{ 1213 statements = singletonParser.getStmts(dbVendor,"select 1 from t where "+TBaseType.newline+expr); 1214 }finally { 1215 TBaseType.setEnableResolver(e); 1216 } 1217 1218 if (statements.size() == 0) return null; 1219 1220// TGSqlParser localParser = new TGSqlParser(dbVendor); 1221// localParser.sqltext = "select 1 from t where "+TBaseType.newline+expr; 1222// int iRet = localParser.doparse(); 1223// if (iRet != 0) { 1224// return null; 1225// } 1226 1227 return ((TSelectSqlStatement)statements.get(0)).getWhereClause().getCondition(); 1228 } 1229 1230 /** 1231 * Create a function object from the parameter: newFunction 1232 * 1233 * @param newFunction a plain text function which will be converted to {@link gudusoft.gsqlparser.nodes.TFunctionCall} object 1234 * @return a function object, or return null if the input string is not a valid function call. 1235 */ 1236 public TFunctionCall parseFunctionCall(String newFunction){ 1237 return parseFunctionCall(this.dbVendor,newFunction); 1238 } 1239 1240 public static TFunctionCall parseFunctionCall(EDbVendor dbVendor, String newFunction){ 1241 TSingletonParser singletonParser = TSingletonParser.getInstance(); 1242 TStatementList statements = singletonParser.getStmts(dbVendor,"select"+TBaseType.newline+newFunction+TBaseType.newline+"from t"); 1243 if (statements.size() == 0) return null; 1244 1245 1246// TGSqlParser localParser = new TGSqlParser(dbVendor); 1247// localParser.sqltext = "select"+TBaseType.newline+newFunction+TBaseType.newline+"from t"; 1248// int iRet = localParser.doparse(); 1249// if (iRet!= 0) { 1250// return null; 1251// } 1252 1253 return statements.get(0).getResultColumnList().getResultColumn(0).getExpr().getFunctionCall(); 1254 } 1255 1256 /** 1257 * Create a database objectName from the parameter: newObjectName 1258 * 1259 * @param newObjectName a plain text objectName which will be converted to {@link gudusoft.gsqlparser.nodes.TObjectName} object 1260 * @return a database objectName object, return null is the input string is not a valid objectName. 1261 */ 1262 public TObjectName parseObjectName(String newObjectName){ 1263 //return parseObjectName(this.dbVendor,newObjectName); 1264 return TObjectName.createObjectName(this.dbVendor,EDbObjectType.column,newObjectName); 1265 } 1266 1267 1268 1269 /** 1270 * 1271 * @param dbVendor 1272 * @param newObjectName 1273 * @return 1274 */ 1275 public static TObjectName parseObjectName(EDbVendor dbVendor, String newObjectName){ 1276 1277 TSingletonParser singletonParser = TSingletonParser.getInstance(); 1278 TStatementList statements = singletonParser.getStmts(dbVendor,"select"+TBaseType.newline+newObjectName+TBaseType.newline+"from t"); 1279 if (statements.size() == 0) return null; 1280 TExpression e = statements.get(0).getResultColumnList().getResultColumn(0).getExpr(); 1281 1282// TGSqlParser localParser = new TGSqlParser(dbVendor); 1283// localParser.sqltext = "select"+TBaseType.newline+newObjectName+TBaseType.newline+"from t"; 1284// int iRet = localParser.doparse(); 1285// if (iRet!= 0) { 1286// return null; 1287// } 1288// TExpression e = ((TSelectSqlStatement)localParser.sqlstatements.get(0)).getResultColumnList().getResultColumn(0).getExpr(); 1289 1290 TObjectName lcResult = null; 1291 switch (e.getExpressionType()){ 1292 case simple_object_name_t: 1293 lcResult = e.getObjectOperand(); 1294 break; 1295 case simple_constant_t: 1296 lcResult = new TObjectName(); 1297 lcResult.init(e.getConstantOperand().getValueToken()); 1298 break; 1299 default: 1300 break; 1301 } 1302 1303 return lcResult; 1304 } 1305 1306 /** 1307 * Create an constant object from the parameter: newConstant 1308 * 1309 * @param newConstant a plian text constant which will be converted to {@link gudusoft.gsqlparser.nodes.TConstant} object 1310 * @return new constant object, returns null if the input is not a valid constant string. 1311 */ 1312 public TConstant parseConstant(String newConstant){ 1313 return parseConstant(this.dbVendor,newConstant); 1314 } 1315 1316 public static TConstant parseConstant(EDbVendor dbVendor, String newConstant){ 1317// TGSqlParser localParser = new TGSqlParser(dbVendor); 1318// localParser.sqltext = "select"+TBaseType.newline+newConstant+TBaseType.newline+"from t"; 1319// int iRet = localParser.doparse(); 1320// if (iRet!= 0) { 1321// return null; 1322// } 1323 1324 TSingletonParser singletonParser = TSingletonParser.getInstance(); 1325 TStatementList statements = singletonParser.getStmts(dbVendor,"select"+TBaseType.newline+newConstant+TBaseType.newline+"from t"); 1326 if (statements.size() == 0) return null; 1327 1328 return statements.get(0).getResultColumnList().getResultColumn(0).getExpr().getConstantOperand(); 1329 } 1330 1331 1332 /** 1333 * The total number of syntax errors founded in the input SQL script. 1334 * <br>Only the first syntax error in a SQL statement is counted if there are more than one syntax errors in 1335 * a single SQL statement. 1336 * <br> In a SQL script, only the first syntax error in each SQL statement is counted. 1337 * 1338 * @return The total number of syntax errors founded in the input SQL script. Returns 0 if no syntax error is founded. 1339 */ 1340 public int getErrorCount(){ 1341 return syntaxErrors.size(); 1342 } 1343 1344 /** 1345 * The text of error message generated by iterating all items in {@link #getSyntaxErrors}. 1346 * User may generate error message in their own format by iterating all items in {@link #getSyntaxErrors}. 1347 * 1348 * @return error message 1349 */ 1350 public String getErrormessage(){ 1351 1352 String s="",hint="Syntax error"; 1353 TSyntaxError t; 1354 for (int i= 0; i< syntaxErrors.size(); i++) 1355 { 1356 t = (TSyntaxError) syntaxErrors.get(i); 1357 if (t.hint.length() > 0) hint = t.hint; 1358 s= s+hint+"("+t.errorno+") near: "+t.tokentext; 1359 s=s+"("+t.lineNo; 1360 s=s+","+t.columnNo +", token code:"+t.tokencode+")"; 1361 //s=s+" expected tokentext:"+t.hint; 1362 1363 // break;//get only one message, remove this one and uncomment next line to get all error messages 1364 if (i != syntaxErrors.size() - 1) 1365 s = s +TBaseType.linebreak; 1366 } 1367 1368 if (errormessage.length() > 0){ 1369 s = errormessage+TBaseType.linebreak+s; 1370 } 1371 return s; 1372 1373 } 1374 1375 /** 1376 * check syntax of the input SQL. This method works exactly the same as {@link #parse} method. 1377 * 1378 * @return 0 means parse SQL script successfully, otherwise, use {@link #getErrorCount}, {@link #getErrormessage} 1379 * to get detailed error information. 1380 * @see #parse 1381 */ 1382 public int checkSyntax(){ 1383 return doparse(); 1384 } 1385 1386 /** 1387 * Check syntax of the input SQL, doing some kind of semantic analysis without connecting to a real database. 1388 * <p></p> 1389 * This method will do a in-depth analysis of the input SQL such as building the link between table and columns. 1390 * The parse tree of the input SQL is available after calling this method. 1391 * 1392 * The parser checks the syntax of those SQL statements one by one. If syntax error is found in a SQL statement, 1393 * an error will be logged, no parse tree will be built for this SQL statement, 1394 * the error message can be fetched using the {@link #getErrormessage()} method. 1395 * <p></p> 1396 * The syntax error in one SQL statement doesn't prevent the parser continue to check the syntax of the next SQL statement. 1397 * After checking syntax of all SQL statements, use the {@link #getErrorCount()} method to get the total number of errors. 1398 * <p></p> 1399 * A syntax error in a SQL stored procedure will cease this parser to check syntax of the rest SQL statements 1400 * in this stored procedure. 1401 * 1402 * @return 0 means parse SQL script successfully, otherwise, use {@link #getErrorCount}, {@link #getErrormessage} 1403 * to get detailed error information. 1404 * @see #getSyntaxErrors 1405 */ 1406 1407 public int parse(){ 1408 return doparse(); 1409 } 1410 1411 public int validate(){ 1412 if (sqlstatements.size() == 0) return 0; 1413 1414 TRelationValidator relationValidate = new TRelationValidator(globalContext); 1415 for(TCustomSqlStatement sqlStatement:sqlstatements){ 1416 sqlStatement.acceptChildren(relationValidate); 1417 } 1418 return 0; 1419 } 1420 1421 void setdelimiterchar(char ch){ 1422 delimiterchar = ch; 1423 flexer.delimiterchar = ch; 1424 } 1425 char curdelimiterchar; 1426 String userDelimiterStr =""; 1427 1428 boolean includesqlstatementtype(ESqlStatementType search, ESqlStatementType[] src){ 1429 boolean ret = false; 1430 for(int i=0;i<src.length;i++){ 1431 if (src[i] == search){ 1432 ret = true; 1433 break; 1434 } 1435 } 1436 return ret; 1437 } 1438 1439 // private int getfileEncodingType(FileInputStream inputStream){ 1440 // BufferedInputStream fr = new BufferedInputStream(inputStream,8); 1441 // return getfileEncodingType(fr); 1442 // } 1443 1444 private int getfileEncodingType(BufferedInputStream fr){ 1445 int ret = 0; // default, 1: utf-16, 2: utf-32 1446 // BufferedInputStream fr = new BufferedInputStream(inputStream,8); 1447 try { 1448 byte[] bom = new byte[4]; 1449 fr.mark(bom.length+1); 1450 1451 fr.read(bom,0,bom.length); 1452 if ( ((bom[0] == (byte)0xFF) && (bom[1] == (byte)0xFE)) 1453 ||((bom[0] == (byte)0xFE) && (bom[1] == (byte)0xFF)) 1454 ) 1455 { 1456 ret = 1; 1457 if ( ((bom[2] == (byte)0xFF) && (bom[3] == (byte)0xFE)) 1458 ||((bom[2] == (byte)0xFE) && (bom[3] == (byte)0xFF)) 1459 ){ 1460 ret = 2; 1461 } 1462 }else{ 1463 if ((bom[0] == (byte)0xEF) && (bom[1] == (byte)0xBB)&& (bom[2] == (byte)0xBF)){ 1464 ret = 3; //UTF-8,EF BB BF 1465 } 1466 } 1467 fr.reset(); 1468 } catch (FileNotFoundException e) { 1469 // e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1470 } catch (IOException e) { 1471 //e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1472 } 1473 return ret; 1474 } 1475 1476 // private int getfileEncodingType(String fn){ 1477 // int ret = 0; // default, 1: utf-16, 2: utf-32 1478 // try { 1479 // FileInputStream fr = new FileInputStream(fn); 1480 // ret = getfileEncodingType(fr); 1481 // fr.close(); 1482 1483 // } catch (FileNotFoundException e) { 1484 // // e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1485 // } catch (IOException e) { 1486 // //e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1487 // } 1488 // return ret; 1489 // } 1490 1491 int readsql(){ 1492 int ret = 0; 1493 1494 syntaxErrors.clear(); 1495 //syntaxHints.clear(); 1496 1497 if (((!TBaseType.full_edition))||(!TBaseType.need_license_file)){ 1498 if (!TBaseType.need_license_file){ 1499 licenseType = TBaseType.license_type_dist; 1500 userName = "dist"; 1501 }else { 1502 licenseType = TBaseType.license_type_trial; 1503 userName = TBaseType.license_trail_username; 1504 } 1505 // machineId = new HardwareBinder().getMachineIdString(); 1506 }else { 1507 if (!licenseOK){ 1508 errormessage = licenseMessage; 1509 return -1; 1510 } 1511 } 1512 1513 try{ 1514 if (finputstream != null) finputstream.close(); 1515 if (flexer == null){ 1516 ret = -1; 1517 errormessage = "requested database not supported:"+this.dbVendor.toString(); 1518 return ret; 1519 } 1520 if (flexer.yyinput != null) flexer.yyinput.close(); 1521 1522 }catch(IOException e){ 1523 ret = -1; 1524 errormessage = "requested database not supported"; 1525 } 1526 1527 if (sqltext.length() > 0){ 1528 finputstream = new BufferedReader(new StringReader(sqltext), TBaseType.LEXER_INPUT_BUFFER_SIZE); 1529 if ((!TBaseType.full_edition) && (sqltext.length() > TBaseType.query_size_limitation)){ 1530 errormessage = TBaseType.trail_version_query_message; 1531 ret = -1; 1532 } 1533 }else if (sqlfilename.length() > 0){ 1534 try{ 1535 1536 streamFromSqlFile = new FileInputStream(sqlfilename); 1537 // Buffer it for mark/reset support 1538 BufferedInputStream bufferedStream = new BufferedInputStream(streamFromSqlFile,8); 1539 int encodingtype = getfileEncodingType(bufferedStream); 1540 streamFromSqlFile.getChannel().position(0); 1541 1542 if(encodingtype == 1){ 1543 sqlStreamReader = new InputStreamReader(streamFromSqlFile,"UTF-16"); 1544 }else if(encodingtype == 2){ 1545 sqlStreamReader = new InputStreamReader(streamFromSqlFile,"UTF-32"); 1546 }else if(encodingtype == 3){ 1547 // System.out.println("utf-8"); 1548 sqlStreamReader = new InputStreamReader(streamFromSqlFile,"UTF-8"); 1549 } 1550 else{ 1551 if (sqlCharset == null){ 1552 sqlCharset = Charset.defaultCharset().name(); 1553 //System.out.println("Charset used: "+Charset.defaultCharset().name()); 1554 } 1555 sqlStreamReader = new InputStreamReader(streamFromSqlFile, sqlCharset); 1556 // isr = new InputStreamReader(fr,"Cp737"); 1557 } 1558 1559 finputstream = new BufferedReader(sqlStreamReader, TBaseType.LEXER_INPUT_BUFFER_SIZE); 1560 if (encodingtype == 3){ 1561 //EF BB BF was not stripped by the InputStreamReader, so we do it 1562 finputstream.skip(1); 1563 } 1564 1565 if ((!TBaseType.full_edition)){ 1566 File file = new File(sqlfilename); 1567 if (!file.exists() || !file.isFile()) { 1568 ret = -1; 1569 errormessage = "not a valid sql file."; 1570 }else{ 1571 if (file.length() > TBaseType.query_size_limitation){ 1572 errormessage = TBaseType.trail_version_file_message; 1573 ret = -1; 1574 } 1575 } 1576 } 1577 1578 }catch(FileNotFoundException e){ 1579 ret = -1; 1580 errormessage = e.toString(); 1581 } catch (UnsupportedEncodingException e) { 1582 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1583 } catch (IOException e) { 1584 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1585 } 1586 1587 }else if(this.sqlInputStream != null){ 1588 int encodingtype = getfileEncodingType(sqlInputStream); 1589 InputStream fr = sqlInputStream; 1590 1591 InputStreamReader isr = null; 1592 try{ 1593 1594 if(encodingtype == 1){ 1595 isr = new InputStreamReader(fr,"UTF-16"); 1596 }else if(encodingtype == 2){ 1597 isr = new InputStreamReader(fr,"UTF-32"); 1598 }else if(encodingtype == 3){ 1599 // System.out.println("utf-8"); 1600 isr = new InputStreamReader(fr,"UTF-8"); 1601 } 1602 else{ 1603 if (sqlCharset == null){ 1604 sqlCharset = Charset.defaultCharset().name(); 1605 } 1606 1607 isr = new InputStreamReader(fr, sqlCharset); 1608 } 1609 1610 finputstream = new BufferedReader(isr, TBaseType.LEXER_INPUT_BUFFER_SIZE); 1611 if (encodingtype == 3){ 1612 //EF BB BF was not stripped by the InputStreamReader, so we do it 1613 finputstream.skip(1); 1614 } 1615 1616 }catch(FileNotFoundException e){ 1617 ret = -1; 1618 errormessage = e.toString(); 1619 } catch (UnsupportedEncodingException e) { 1620 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1621 } catch (IOException e) { 1622 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1623 } 1624 1625 try { 1626 if ((!TBaseType.full_edition) && (sqlInputStream.available() > TBaseType.query_size_limitation)){ 1627 errormessage = TBaseType.trail_version_query_message; 1628 ret = -1; 1629 } 1630 } catch (IOException e) { 1631 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1632 } 1633 } 1634 1635 if (finputstream == null) ret = -1; 1636 1637 if (ret == 0) 1638 { 1639 flexer.yyinput = finputstream; 1640 flexer.setSqlCharset(this.sqlCharset); 1641 flexer.reset(); 1642 } 1643 1644 sourcetokenlist.clear(); 1645 sourcetokenlist.curpos = -1; 1646 1647 flexer.resetTokenTable(); 1648 1649 1650 return ret; 1651 } 1652 1653 TSourceToken getanewsourcetoken(){ 1654 TSourceToken pst = null,prevst; 1655 1656 while (true) { 1657 pst = new TSourceToken(""); 1658 if (flexer.yylexwrap(pst) == 0) { pst = null; break;} 1659 1660 pst.setDbvendor(dbVendor); 1661 pst.tokenstatus = ETokenStatus.tsoriginal; 1662 if (pst.tokentype == ETokenType.ttreturn){ 1663 pst.setAstext(towinlinebreak(pst.getAstext())); 1664 } 1665 //combine space && linebreak after a linebreak into one 1666 if ( (pst.tokentype == ETokenType.ttwhitespace) 1667 && (sourcetokenlist.curpos >= 0) ) 1668 { 1669 prevst = sourcetokenlist.get(sourcetokenlist.curpos); 1670 if ( prevst.tokentype == ETokenType.ttreturn ) 1671 { 1672 //can't discard whitespace after linebreak, it will be used 1673 // to judge whether / at the { of the line is a sqlplus cmd or not 1674 // check isvalidplacefordivtosqlpluscmd for more 1675 prevst.setAstext(prevst.getAstext() + pst.getAstext()); 1676 //newst.free; 1677 //newst = nil; 1678 continue; 1679 } 1680 } 1681 1682 if ( (pst.tokentype == ETokenType.ttreturn) 1683 && (sourcetokenlist.curpos >= 0) ) 1684 { 1685 prevst = sourcetokenlist.get(sourcetokenlist.curpos); 1686 1687 if ( prevst.tokentype == ETokenType.ttreturn ) 1688 { 1689 prevst.setAstext(prevst.getAstext() + pst.getAstext()); 1690 //newst.free; 1691 //newst = nil; 1692 continue; 1693 } 1694 1695 if ( prevst.tokentype == ETokenType.ttwhitespace ) 1696 { 1697 //merge previous whitespace with this linebreak 1698 //doesn't work for sqlplus cmd 1699 1700 // prevst.sourcecode = newst.sourcecode; 1701 // prevst.tokentype = newst.tokentype; 1702 // prevst.tokencode = newst.tokencode; 1703 // newst.free; 1704 // newst = nil; 1705 // continue; 1706 1707 //prevst.prevsourcecode = prevst.astext; 1708 //prevst.astext = ""; 1709 } 1710 } 1711 1712 break; 1713 1714 } 1715 1716 if (pst != null){ 1717 pst.container = sourcetokenlist; 1718 sourcetokenlist.curpos = sourcetokenlist.curpos+1; 1719 pst.posinlist = sourcetokenlist.curpos; 1720 if (tokenHandle != null){ 1721 tokenHandle.processToken(pst); 1722 } 1723 } 1724 1725 // System.out.println(pst); 1726 // flexer.setTokenTableValue(pst); 1727 return pst; 1728 1729 } 1730 1731 String towinlinebreak(String s){ 1732 return s; 1733// todo not implemented yet 1734 } 1735 1736void checkconstarinttoken(TSourceToken lcprevtoken){ 1737 TSourceTokenList lcStList = lcprevtoken.container; 1738 if (TBaseType.assigned(lcStList)) 1739 { 1740 TSourceToken lcPPToken = lcStList.nextsolidtoken(lcprevtoken.posinlist,-2,false); 1741 if (TBaseType.assigned(lcPPToken)) 1742 { 1743 1744 if (lcPPToken.tokencode == flexer.getkeywordvalue("constraint")) 1745 { 1746 //System.out.println(lcPPToken); 1747 lcPPToken.tokencode = TBaseType.rw_constraint2; 1748 } 1749 } 1750 } 1751 1752} 1753 1754TSourceToken getprevtoken(TSourceToken ptoken){ 1755 // ETokenType[] lcnonsolidtokenset = {ETokenType.ttwhitespace,ETokenType.ttreturn,ETokenType.ttsimplecomment,ETokenType.ttbracketedcomment} ; 1756 TSourceTokenList lcstlist = ptoken.container; 1757 if (TBaseType.assigned(lcstlist)){ 1758 if ((ptoken.posinlist > 0) && (lcstlist.size() > ptoken.posinlist-1)) 1759 { 1760 if (!( 1761 (lcstlist.get(ptoken.posinlist-1).tokentype == ETokenType.ttwhitespace) 1762 ||(lcstlist.get(ptoken.posinlist-1).tokentype == ETokenType.ttreturn) 1763 ||(lcstlist.get(ptoken.posinlist-1).tokentype == ETokenType.ttsimplecomment) 1764 ||(lcstlist.get(ptoken.posinlist-1).tokentype == ETokenType.ttbracketedcomment) 1765 ) 1766 ) 1767 {return lcstlist.get(ptoken.posinlist-1);} 1768 else{ 1769 { return lcstlist.nextsolidtoken(ptoken.posinlist-1,-1,false);} 1770 } 1771 } 1772 } 1773 1774 return null; 1775} 1776 1777void docommonsqltexttotokenlist(){ 1778 1779 TSourceToken asourcetoken,lcprevst; 1780 int yychar; 1781 1782 asourcetoken = getanewsourcetoken(); 1783 if ( asourcetoken == null ) return; 1784 yychar = asourcetoken.tokencode; 1785 1786 while (yychar > 0) 1787 { 1788 sourcetokenlist.add(asourcetoken); 1789 asourcetoken = getanewsourcetoken(); 1790 if ( asourcetoken == null ) break; 1791 yychar = asourcetoken.tokencode; 1792 } 1793 1794} 1795void dosqltexttotokenlist(){ 1796 // Legacy path: Only called for non-delegated vendors (e.g., dbvfirebird, dbvexasol) 1797 // All delegated vendors (mssql, access, generic, mysql, oracle, etc.) use getOrCreateVendorParser() 1798 docommonsqltexttotokenlist(); 1799 1800 doAfterTokenize(); 1801 1802 TBaseType.resetTokenChain(sourcetokenlist,0); 1803 1804 processTokensInTokenTable(dbVendor); 1805 processTokensBeforeParse(dbVendor); 1806 1807 closeFileStream(); 1808 1809 if (tokenListHandle != null){ 1810 tokenListHandle.processTokenList(sourcetokenlist); 1811 } 1812} 1813 1814void doAfterTokenize(){ 1815 1816 int leftParenCount = 0; 1817 int rightParenCount = 0; 1818 int leftIndex = 0; 1819 int rightIndex = sourcetokenlist.size() - 1; 1820 1821 // Count opening parentheses at the beginning 1822 while (leftIndex < sourcetokenlist.size() && sourcetokenlist.get(leftIndex).tokencode == '(') { 1823 leftParenCount++; 1824 leftIndex++; 1825 } 1826 1827 // Count closing parentheses at the end 1828 while (rightIndex >= 0 && sourcetokenlist.get(rightIndex).tokencode == ')') { 1829 rightParenCount++; 1830 rightIndex--; 1831 } 1832 1833 // Set matching parentheses to be ignored 1834 int parensToIgnore = Math.min(leftParenCount, rightParenCount); 1835 // if there is a semicolon before the right parenthesis, set the semicolon to be ignored 1836 // mantisbt/view.php?id=3690 1837 1838 if ((parensToIgnore > 0) && (sourcetokenlist.get(sourcetokenlist.size() - 1 - (parensToIgnore - 1) - 1).tokencode == ';')){ 1839 // set to whitespace that this semicolon will be ignored during getting raw sql 1840 sourcetokenlist.get(sourcetokenlist.size() - 1 - (parensToIgnore - 1) - 1).tokentype = ETokenType.ttwhitespace; 1841 // set to ignore by yacc that this semicolon will be ignored during parsing 1842 sourcetokenlist.get(sourcetokenlist.size() - 1 - (parensToIgnore - 1) - 1).tokenstatus = ETokenStatus.tsignorebyyacc; 1843 } 1844// for (int i = 0; i < parensToIgnore; i++) { 1845// //sourcetokenlist.get(i).tokenstatus = ETokenStatus.tsignorebyyacc; 1846// sourcetokenlist.get(i).tokencode = TBaseType.lexspace; 1847// 1848// // sourcetokenlist.get(sourcetokenlist.size() - 1 - i).tokenstatus = ETokenStatus.tsignorebyyacc; 1849// sourcetokenlist.get(sourcetokenlist.size() - 1 - i).tokencode = TBaseType.lexspace; 1850// } 1851 1852} 1853 1854 1855 1856void processTokensBeforeParse(EDbVendor dbVendor){ 1857 1858 // 为确保性能,只在snowflake数据库中处理,因为目前只有snowflake数据库中有连续的分号有用户提出这个需求,其他数据库中暂时不处理 1859 if (dbVendor != EDbVendor.dbvsnowflake) return; 1860 1861 // mantisbt/view.php?id=3579 1862 // if there are consecutive semicolon tokens, mark the second semi colon token as deleted token 1863 for(int i=0;i<sourcetokenlist.size();i++){ 1864 TSourceToken st = sourcetokenlist.get(i); 1865 if (st.tokencode == ';'){ 1866 TSourceToken nextToken = st.nextSolidToken(); 1867 if (nextToken != null){ 1868 if (nextToken.tokencode == ';'){ 1869 nextToken.tokenstatus = ETokenStatus.tsdeleted; 1870 } 1871 } 1872 } 1873 } 1874 1875} 1876 1877void processTokensInTokenTable(EDbVendor dbVendor){ 1878 // 获得所有token后,根据需要对token code进行预处理, token table是 TBaseType.TOKEN_TABLE 1879 long[][] TOKEN_TABLE1 = flexer.TOKEN_TABLE; 1880 1881 switch (dbVendor){ 1882 case dbvbigquery: 1883 case dbvsnowflake: 1884 // case 1, DO 关键字如果没有发现对于的 FOR, WHILE 等关键字,把 DO 关键字的token code设置为 TBaseType.ident 1885 1886 if (TOKEN_TABLE1[TBaseType.rrw_do][0] > 0){ 1887 if ((TOKEN_TABLE1[TBaseType.rrw_while][0] == 0)&&(TOKEN_TABLE1[TBaseType.rrw_for][0] == 0)){ 1888 for(int i=0;i<sourcetokenlist.size();i++){ 1889 TSourceToken st = sourcetokenlist.get(i); 1890 if (st.tokencode == TBaseType.rrw_do){ 1891 st.tokencode = TBaseType.ident; 1892 } 1893 } 1894 } 1895 } 1896 1897 break; 1898 } 1899 1900} 1901 1902boolean isDollarFunctionDelimiter(int tokencode, EDbVendor dbVendor){ 1903 return ((tokencode == TBaseType.rrw_postgresql_function_delimiter)&&(dbVendor == EDbVendor.dbvpostgresql)) 1904 ||((tokencode == TBaseType.rrw_greenplum_function_delimiter)&&(dbVendor == EDbVendor.dbvgreenplum)) 1905 ||((tokencode == TBaseType.rrw_redshift_function_delimiter)&&(dbVendor == EDbVendor.dbvredshift)) 1906 ||((tokencode == TBaseType.rrw_snowflake_function_delimiter)&&(dbVendor == EDbVendor.dbvsnowflake)); 1907} 1908 1909/** 1910 * Returns the 1-based line number of the end of the last SQL statement that 1911 * was successfully recognized during raw-statement separation (e.g. via 1912 * vendor-specific {@code do*getrawsqlstatements} routines). 1913 * <p> 1914 * The value corresponds to the ending line of the last validated statement in 1915 * the most recent parse operation. Any trailing, incomplete statement at the 1916 * end of the input is intentionally excluded and will not affect this value. 1917 * <p> 1918 * Notes: 1919 * <ul> 1920 * <li>The line number is relative to the current input provided to the 1921 * parser (not an absolute position in an external, larger file).</li> 1922 * <li>This is useful when splitting huge SQL files by safe statement 1923 * boundaries — callers can cut the source at this line without risking a 1924 * partial statement.</li> 1925 * </ul> 1926 * 1927 * @return the 1-based line number of the last validated statement's ending 1928 * line, or {@code -1} if no statement has been validated yet 1929 */ 1930public int getLastLineNoOfLastStatementBeenValidated(){ 1931 if (lastTokenOfStatementBeenValidated != null){ 1932 return (int)lastTokenOfStatementBeenValidated.lineNo; 1933 } 1934 return -1; 1935} 1936 1937private TSourceToken lastTokenOfStatementBeenValidated; 1938 1939 1940void doongetrawsqlstatementevent(TCustomSqlStatement pcsqlstatement){ 1941 doongetrawsqlstatementevent(pcsqlstatement,false); 1942} 1943 1944void doongetrawsqlstatementevent(TCustomSqlStatement pcsqlstatement, boolean isLastSQL){ 1945 pcsqlstatement.setGsqlparser(this); 1946 pcsqlstatement.parser = this.fparser; 1947 pcsqlstatement.plsqlparser = this.fplsqlparser; 1948 pcsqlstatement.setStartToken(pcsqlstatement.sourcetokenlist.get(0)); 1949 pcsqlstatement.setEndToken(pcsqlstatement.sourcetokenlist.get(pcsqlstatement.sourcetokenlist.size()-1)); 1950 sqlstatements.add(pcsqlstatement); 1951 1952 if (!isLastSQL){ // 最后一个语句没有经过验证,只是在结束的时候强行加入的,很有可能是语法不正确的,因此我们这里只记录非最后的语句 1953 lastTokenOfStatementBeenValidated = pcsqlstatement.getEndToken(); 1954 } 1955 1956 // if stored procedure body is not written in sql or plsql, then, set the token in body to 1957 if ( ((this.dbVendor == EDbVendor.dbvpostgresql)||(this.dbVendor == EDbVendor.dbvgreenplum) ||(this.dbVendor == EDbVendor.dbvredshift) ||(this.dbVendor == EDbVendor.dbvsnowflake) ) 1958 && (pcsqlstatement instanceof TRoutine) 1959 ){ 1960 if (!((TRoutine)pcsqlstatement).isBodyInSQL()){ 1961 TSourceToken st; 1962 boolean inBody = false; 1963 StringBuilder routineBodyBuilder = new StringBuilder(); 1964 for(int i=0;i<pcsqlstatement.sourcetokenlist.size();i++){ 1965 st = pcsqlstatement.sourcetokenlist.get(i); 1966 if (isDollarFunctionDelimiter(st.tokencode,this.dbVendor) 1967// ((st.tokencode == TBaseType.rrw_postgresql_function_delimiter)&&(this.dbVendor == EDbVendor.dbvpostgresql)) 1968// ||((st.tokencode == TBaseType.rrw_greenplum_function_delimiter)&&(this.dbVendor == EDbVendor.dbvgreenplum)) 1969// ||((st.tokencode == TBaseType.rrw_redshift_function_delimiter)&&(this.dbVendor == EDbVendor.dbvredshift)) 1970// ||((st.tokencode == TBaseType.rrw_snowflake_function_delimiter)&&(this.dbVendor == EDbVendor.dbvsnowflake)) 1971 ){ 1972 if (!inBody){ 1973 inBody = true; 1974 routineBodyBuilder.setLength(0); 1975 routineBodyBuilder.append(st.toString()); 1976 }else{ 1977 inBody = false; 1978 routineBodyBuilder.append(st.toString()); 1979 break; 1980 } 1981 continue; 1982 } 1983 1984 if (inBody){ 1985 st.tokencode = TBaseType.sqlpluscmd; 1986 routineBodyBuilder.append(st.toString()); 1987 } 1988 } 1989 1990 ((TRoutine)pcsqlstatement).setRoutineBody(routineBodyBuilder.toString()); 1991 } 1992 } 1993} 1994 1995 boolean checkTokenPairWithEnd(int tokencode){ 1996 return ((tokencode == TBaseType.rrw_if)||(tokencode == TBaseType.rrw_case) 1997 ||(tokencode == TBaseType.rrw_loop)||(tokencode == TBaseType.rrw_repeat) 1998 ||(tokencode == TBaseType.rrw_while)||(tokencode == TBaseType.rrw_for) 1999 ||(tokencode == TBaseType.rrw_case) 2000 ); 2001 } 2002 2003 private TCustomSqlStatement startDaxStmt(TSourceToken currToken,TCustomSqlStatement currStmt){ 2004 TCustomSqlStatement newStmt = null; 2005 if (currToken == null) return null; 2006 if ((currToken.tokencode == '=')&&(currToken.isFirstTokenOfLine())){ 2007 currToken.tokencode = TBaseType.equal_start_expr; 2008 newStmt = new TDaxExprStmt(EDbVendor.dbvdax); 2009 }else if ((currToken.tokencode == TBaseType.rrw_dax_define)&&(currToken.isFirstTokenOfLine())){ 2010 newStmt = new TDaxEvaluateStmt(EDbVendor.dbvdax); 2011 ((TDaxEvaluateStmt)newStmt).setStartWithDefine(true); 2012 }else if ((currToken.tokencode == TBaseType.rrw_dax_evaluate)&&(currToken.isFirstTokenOfLine())){ 2013 if ((currStmt != null)&&(currStmt instanceof TDaxEvaluateStmt)){ 2014 TDaxEvaluateStmt tmp = (TDaxEvaluateStmt)currStmt; 2015 if (tmp.isStartWithDefine()) return null; 2016 } 2017 newStmt = new TDaxEvaluateStmt(EDbVendor.dbvdax); 2018 } 2019 2020 if (newStmt == null){ 2021 // let's check is this the first token of query 2022 boolean isFirst = currToken.isFirstTokenOfLine(); 2023 TSourceToken prevToken = currToken.prevSolidToken(); 2024 if ((isFirst)&&(prevToken == null)){ 2025 newStmt = new TDaxExprStmt(EDbVendor.dbvdax); 2026 } 2027 } 2028 return newStmt; 2029 } 2030 2031int dogetrawsqlstatements(){ 2032 // This method should not be called - all vendors are now delegated to vendor-specific parsers. 2033 // If this is reached, it means a new vendor was added but not included in getOrCreateVendorParser(). 2034 throw new IllegalStateException( 2035 "dogetrawsqlstatements() called for vendor " + dbVendor + 2036 ". All vendors should be delegated via getOrCreateVendorParser(). " + 2037 "Please add this vendor to the switch statement in getOrCreateVendorParser()."); 2038} 2039 2040 /** 2041 * separates the SQL statements in the input SQL script without doing syntax check. 2042 * <p></p> 2043 * Use the {@link #getSqlstatements()} method to get the list of SQL statements. 2044 * The SQL statement object is the instance of the sub-class of {@link TCustomSqlStatement}, get SQL statement type 2045 * via the {@link TCustomSqlStatement#sqlstatementtype} field, get string representation of 2046 * each SQL statement via the {@link TCustomSqlStatement#toString} method. 2047 * <p></p> 2048 * All source tokens in this SQL statement 2049 * is available by using {@link TCustomSqlStatement#sourcetokenlist} filed. 2050 * Since no parse tree is built by calling this method, no further detailed information about the SQL statement is available. 2051 * 2052 * @return 0 if get SQL statements successfully 2053 */ 2054public int getrawsqlstatements(){ 2055 // Clear errors from any previous call on this instance 2056 syntaxErrors.clear(); 2057 2058 // Check for vendor parser delegation (MSSQL, etc.) 2059 // This ensures raw-split logic is maintained in only one place (vendor parser) 2060 SqlParser vp = getOrCreateVendorParser(); 2061 if (vp != null) { 2062 return doDelegatedRawParse(vp); 2063 } 2064 2065 // Legacy path for non-delegated vendors 2066 int ret = readsql(); 2067 if (ret != 0) return ret; 2068 dosqltexttotokenlist(); 2069 2070 return dogetrawsqlstatements(); 2071} 2072 2073 /** 2074 * turns the input SQL into a sequence of token which is the 2075 * basic lexis element of SQL syntax. Token is categorized as keyword, identifier, 2076 * number, operator, whitespace and other types. All source tokens can be fetched 2077 * via the {@link #getSourcetokenlist()} method. 2078 * 2079 */ 2080public void tokenizeSqltext(){ 2081 // Check for vendor parser delegation (MSSQL, etc.) 2082 // This ensures tokenization logic is maintained in only one place (vendor parser) 2083 SqlParser vp = getOrCreateVendorParser(); 2084 if (vp != null) { 2085 doDelegatedTokenize(vp); 2086 return; 2087 } 2088 2089 // Legacy path for non-delegated vendors 2090 getFlexer(); 2091 readsql(); 2092 dosqltexttotokenlist(); 2093} 2094 2095/** 2096 * Delegate tokenization to vendor parser and backfill results. 2097 * Similar to doDelegatedRawParse but only performs tokenization. 2098 */ 2099private void doDelegatedTokenize(SqlParser vendorParser) { 2100 // Build context for vendor parser 2101 ParserContext context = buildContext(); 2102 2103 // Delegate tokenization to vendor parser 2104 SqlParseResult tokenResult = vendorParser.tokenize(context); 2105 2106 // Copy results back to TGSqlParser fields for backward compatibility 2107 if (tokenResult.getSourceTokenList() != null) { 2108 this.sourcetokenlist = tokenResult.getSourceTokenList(); 2109 } 2110 if (tokenResult.getLexer() != null) { 2111 this.flexer = tokenResult.getLexer(); 2112 } 2113} 2114 2115 2116 2117void findAllSyntaxErrorsInPlsql(TCustomSqlStatement psql){ 2118 if (psql.getErrorCount() > 0){ 2119 copyerrormsg(psql); 2120 } 2121 2122 for (int k=0;k<psql.getStatements().size();k++){ 2123 findAllSyntaxErrorsInPlsql(psql.getStatements().get(k)); 2124 } 2125 2126} 2127 2128private TSQLEnv sqlEnv = null; 2129 2130 /** 2131 * SQL environment includes the database metadata such as procedure, function, trigger, table and etc. 2132 * 2133 * In order to link column to table correctly without connecting to database, 2134 * we need to provide a class which implements {@link TSQLEnv} to TGSqlParser. 2135 * this class tells TGSqlParser the relationship between column and table. 2136 * 2137 * <p>Take this SQL for example: 2138 * <pre> 2139 * SELECT Quantity,b.Time,c.Description 2140 * FROM 2141 * (SELECT ID2,Time FROM bTab) b 2142 * INNER JOIN aTab a on a.ID=b.ID 2143 * INNER JOIN cTab c on a.ID=c.ID 2144 * </pre> 2145 * 2146 * <p>General SQL Parser can build relationship between column: ID2 and table: bTable 2147 * correctly without metadata information from database because there is only one table 2148 * in from clause. But it can't judge column: Quantity belong to table: aTab or cTab, 2149 * since no table alias was prefixed to column: Quantity. If no metadata provided, 2150 * General SQL Parser will link column: Quantity to the first valid table (here it is aTab) 2151 * 2152 * <p>If we create a class TRealDatabaseSQLEnv implements {@link TSQLEnv},then 2153 * {@link #setSqlEnv(TSQLEnv)}, General SQL Parser can take this advantage to create 2154 * a correct relationship between column and tables. 2155 * 2156 *<pre> 2157 * class TSQLServerEnv extends TSQLEnv{ 2158 * 2159 * public TSQLServerEnv(){ 2160 * super(EDbVendor.dbvmssql); 2161 * initSQLEnv(); 2162 * } 2163 * 2164 * @Override 2165 * public void initSQLEnv() { 2166 * 2167 * // add a new database: master 2168 * TSQLCatalog sqlCatalog = createSQLCatalog("master"); 2169 * // add a new schema: dbo 2170 * TSQLSchema sqlSchema = sqlCatalog.createSchema("dbo"); 2171 * //add a new table: aTab 2172 * TSQLTable aTab = sqlSchema.createTable("aTab"); 2173 * aTab.addColumn("Quantity1"); 2174 * 2175 * //add a new table: bTab 2176 * TSQLTable bTab = sqlSchema.createTable("bTab"); 2177 * bTab.addColumn("Quantity2"); 2178 * 2179 * //add a new table: cTab 2180 * TSQLTable cTab = sqlSchema.createTable("cTab"); 2181 * cTab.addColumn("Quantity"); 2182 * 2183 * } 2184 * } 2185 * </pre> 2186 * 2187 * @return SQL environment 2188 */ 2189 public TSQLEnv getSqlEnv() { 2190 return sqlEnv; 2191 } 2192 2193 private boolean onlyNeedRawParseTree = false; 2194 2195 public void setOnlyNeedRawParseTree(boolean onlyNeedRawParseTree) { 2196 this.onlyNeedRawParseTree = onlyNeedRawParseTree; 2197 } 2198 2199 public void setSqlEnv(TSQLEnv sqlEnv) { 2200 this.sqlEnv = sqlEnv; 2201 } 2202 2203 2204 // Time tracking variables 2205 private boolean enableTimeLogging = false; 2206 private long rawSqlStatementsTime = 0; 2207 private long parsingTime = 0; 2208 private long semanticAnalysisTime = 0; 2209 private long interpreterTime = 0; 2210 2211 /** 2212 * Enable or disable time logging for parser steps 2213 * @param enable true to enable time logging, false to disable 2214 */ 2215 public void setEnableTimeLogging(boolean enable) { 2216 this.enableTimeLogging = enable; 2217 } 2218 2219 /** 2220 * Check if time logging is enabled 2221 * @return true if time logging is enabled, false otherwise 2222 */ 2223 public boolean isTimeLoggingEnabled() { 2224 return this.enableTimeLogging; 2225 } 2226 2227 /** 2228 * Reset all accumulated time counters to zero 2229 */ 2230 public void resetTimeCounters() { 2231 rawSqlStatementsTime = 0; 2232 parsingTime = 0; 2233 semanticAnalysisTime = 0; 2234 interpreterTime = 0; 2235 } 2236 2237 /** 2238 * Get accumulated time spent getting raw SQL statements in milliseconds 2239 * @return time in milliseconds 2240 */ 2241 public long getRawSqlStatementsTime() { 2242 return rawSqlStatementsTime; 2243 } 2244 2245 /** 2246 * Get accumulated time spent parsing in milliseconds 2247 * @return time in milliseconds 2248 */ 2249 public long getParsingTime() { 2250 return parsingTime; 2251 } 2252 2253 /** 2254 * Get accumulated time spent on semantic analysis in milliseconds 2255 * @return time in milliseconds 2256 */ 2257 public long getSemanticAnalysisTime() { 2258 return semanticAnalysisTime; 2259 } 2260 2261 /** 2262 * Get accumulated time spent in interpreter in milliseconds 2263 * @return time in milliseconds 2264 */ 2265 public long getInterpreterTime() { 2266 return interpreterTime; 2267 } 2268 2269 /** 2270 * Get total accumulated time spent in all steps 2271 * @return total time in milliseconds 2272 */ 2273 public long getTotalTime() { 2274 return rawSqlStatementsTime + parsingTime + semanticAnalysisTime + interpreterTime; 2275 } 2276 2277 /** 2278 * Get or create the vendor-specific parser for delegation. 2279 * The parser is cached in vendorParser field and reused for subsequent calls. 2280 * This allows getFlexer() to lazily create the parser to access its lexer. 2281 * 2282 * @return vendor-specific SqlParser or null if not a delegated vendor 2283 * @since 3.2.0.0 2284 */ 2285 private SqlParser getOrCreateVendorParser() { 2286 // Return cached parser if available 2287 if (vendorParser != null) { 2288 return vendorParser; 2289 } 2290 2291 // Create new vendor parser based on database vendor 2292 switch (dbVendor) { 2293 case dbvmssql: 2294 case dbvazuresql: 2295 case dbvaccess: // Access uses MSSQL lexer/parser 2296 case dbvgeneric: // Generic uses MSSQL lexer/parser 2297 case dbvfirebird: // Firebird uses MSSQL lexer/parser 2298 case dbvexasol: // Exasol uses MSSQL lexer/parser 2299 vendorParser = new gudusoft.gsqlparser.parser.MssqlSqlParser(); 2300 break; 2301 case dbvmysql: 2302 vendorParser = new gudusoft.gsqlparser.parser.MySqlSqlParser(); 2303 break; 2304 case dbvpostgresql: 2305 vendorParser = new gudusoft.gsqlparser.parser.PostgreSqlParser(); 2306 break; 2307 case dbvduckdb: 2308 vendorParser = new gudusoft.gsqlparser.parser.DuckdbSqlParser(); 2309 break; 2310 case dbvoracle: 2311 vendorParser = new gudusoft.gsqlparser.parser.OracleSqlParser(); 2312 break; 2313 case dbvbigquery: 2314 vendorParser = new gudusoft.gsqlparser.parser.BigQuerySqlParser(); 2315 break; 2316 case dbvathena: 2317 vendorParser = new gudusoft.gsqlparser.parser.AthenaSqlParser(); 2318 break; 2319 case dbvcouchbase: 2320 vendorParser = new gudusoft.gsqlparser.parser.CouchbaseSqlParser(); 2321 break; 2322 case dbvdatabricks: 2323 vendorParser = new gudusoft.gsqlparser.parser.DatabricksSqlParser(); 2324 break; 2325 case dbvdax: 2326 vendorParser = new gudusoft.gsqlparser.parser.DaxSqlParser(); 2327 break; 2328 case dbvpowerquery: 2329 vendorParser = new gudusoft.gsqlparser.parser.PowerQuerySqlParser(); 2330 break; 2331 case dbvdb2: 2332 vendorParser = new gudusoft.gsqlparser.parser.Db2SqlParser(); 2333 break; 2334 case dbvdoris: 2335 vendorParser = new gudusoft.gsqlparser.parser.DorisSqlParser(); 2336 break; 2337 case dbvstarrocks: 2338 vendorParser = new gudusoft.gsqlparser.parser.StarrocksSqlParser(); 2339 break; 2340 case dbvflink: 2341 vendorParser = new gudusoft.gsqlparser.parser.FlinkSqlParser(); 2342 break; 2343 case dbvgaussdb: 2344 vendorParser = new gudusoft.gsqlparser.parser.GaussDbSqlParser(); 2345 break; 2346 case dbvedb: 2347 vendorParser = new gudusoft.gsqlparser.parser.EdbSqlParser(); 2348 break; 2349 case dbvdameng: 2350 vendorParser = new gudusoft.gsqlparser.parser.DamengSqlParser(); 2351 break; 2352 case dbvoceanbase: 2353 vendorParser = new gudusoft.gsqlparser.parser.OceanBaseSqlParser(); 2354 break; 2355 case dbvgreenplum: 2356 vendorParser = new gudusoft.gsqlparser.parser.GreenplumSqlParser(); 2357 break; 2358 case dbvhive: 2359 vendorParser = new gudusoft.gsqlparser.parser.HiveSqlParser(); 2360 break; 2361 case dbvhana: 2362 vendorParser = new gudusoft.gsqlparser.parser.HanaSqlParser(); 2363 break; 2364 case dbvimpala: 2365 vendorParser = new gudusoft.gsqlparser.parser.ImpalaSqlParser(); 2366 break; 2367 case dbvinformix: 2368 vendorParser = new gudusoft.gsqlparser.parser.InformixSqlParser(); 2369 break; 2370 case dbvmdx: 2371 vendorParser = new gudusoft.gsqlparser.parser.MdxSqlParser(); 2372 break; 2373 case dbvnetezza: 2374 vendorParser = new gudusoft.gsqlparser.parser.NetezzaSqlParser(); 2375 break; 2376 case dbvodbc: 2377 vendorParser = new gudusoft.gsqlparser.parser.OdbcSqlParser(); 2378 break; 2379 case dbvopenedge: 2380 vendorParser = new gudusoft.gsqlparser.parser.OpenEdgeSqlParser(); 2381 break; 2382 case dbvpresto: 2383 vendorParser = new gudusoft.gsqlparser.parser.PrestoSqlParser(); 2384 break; 2385 case dbvredshift: 2386 vendorParser = new gudusoft.gsqlparser.parser.RedshiftSqlParser(); 2387 break; 2388 case dbvsnowflake: 2389 vendorParser = new gudusoft.gsqlparser.parser.SnowflakeSqlParser(); 2390 break; 2391 case dbvsqlite: 2392 vendorParser = new gudusoft.gsqlparser.parser.SqliteSqlParser(); 2393 break; 2394 case dbvclickhouse: 2395 vendorParser = new gudusoft.gsqlparser.parser.ClickhouseSqlParser(); 2396 break; 2397 case dbvsoql: 2398 vendorParser = new gudusoft.gsqlparser.parser.SoqlSqlParser(); 2399 break; 2400 case dbvsparksql: 2401 vendorParser = new gudusoft.gsqlparser.parser.SparksqlSqlParser(); 2402 break; 2403 case dbvsybase: 2404 vendorParser = new gudusoft.gsqlparser.parser.SybaseSqlParser(); 2405 break; 2406 case dbvteradata: 2407 vendorParser = new gudusoft.gsqlparser.parser.TeradataSqlParser(); 2408 break; 2409 case dbvtrino: 2410 vendorParser = new gudusoft.gsqlparser.parser.TrinoSqlParser(); 2411 break; 2412 case dbvvertica: 2413 vendorParser = new gudusoft.gsqlparser.parser.VerticaSqlParser(); 2414 break; 2415 case dbvansi: 2416 vendorParser = new gudusoft.gsqlparser.parser.AnsiSqlParser(); 2417 break; 2418 default: 2419 return null; 2420 } 2421 return vendorParser; 2422 } 2423 2424 /** 2425 * Prepare this parser for reuse by clearing cached state. 2426 * This is useful when reusing a TGSqlParser instance (e.g., in TExecImmeStmt) 2427 * to avoid state pollution between parsing operations. 2428 * 2429 * Clears: vendorParser, sqlEnv, sqlfilename, and resets parsing options. 2430 * 2431 * @since 3.2.0.0 2432 */ 2433 public void prepareForReuse() { 2434 // Clear cached vendor parser to force recreation 2435 this.vendorParser = null; 2436 // Clear SQL environment to avoid old schema information affecting new parse 2437 this.sqlEnv = null; 2438 // Clear filename in case it was set previously 2439 this.sqlfilename = null; 2440 // Reset parsing options to defaults 2441 this.isSinglePLBlock = false; 2442 // Clear statement list 2443 if (this.sqlstatements != null) { 2444 this.sqlstatements.clear(); 2445 } 2446 // Clear error state 2447 this.syntaxErrors.clear(); 2448 } 2449 2450 /** 2451 * Build a ParserContext from current TGSqlParser state. 2452 * This creates an immutable context for delegation to vendor parsers. 2453 * 2454 * @return immutable ParserContext 2455 * @since 3.2.0.0 2456 */ 2457 private ParserContext buildContext() { 2458 ParserContext.Builder builder = new ParserContext.Builder(this.dbVendor); 2459 2460 // Set SQL text source (only one will be non-null or non-empty) 2461 // CRITICAL: Check for non-null AND non-empty because TGSqlParser initializes sqltext="" 2462 // which would cause vendor parser to use empty string instead of reading from file 2463 if (this.sqltext != null && !this.sqltext.isEmpty()) { 2464 builder.sqlText(this.sqltext); 2465 } 2466 if (this.sqlfilename != null && !this.sqlfilename.isEmpty()) { 2467 builder.sqlFilename(this.sqlfilename); 2468 } 2469 2470 // Set charset 2471 if (this.sqlCharset != null) { 2472 builder.sqlCharset(this.sqlCharset); 2473 } 2474 2475 // Set parsing options 2476 builder.enablePartialParsing(this.isEnablePartialParsing()); 2477 builder.singlePLBlock(this.isSinglePLBlock); 2478 2479 // Mirror OceanBase tenant mode into the context so OceanBaseSqlParser 2480 // (and any other downstream component) can read it without a 2481 // back-reference to TGSqlParser. 2482 builder.oceanBaseTenantMode(this.oceanBaseTenantMode); 2483 2484 // Set gsqlparser reference for backward compatibility 2485 builder.gsqlparser(this); 2486 2487 // Set SQL environment if available 2488 if (this.sqlEnv != null) { 2489 builder.sqlEnv(this.sqlEnv); 2490 } 2491 2492 // Set token handle for callback during tokenization 2493 if (this.tokenHandle != null) { 2494 builder.tokenHandle(this.tokenHandle); 2495 } 2496 2497 return builder.build(); 2498 } 2499 2500 /** 2501 * Returns time statistics as a formatted string 2502 * @return formatted string with time statistics 2503 */ 2504 public String getTimeStatistics() { 2505 if (!enableTimeLogging) { 2506 return "Time logging is disabled"; 2507 } 2508 2509 StringBuilder sb = new StringBuilder(); 2510 sb.append(String.format("Time statistics for TGSqlParser version: %s, released at: %s, dbvendor: %s:\n", TBaseType.versionid,TBaseType.releaseDate, this.dbVendor)); 2511 sb.append(String.format("1. Raw SQL statements: %d ms (%.2f%%)\n", 2512 rawSqlStatementsTime, 2513 getTotalTime() > 0 ? 100.0 * rawSqlStatementsTime / getTotalTime() : 0)); 2514 sb.append(String.format("2. Parsing: %d ms (%.2f%%)\n", 2515 parsingTime, 2516 getTotalTime() > 0 ? 100.0 * parsingTime / getTotalTime() : 0)); 2517 sb.append(String.format("3. Semantic analysis: %d ms (%.2f%%)\n", 2518 semanticAnalysisTime, 2519 getTotalTime() > 0 ? 100.0 * semanticAnalysisTime / getTotalTime() : 0)); 2520 sb.append(String.format("4. Interpreter: %d ms (%.2f%%)\n", 2521 interpreterTime, 2522 getTotalTime() > 0 ? 100.0 * interpreterTime / getTotalTime() : 0)); 2523 sb.append(String.format("Total: %d ms", getTotalTime())); 2524 return sb.toString(); 2525 } 2526 2527 /** 2528 * Delegate only tokenization and raw statement extraction to vendor parser. 2529 * The actual AST parsing is done by the common parsing loop in doparse(). 2530 * This architecture ensures that: 2531 * 1. Vendor-specific tokenization/lexing is handled by vendor parser 2532 * 2. Raw statement extraction (splitting SQL text into statements) is vendor-specific 2533 * 3. AST parsing (building parse tree for each statement) uses common code path 2534 * 2535 * @param vendorParser the vendor-specific parser to use 2536 * @return 0 if successful, non-zero on error 2537 */ 2538 private int doDelegatedRawParse(SqlParser vendorParser) { 2539 long phaseStart, phaseEnd; 2540 2541 // Build context for vendor parser 2542 ParserContext context = buildContext(); 2543 2544 // Delegate only tokenization + raw statement extraction to vendor parser 2545 phaseStart = enableTimeLogging ? System.currentTimeMillis() : 0; 2546 SqlParseResult rawResult = vendorParser.getrawsqlstatements(context); 2547 if (enableTimeLogging) { 2548 phaseEnd = System.currentTimeMillis(); 2549 rawSqlStatementsTime += (phaseEnd - phaseStart); 2550 } 2551 2552 // Copy results back to TGSqlParser fields for backward compatibility 2553 if (rawResult.getSourceTokenList() != null) { 2554 this.sourcetokenlist = rawResult.getSourceTokenList(); 2555 this.sourcetokenlist.setGsqlparser(this); 2556 } 2557 if (rawResult.getLexer() != null) { 2558 this.flexer = rawResult.getLexer(); 2559 } 2560 // Copy parser from vendor parser result - needed for parsestatement() 2561 if (rawResult.getParser() != null) { 2562 this.fparser = rawResult.getParser(); 2563 // CRITICAL: Set gsqlparser on NodeFactory for correct AST construction 2564 this.fparser.getNf().setGsqlParser(this); 2565 } 2566 // Copy secondary parser (for Oracle PL/SQL) and set its NodeFactory 2567 if (rawResult.getSecondaryParser() != null) { 2568 this.fplsqlparser = rawResult.getSecondaryParser(); 2569 // CRITICAL: Also set gsqlparser on secondary parser's NodeFactory 2570 // This is needed for Oracle PL/SQL statements which use fplsqlparser for parsing 2571 this.fplsqlparser.getNf().setGsqlParser(this); 2572 } 2573 if (rawResult.getSqlStatements() != null) { 2574 this.sqlstatements = rawResult.getSqlStatements(); 2575 } 2576 2577 // Copy lastTokenOfStatementBeenValidated for getLastLineNoOfLastStatementBeenValidated() API 2578 this.lastTokenOfStatementBeenValidated = rawResult.getLastTokenOfStatementBeenValidated(); 2579 2580 // Copy any tokenization errors 2581 if (rawResult.getSyntaxErrors() != null) { 2582 for (TSyntaxError error : rawResult.getSyntaxErrors()) { 2583 this.syntaxErrors.add(error); 2584 } 2585 } 2586 2587 return rawResult.getErrorCode(); 2588 } 2589 2590 int doparse() { 2591 int j; 2592 long startTime, endTime; 2593 boolean useDelegatedRawParse = false; 2594 2595 // Clear errors from any previous parse() call on this instance 2596 syntaxErrors.clear(); 2597 2598 // 1. Get raw SQL statements 2599 // For delegated vendors, use vendor parser for tokenization + raw extraction 2600 // The parsing loop below is common for all vendors 2601 SqlParser vp = getOrCreateVendorParser(); 2602 if (vp != null) { 2603 int ret = doDelegatedRawParse(vp); 2604 if (ret != 0) { 2605 // Tokenization/extraction failed, return error 2606 return ret; 2607 } 2608 useDelegatedRawParse = true; 2609 // Continue to common parsing loop below 2610 } 2611 2612 // Legacy path: get raw statements for non-delegated vendors 2613 if (!useDelegatedRawParse) { 2614 startTime = enableTimeLogging ? System.currentTimeMillis() : 0; 2615 2616 int ret = getrawsqlstatements(); 2617 2618 if (enableTimeLogging) { 2619 endTime = System.currentTimeMillis(); 2620 rawSqlStatementsTime += (endTime - startTime); 2621 } 2622 } 2623 2624 boolean isPushGloablStack = false; 2625 //if (ret != 0) return ret; 2626 TFrame firstFrame = null; 2627 2628 globalContext = new TContext(); 2629 2630 if (this.sqlEnv == null) { 2631 this.sqlEnv = new TSQLEnv(this.dbVendor) { 2632 @Override 2633 public void initSQLEnv() { 2634 } 2635 }; 2636 // this.sqlEnv.setEnableGetMetadataFromDDL(false); 2637 } 2638 globalContext.setSqlEnv(this.sqlEnv, this.sqlstatements); 2639 2640 //TStackFrame stackFrame = new TStackFrame(new TGlobalScope()); 2641 if (getFrameStack().size() == 0) { // stack passed from outside gsqlparser will contain frames 2642 //getFrameStack().push(new TStackFrame(new TGlobalScope())); 2643 TGlobalScope globalScope = new TGlobalScope(); 2644 globalScope.resetCurrentStmtIndex(); 2645 2646 globalScope.setSqlEnv(this.sqlEnv); 2647 firstFrame = new TFrame(globalScope); 2648 firstFrame.pushMeToStack(getFrameStack()); 2649 isPushGloablStack = true; 2650 } 2651 2652 // 2. start parsing 2653 startTime = enableTimeLogging ? System.currentTimeMillis() : 0; 2654 2655 for (int i = 0; i < sqlstatements.size(); i++) { 2656 sqlstatements.getRawSql(i).setFrameStack(frameStack); 2657 j = sqlstatements.getRawSql(i).parsestatement(null, false, onlyNeedRawParseTree); 2658 2659 // NOTE: No need for setGsqlparserRecursively() here for delegated vendors. 2660 // The gsqlparser reference is already set on all AST nodes through two mechanisms: 2661 // 1. doDelegatedRawParse() sets fparser.getNf().setGsqlParser(this) and 2662 // fplsqlparser.getNf().setGsqlParser(this) BEFORE parsestatement() is called 2663 // 2. TNodeFactory.createNode() propagates gsqlparser to every node it creates 2664 // The recursive walk was causing 4-5x performance degradation for Oracle parsing 2665 // by redundantly traversing the entire AST after parsing. 2666 2667 TCustomSqlStatement sql0 = null; 2668 if (sqlstatements.get(i).isoracleplsql()) { 2669 // check syntax error in select/insert statement inside plsql 2670 sql0 = sqlstatements.get(i); 2671 findAllSyntaxErrorsInPlsql(sql0); 2672 } 2673 2674 boolean doRecover = TBaseType.ENABLE_ERROR_RECOVER_IN_CREATE_TABLE; 2675 2676 if (doRecover && ((j != 0) || (sqlstatements.get(i).getErrorCount() > 0))) { 2677 if (((sqlstatements.get(i).sqlstatementtype == ESqlStatementType.sstcreatetable) 2678 || ((sqlstatements.get(i).sqlstatementtype == ESqlStatementType.sstcreateindex) && (dbVendor != EDbVendor.dbvcouchbase)) 2679 ) && (!TBaseType.c_createTableStrictParsing) 2680 ) { 2681 // only parse main body of create table, 2682 TCustomSqlStatement errorSqlStatement = (TCustomSqlStatement) sqlstatements.get(i); 2683 2684 int nested = 0; 2685 boolean isIgnore = false, isFoundIgnoreToken = false; 2686 TSourceToken firstIgnoreToken = null; 2687 for (int k = 0; k < errorSqlStatement.sourcetokenlist.size(); k++) { 2688 TSourceToken st = errorSqlStatement.sourcetokenlist.get(k); 2689 if (isIgnore) { 2690 if (st.issolidtoken() && (st.tokencode != ';')) { 2691 isFoundIgnoreToken = true; 2692 if (firstIgnoreToken == null) { 2693 firstIgnoreToken = st; 2694 } 2695 } 2696 if (st.tokencode != ';') { 2697 st.tokencode = TBaseType.sqlpluscmd; 2698 } 2699 continue; 2700 } 2701 if (st.tokencode == (int) ')') { 2702 nested--; 2703 if (nested == 0) { 2704 //let's check is next token is 2705 // as ( select 2706 boolean isSelect = false; 2707 TSourceToken st1 = st.searchToken(TBaseType.rrw_as, 1); 2708 if (st1 != null) { 2709 TSourceToken st2 = st.searchToken((int) '(', 2); 2710 if (st2 != null) { 2711 TSourceToken st3 = st.searchToken(TBaseType.rrw_select, 3); 2712 isSelect = (st3 != null); 2713 } 2714 } 2715 if (!isSelect) isIgnore = true; 2716 } 2717 } 2718 if ((st.tokencode == (int) '(') || (st.tokencode == TBaseType.left_parenthesis_2)) { 2719 nested++; 2720 } 2721 } 2722 2723 if ((dbVendor == EDbVendor.dbvoracle) && ((firstIgnoreToken != null) && (!TBaseType.searchOracleTablePros(firstIgnoreToken.toString())))) { 2724 // if it is not the valid Oracle table properties option, let raise the error. 2725 isFoundIgnoreToken = false; 2726 } 2727 if (isFoundIgnoreToken) { 2728 errorSqlStatement.clearError(); 2729 j = sqlstatements.get(i).parsestatement(null, false); 2730 } 2731 } 2732 2733 if (((sqlstatements.get(i).sqlstatementtype == ESqlStatementType.sstcreatetrigger) 2734 || (sqlstatements.get(i).sqlstatementtype == ESqlStatementType.sstcreatefunction) 2735 || (sqlstatements.get(i).sqlstatementtype == sstcreateprocedure) 2736 ) && (dbVendor == EDbVendor.dbvdb2)) { 2737 // db2 can handle Oracle pl/sql code, so we give it a try here 2738 TCustomSqlStatement stmt = sqlstatements.get(i); 2739 StringBuffer stmtStr = new StringBuffer(1024); 2740 for (int k = 0; k < stmt.sourcetokenlist.size(); k++) { 2741 stmtStr.append(stmt.sourcetokenlist.get(k).getAstext()); 2742 } 2743 2744 TGSqlParser lc_sqlparser = new TGSqlParser(EDbVendor.dbvoracle); 2745 lc_sqlparser.sqltext = stmtStr.toString(); 2746 int iRet = lc_sqlparser.parse(); 2747 if (iRet == 0) { 2748 sqlstatements.remove(i); 2749 sqlstatements.add(i, lc_sqlparser.sqlstatements.get(0)); 2750 continue; 2751 } 2752 } 2753 } 2754 2755 if ((j != 0) || (sqlstatements.get(i).getErrorCount() > 0)) { 2756 copyerrormsg(sqlstatements.get(i)); 2757 2758 if ((isEnablePartialParsing()) && (dbVendor == EDbVendor.dbvsybase) && (sqlstatements.get(i).sqlstatementtype == ESqlStatementType.sstmssqlcreateprocedure)) { 2759 TMssqlCreateProcedure createProcedure = (TMssqlCreateProcedure) sqlstatements.get(i); 2760 2761 StringBuffer storedProcedure = new StringBuffer(1024); 2762 boolean ASKeyword = false; 2763 for (int k = 0; k < createProcedure.sourcetokenlist.size(); k++) { 2764 if ((!ASKeyword) && (createProcedure.sourcetokenlist.get(k).tokencode == TBaseType.rrw_as)) { 2765 ASKeyword = true; 2766 continue; 2767 } 2768 if (ASKeyword) { 2769 storedProcedure.append(createProcedure.sourcetokenlist.get(k).getAstext()); 2770 } 2771 } 2772 2773 TGSqlParser lc_sqlparser = new TGSqlParser(dbVendor); 2774 lc_sqlparser.sqltext = storedProcedure.toString(); 2775 lc_sqlparser.parse(); 2776 for (int k = 0; k < lc_sqlparser.sqlstatements.size(); k++) { 2777 createProcedure.getBodyStatements().add(lc_sqlparser.sqlstatements.get(k)); 2778 } 2779 } 2780 } 2781 2782 // fire SQL Statement handle event if set, if return true, stop parsing 2783 if (sqlStatementHandle != null) { 2784 if (sqlStatementHandle.processSQLStatement(sqlstatements.get(i), this)) break; 2785 } 2786 } 2787 2788 if (enableTimeLogging) { 2789 endTime = System.currentTimeMillis(); 2790 parsingTime += (endTime - startTime); 2791 } 2792 2793 if (isPushGloablStack) { 2794 firstFrame.popMeFromStack(getFrameStack()); 2795 } 2796 2797 // 3. start semantic analysis 2798 startTime = enableTimeLogging ? System.currentTimeMillis() : 0; 2799 2800 // Reset resolver2 for each parse 2801 this.resolver2 = null; 2802 2803 // Determine effective resolver type (instance setting takes precedence over TBaseType) 2804 EResolverType effectiveResolverType = this.resolverType; 2805 if (effectiveResolverType == EResolverType.DEFAULT) { 2806 // Fall back to TBaseType settings for backward compatibility 2807 if (TBaseType.isEnableResolver2()) { 2808 effectiveResolverType = EResolverType.RESOLVER2; 2809 } else if (TBaseType.isEnableResolver()) { 2810 effectiveResolverType = EResolverType.RESOLVER; 2811 } else { 2812 effectiveResolverType = EResolverType.NONE; 2813 } 2814 } 2815 2816 // Run the appropriate resolver based on effective type 2817 if (getErrorCount() == 0) { 2818 switch (effectiveResolverType) { 2819 case RESOLVER: 2820 TSQLResolver resolver = new TSQLResolver(globalContext, sqlstatements); 2821 if (!resolver.resolve()) { 2822 // Handle resolution errors 2823 // addErrors(resolver.getLog().getErrors()); 2824 } 2825 break; 2826 2827 case RESOLVER2: 2828 // Use provided config or create default 2829 TSQLResolverConfig config = this.resolver2Config; 2830 if (config == null) { 2831 config = new TSQLResolverConfig(); 2832 } 2833 // Always set vendor on config (needed for vendor-specific resolution like struct-field fallback) 2834 config.setVendor(dbVendor); 2835 // Pass null as globalContext to match original TSQLResolver2 behavior 2836 this.resolver2 = new TSQLResolver2(null, sqlstatements, config); 2837 // Pass sqlEnv to resolver2 for metadata lookup 2838 if (this.sqlEnv != null) { 2839 this.resolver2.setSqlEnv(this.sqlEnv); 2840 } 2841 if (!this.resolver2.resolve()) { 2842 // Handle resolution errors if needed 2843 } 2844 break; 2845 2846 case NONE: 2847 default: 2848 // No resolution 2849 break; 2850 } 2851 } 2852 2853 if (enableTimeLogging) { 2854 endTime = System.currentTimeMillis(); 2855 semanticAnalysisTime += (endTime - startTime); 2856 } 2857 2858 // 4. start interpreter 2859 startTime = enableTimeLogging ? System.currentTimeMillis() : 0; 2860 2861 if ((TBaseType.ENABLE_INTERPRETER) && (getErrorCount() == 0)) { 2862 TLog.clearLogs(); 2863 TGlobalScope globalScope = new TGlobalScope(sqlEnv); 2864 TLog.enableInterpreterLogOnly(); 2865 2866 TASTEvaluator astEvaluator = new TASTEvaluator(this.sqlstatements, globalScope); 2867 astEvaluator.eval(); 2868 } 2869 2870 if (enableTimeLogging) { 2871 endTime = System.currentTimeMillis(); 2872 interpreterTime += (endTime - startTime); 2873 } 2874 2875 return getErrorCount(); 2876 } 2877 2878 2879void copyerrormsg(TCustomSqlStatement sql){ 2880 for (int i = 0; i<sql.getSyntaxErrors().size(); i++){ 2881 this.syntaxErrors.add(new TSyntaxError( (TSyntaxError)sql.getSyntaxErrors().get(i) )); 2882 } 2883// for (int i = 0; i<sql.getSyntaxHints().size(); i++){ 2884// this.syntaxHints.add(new TSyntaxError( (TSyntaxError)sql.getSyntaxHints().get(i) )); 2885// } 2886} 2887 2888private static String calculateLicenseKey(boolean ignoreMachineId){ 2889 2890 if (userName == null) return null; 2891 if (machineId == null) return null; 2892 2893 byte[] bytesOfMessage=null; 2894 String licenseStr = "I love sql pretty printer, yeah!"+userName.toLowerCase(); 2895 if (!ignoreMachineId){ 2896 licenseStr += machineId.toLowerCase(); 2897 } 2898 try { 2899 bytesOfMessage = licenseStr.getBytes("UTF-8"); 2900 } catch (UnsupportedEncodingException e) { 2901 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 2902 } 2903 2904 MessageDigest md = null; 2905 try { 2906 md = MessageDigest.getInstance("MD5"); 2907 } catch (NoSuchAlgorithmException e) { 2908 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 2909 } 2910 byte[] digest = md.digest(bytesOfMessage); 2911 2912 return null;//HardwareBinder.getHex(digest); 2913} 2914 2915private static boolean validateLicense(){ 2916 2917 int ret = 0; 2918 return ret == 0; 2919 2920} 2921 2922private static boolean check_license_time(){ 2923 2924 boolean ret = false; 2925 2926 String toDate = TBaseType.license_expired_date; 2927 2928 DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT); 2929 Calendar currDtCal = Calendar.getInstance(); 2930 2931 // Zero out the hour, minute, second, and millisecond 2932 currDtCal.set(Calendar.HOUR_OF_DAY, 0); 2933 currDtCal.set(Calendar.MINUTE, 0); 2934 currDtCal.set(Calendar.SECOND, 0); 2935 currDtCal.set(Calendar.MILLISECOND, 0); 2936 2937 Date currDt = currDtCal.getTime(); 2938 2939 Date toDt; 2940 try { 2941 toDt = df.parse(toDate); 2942 } catch (ParseException e) { 2943 toDt = null; 2944 // Print some error message back to the user 2945 } 2946 2947 if (toDt != null){ 2948 int results = toDt.compareTo(currDt); 2949 ret = results > 0; 2950 } 2951 2952 return ret; 2953} 2954 2955 public void setTeradataUtilityType(TeradataUtilityType teradataUtilityType) { 2956 if ((this.getFlexer() != null) && (this.getFlexer() instanceof TLexerTeradata)){ 2957 ((TLexerTeradata)this.getFlexer()).setTeradataUtilityType(teradataUtilityType); 2958 } 2959 } 2960 2961 /** 2962 * Dispose of parser resources and clean up references. 2963 * 2964 * <p>This method releases internal resources used by the parser. 2965 * After calling this method, the parser should not be used further.</p> 2966 * 2967 * <p>If a ManagedSourceBuffer was set, the source text remains in the buffer 2968 * so that tokens can still access it. The user is responsible for calling 2969 * {@link ManagedSourceBuffer#release()} when all tokens are no longer needed.</p> 2970 * 2971 * <p>Note: Tokens created by this parser will remain usable after dispose() 2972 * if a ManagedSourceBuffer was used, as they reference the buffer by ID 2973 * rather than holding direct parser references.</p> 2974 * 2975 * @since 3.1.0.9 2976 */ 2977 public void dispose() { 2978 // Note: We intentionally do NOT remove the source from managed buffer 2979 // This allows tokens to remain usable after parser disposal 2980 // The user must call buffer.release() when done with all tokens 2981 2982 // Clear references to help GC 2983 flexer = null; 2984 fparser = null; 2985 fplsqlparser = null; 2986 finputstream = null; 2987 sqlInputStream = null; 2988 gcurrentsqlstatement = null; 2989 nextStmt = null; 2990 sqlstatements = null; 2991 sourcetokenlist = null; 2992 syntaxErrors = null; 2993 } 2994 2995}