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 private ISQLStatementHandle sqlStatementHandle = null; 920 921 public void setSqlStatementHandle(ISQLStatementHandle sqlStatementHandle) { 922 this.sqlStatementHandle = sqlStatementHandle; 923 } 924 925 private ITokenHandle tokenHandle = null; 926 927 /** 928 * Set an event handler which will be fired when a new source token is created by the lexer during tokenize the 929 * input SQL. 930 * 931 * @param tokenHandle the event handler to process the new created source token 932 */ 933 public void setTokenHandle(ITokenHandle tokenHandle) { 934 this.tokenHandle = tokenHandle; 935 } 936 937 private ITokenListHandle tokenListHandle = null; 938 939 public void setTokenListHandle(ITokenListHandle tokenListHandle) { 940 this.tokenListHandle = tokenListHandle; 941 } 942 943 private IMetaDatabase metaDatabase = null; 944 945 /** 946 * @deprecated As of v2.0.3.1, please use {@link #getSqlEnv()} instead 947 * 948 * set an instance of a class which implement the interface: {@link IMetaDatabase}. 949 * The parser will call {@link IMetaDatabase#checkColumn} method when it needs to know 950 * whether a column is belonged to a table. 951 * <p></p> 952 * The class that implements the interface: {@link IMetaDatabase} usually fetch the metadata from the database 953 * by connecting to a database instance. 954 * <p></p> 955 * If the class is not provided, the parser has to guess the relationship between a un-qualified column and table 956 * in the input SQL which may lead to a un-determined result between the column and table. 957 * 958 * @param metaDatabase a new instance of the class which implements the {@link IMetaDatabase} interface 959 * @see IMetaDatabase 960 */ 961 public void setMetaDatabase(IMetaDatabase metaDatabase) { 962 this.metaDatabase = metaDatabase; 963 } 964 965 /** 966 * @deprecated As of v2.0.3.1, please use {@link #getSqlEnv()} instead 967 * 968 * a new instance of the class which implements the {@link IMetaDatabase} interface 969 * 970 * @return a new instance of the class which implements the {@link IMetaDatabase} interface 971 * @see #setMetaDatabase 972 */ 973 public IMetaDatabase getMetaDatabase() { 974 975 return metaDatabase; 976 } 977 978 /** 979 * Not used. 980 * 981 */ 982 public void freeParseTable() { 983 flexer.yystack = null; 984 flexer.yytextbuf = null; 985 flexer.buf = null; 986 987// TLexerOracle.yyk = null; 988// TLexerOracle.yykl = null; 989// TLexerOracle.yykh = null; 990// TLexerOracle.yym = null; 991// TLexerOracle.yyml = null; 992// TLexerOracle.yymh = null; 993// TLexerOracle.yyt = null; 994// TLexerOracle.yytl = null; 995// TLexerOracle.yyth = null; 996// TParserOracleSql.yyah = null; 997// TParserOracleSql.yyal = null; 998// TParserOracleSql.yygh = null; 999// TParserOracleSql.yygl = null; 1000// TParserOracleSql.yyd = null; 1001// TParserOracleSql.yya_sym= null; 1002// TParserOracleSql.yya_act= null; 1003// TParserOracleSql.yyr_len= null; 1004// TParserOracleSql.yyr_sym= null; 1005// TParserOracleSql.yyg_sym= null; 1006// TParserOracleSql.yyg_act= null; 1007// 1008// TParserOraclePLSql.yyah = null; 1009// TParserOraclePLSql.yyal = null; 1010// TParserOraclePLSql.yygh = null; 1011// TParserOraclePLSql.yygl = null; 1012// TParserOraclePLSql.yyd = null; 1013// TParserOraclePLSql.yya_sym= null; 1014// TParserOraclePLSql.yya_act= null; 1015// TParserOraclePLSql.yyr_len= null; 1016// TParserOraclePLSql.yyr_sym= null; 1017// TParserOraclePLSql.yyg_sym= null; 1018// TParserOraclePLSql.yyg_act= null; 1019// 1020// TLexerMssql.yyk = null; 1021// TLexerMssql.yykl = null; 1022// TLexerMssql.yykh = null; 1023// TLexerMssql.yym = null; 1024// TLexerMssql.yyml = null; 1025// TLexerMssql.yymh = null; 1026// TLexerMssql.yyt = null; 1027// TLexerMssql.yytl = null; 1028// TLexerMssql.yyth = null; 1029// TParserMssqlSql.yyah = null; 1030// TParserMssqlSql.yyal = null; 1031// TParserMssqlSql.yygh = null; 1032// TParserMssqlSql.yygl = null; 1033// TParserMssqlSql.yyd = null; 1034// TParserMssqlSql.yya_sym= null; 1035// TParserMssqlSql.yya_act= null; 1036// TParserMssqlSql.yyr_len= null; 1037// TParserMssqlSql.yyr_sym= null; 1038// TParserMssqlSql.yyg_sym= null; 1039// TParserMssqlSql.yyg_act= null; 1040// 1041// TLexerMysql.yyk = null; 1042// TLexerMysql.yykl = null; 1043// TLexerMysql.yykh = null; 1044// TLexerMysql.yym = null; 1045// TLexerMysql.yyml = null; 1046// TLexerMysql.yymh = null; 1047// TLexerMysql.yyt = null; 1048// TLexerMysql.yytl = null; 1049// TLexerMysql.yyth = null; 1050// TParserMysqlSql.yyah = null; 1051// TParserMysqlSql.yyal = null; 1052// TParserMysqlSql.yygh = null; 1053// TParserMysqlSql.yygl = null; 1054// TParserMysqlSql.yyd = null; 1055// TParserMysqlSql.yya_sym= null; 1056// TParserMysqlSql.yya_act= null; 1057// TParserMysqlSql.yyr_len= null; 1058// TParserMysqlSql.yyr_sym= null; 1059// TParserMysqlSql.yyg_sym= null; 1060// TParserMysqlSql.yyg_act= null; 1061 } 1062 1063 /** 1064 * Class constructor, create a new instance of the parser by setting the database vendor 1065 * 1066 * @param pdbvendor the database vendor whose grammar rule will be used to validate the syntax of the input SQL 1067 */ 1068 public TGSqlParser(EDbVendor pdbvendor) { 1069 dbVendor = pdbvendor; 1070 sqltext = ""; 1071 sqlfilename = ""; 1072 1073 delimiterchar = ';'; 1074 defaultDelimiterStr = ";"; 1075 1076 // Most vendors are now delegated to vendor-specific parsers via getOrCreateVendorParser(). 1077 // This constructor only handles delimiter settings for vendors with non-default delimiters. 1078 // Lexer/parser initialization is handled by getOrCreateVendorParser() for all vendors. 1079 switch(pdbvendor){ 1080 case dbvazuresql: 1081 dbVendor = EDbVendor.dbvmssql; 1082 break; 1083 case dbvoracle: 1084 case dbvteradata: 1085 case dbvpostgresql: 1086 case dbvduckdb: 1087 case dbvredshift: 1088 case dbvgreenplum: 1089 delimiterchar = '/'; 1090 break; 1091 case dbvdameng: 1092 if (TBaseType.enterprise_edition || (!TBaseType.full_edition) || TBaseType.dameng_edition) { 1093 delimiterchar = '/'; 1094 } 1095 break; 1096 case dbvdb2: 1097 delimiterchar = '@'; 1098 break; 1099 case dbvmysql: 1100 delimiterchar = '$'; 1101 defaultDelimiterStr = "$"; 1102 break; 1103 case dbvoceanbase: 1104 // Effective delimiter is mode-dependent and resolved inside 1105 // OceanBaseSqlParser based on EOBTenantMode: ';' for MYSQL/SYSTEM, 1106 // '/' for ORACLE PL/SQL blocks. The '$' default mirrors MySQL's 1107 // DELIMITER override behavior so multi-statement scripts split 1108 // correctly under MYSQL mode (the default). 1109 delimiterchar = '$'; 1110 defaultDelimiterStr = "$"; 1111 break; 1112 case dbvdoris: 1113 if (TBaseType.enterprise_edition || (!TBaseType.full_edition) || TBaseType.doris_edition) { 1114 delimiterchar = ';'; 1115 } 1116 break; 1117 case dbvstarrocks: 1118 if (TBaseType.enterprise_edition || (!TBaseType.full_edition) || TBaseType.starrocks_edition) { 1119 delimiterchar = ';'; 1120 } 1121 break; 1122 default: 1123 // All other vendors use default delimiter (;) 1124 break; 1125 } 1126 1127 // fparser is null here - it will be set later in doDelegatedRawParse() from vendor parser result 1128 // All vendors are now delegated to vendor-specific parsers via getOrCreateVendorParser() 1129 1130 sourcetokenlist = new TSourceTokenList(); 1131 sourcetokenlist.setGsqlparser(this); 1132 sqlstatements = new TStatementList(); 1133 // Inject vendor-specific command resolver 1134 sqlcmds = SqlCmdsFactory.get(dbVendor); 1135 sqlpluskeywordList = new HashMap(); 1136 syntaxErrors = new ArrayList(); 1137 1138 errormessage = ""; 1139 1140 if (TBaseType.license_expired_check){ 1141 if (!check_license_time()) { 1142 flexer = null; 1143 fparser = null; 1144 } 1145 } 1146 1147 TGSqlParser.setCurrentDBVendor(dbVendor); 1148 } 1149 1150 TContext globalContext; 1151 1152 1153 /** 1154 * Create a select statement object from the parameter: subquery 1155 * 1156 * @param subquery a plain text select statement which need to be converted to a {@link gudusoft.gsqlparser.stmt.TSelectSqlStatement} object 1157 * @return a select statement object, return null if the input select string is syntax invalid. 1158 */ 1159 public TSelectSqlStatement parseSubquery(String subquery){ 1160 return parseSubquery(this.dbVendor,subquery); 1161 } 1162 1163 /** 1164 * this method is thread safe. 1165 * 1166 * C:\prg\gsp_java\gsp_java_core\src\test\java\gudusoft\gsqlparser\ParseSubqueryThreadSafetyTest.java 1167 * 1168 */ 1169 public static TSelectSqlStatement parseSubquery(EDbVendor dbVendor, String subquery){ 1170// TGSqlParser localParser = new TGSqlParser(dbVendor); 1171// localParser.sqltext = subquery; 1172// int iRet = localParser.doparse(); 1173// if (iRet != 0) { 1174// return null; 1175// } 1176 1177 TSingletonParser singletonParser = TSingletonParser.getInstance(); 1178 TStatementList statements = singletonParser.getStmts(dbVendor,subquery); 1179 if (statements.size() == 0) return null; 1180 1181 return (TSelectSqlStatement)statements.get(0); 1182 } 1183 1184 /** 1185 * Create an expression object from the parameter: expr 1186 * 1187 * @param expr a plain text expression which will be converted to {@link gudusoft.gsqlparser.nodes.TExpression} object 1188 * @return an expression object, return null if the input expression string is not syntax valid. 1189 */ 1190 public TExpression parseExpression(String expr){ 1191 return parseExpression(this.dbVendor,expr); 1192 } 1193 1194 /* 1195 * this method is thread safe. 1196 * 1197 * getStmts() is synchronized - only one thread can execute it at a time 1198 * getParser() is also synchronized 1199 * Each thread gets a new TStatementList instance 1200 1201 * this is the test case for this method. 1202 * C:\prg\gsp_java\gsp_java_core\src\test\java\gudusoft\gsqlparser\ParseExpressionThreadSafetyTest.java 1203 */ 1204 public static TExpression parseExpression(EDbVendor dbVendor, String expr){ 1205 TSingletonParser singletonParser = TSingletonParser.getInstance(); 1206 1207 boolean e = TBaseType.isEnableResolver(); 1208 TBaseType.setEnableResolver(false); 1209 TStatementList statements; 1210 try{ 1211 statements = singletonParser.getStmts(dbVendor,"select 1 from t where "+TBaseType.newline+expr); 1212 }finally { 1213 TBaseType.setEnableResolver(e); 1214 } 1215 1216 if (statements.size() == 0) return null; 1217 1218// TGSqlParser localParser = new TGSqlParser(dbVendor); 1219// localParser.sqltext = "select 1 from t where "+TBaseType.newline+expr; 1220// int iRet = localParser.doparse(); 1221// if (iRet != 0) { 1222// return null; 1223// } 1224 1225 return ((TSelectSqlStatement)statements.get(0)).getWhereClause().getCondition(); 1226 } 1227 1228 /** 1229 * Create a function object from the parameter: newFunction 1230 * 1231 * @param newFunction a plain text function which will be converted to {@link gudusoft.gsqlparser.nodes.TFunctionCall} object 1232 * @return a function object, or return null if the input string is not a valid function call. 1233 */ 1234 public TFunctionCall parseFunctionCall(String newFunction){ 1235 return parseFunctionCall(this.dbVendor,newFunction); 1236 } 1237 1238 public static TFunctionCall parseFunctionCall(EDbVendor dbVendor, String newFunction){ 1239 TSingletonParser singletonParser = TSingletonParser.getInstance(); 1240 TStatementList statements = singletonParser.getStmts(dbVendor,"select"+TBaseType.newline+newFunction+TBaseType.newline+"from t"); 1241 if (statements.size() == 0) return null; 1242 1243 1244// TGSqlParser localParser = new TGSqlParser(dbVendor); 1245// localParser.sqltext = "select"+TBaseType.newline+newFunction+TBaseType.newline+"from t"; 1246// int iRet = localParser.doparse(); 1247// if (iRet!= 0) { 1248// return null; 1249// } 1250 1251 return statements.get(0).getResultColumnList().getResultColumn(0).getExpr().getFunctionCall(); 1252 } 1253 1254 /** 1255 * Create a database objectName from the parameter: newObjectName 1256 * 1257 * @param newObjectName a plain text objectName which will be converted to {@link gudusoft.gsqlparser.nodes.TObjectName} object 1258 * @return a database objectName object, return null is the input string is not a valid objectName. 1259 */ 1260 public TObjectName parseObjectName(String newObjectName){ 1261 //return parseObjectName(this.dbVendor,newObjectName); 1262 return TObjectName.createObjectName(this.dbVendor,EDbObjectType.column,newObjectName); 1263 } 1264 1265 1266 1267 /** 1268 * 1269 * @param dbVendor 1270 * @param newObjectName 1271 * @return 1272 */ 1273 public static TObjectName parseObjectName(EDbVendor dbVendor, String newObjectName){ 1274 1275 TSingletonParser singletonParser = TSingletonParser.getInstance(); 1276 TStatementList statements = singletonParser.getStmts(dbVendor,"select"+TBaseType.newline+newObjectName+TBaseType.newline+"from t"); 1277 if (statements.size() == 0) return null; 1278 TExpression e = statements.get(0).getResultColumnList().getResultColumn(0).getExpr(); 1279 1280// TGSqlParser localParser = new TGSqlParser(dbVendor); 1281// localParser.sqltext = "select"+TBaseType.newline+newObjectName+TBaseType.newline+"from t"; 1282// int iRet = localParser.doparse(); 1283// if (iRet!= 0) { 1284// return null; 1285// } 1286// TExpression e = ((TSelectSqlStatement)localParser.sqlstatements.get(0)).getResultColumnList().getResultColumn(0).getExpr(); 1287 1288 TObjectName lcResult = null; 1289 switch (e.getExpressionType()){ 1290 case simple_object_name_t: 1291 lcResult = e.getObjectOperand(); 1292 break; 1293 case simple_constant_t: 1294 lcResult = new TObjectName(); 1295 lcResult.init(e.getConstantOperand().getValueToken()); 1296 break; 1297 default: 1298 break; 1299 } 1300 1301 return lcResult; 1302 } 1303 1304 /** 1305 * Create an constant object from the parameter: newConstant 1306 * 1307 * @param newConstant a plian text constant which will be converted to {@link gudusoft.gsqlparser.nodes.TConstant} object 1308 * @return new constant object, returns null if the input is not a valid constant string. 1309 */ 1310 public TConstant parseConstant(String newConstant){ 1311 return parseConstant(this.dbVendor,newConstant); 1312 } 1313 1314 public static TConstant parseConstant(EDbVendor dbVendor, String newConstant){ 1315// TGSqlParser localParser = new TGSqlParser(dbVendor); 1316// localParser.sqltext = "select"+TBaseType.newline+newConstant+TBaseType.newline+"from t"; 1317// int iRet = localParser.doparse(); 1318// if (iRet!= 0) { 1319// return null; 1320// } 1321 1322 TSingletonParser singletonParser = TSingletonParser.getInstance(); 1323 TStatementList statements = singletonParser.getStmts(dbVendor,"select"+TBaseType.newline+newConstant+TBaseType.newline+"from t"); 1324 if (statements.size() == 0) return null; 1325 1326 return statements.get(0).getResultColumnList().getResultColumn(0).getExpr().getConstantOperand(); 1327 } 1328 1329 1330 /** 1331 * The total number of syntax errors founded in the input SQL script. 1332 * <br>Only the first syntax error in a SQL statement is counted if there are more than one syntax errors in 1333 * a single SQL statement. 1334 * <br> In a SQL script, only the first syntax error in each SQL statement is counted. 1335 * 1336 * @return The total number of syntax errors founded in the input SQL script. Returns 0 if no syntax error is founded. 1337 */ 1338 public int getErrorCount(){ 1339 return syntaxErrors.size(); 1340 } 1341 1342 /** 1343 * The text of error message generated by iterating all items in {@link #getSyntaxErrors}. 1344 * User may generate error message in their own format by iterating all items in {@link #getSyntaxErrors}. 1345 * 1346 * @return error message 1347 */ 1348 public String getErrormessage(){ 1349 1350 String s="",hint="Syntax error"; 1351 TSyntaxError t; 1352 for (int i= 0; i< syntaxErrors.size(); i++) 1353 { 1354 t = (TSyntaxError) syntaxErrors.get(i); 1355 if (t.hint.length() > 0) hint = t.hint; 1356 s= s+hint+"("+t.errorno+") near: "+t.tokentext; 1357 s=s+"("+t.lineNo; 1358 s=s+","+t.columnNo +", token code:"+t.tokencode+")"; 1359 //s=s+" expected tokentext:"+t.hint; 1360 1361 // break;//get only one message, remove this one and uncomment next line to get all error messages 1362 if (i != syntaxErrors.size() - 1) 1363 s = s +TBaseType.linebreak; 1364 } 1365 1366 if (errormessage.length() > 0){ 1367 s = errormessage+TBaseType.linebreak+s; 1368 } 1369 return s; 1370 1371 } 1372 1373 /** 1374 * check syntax of the input SQL. This method works exactly the same as {@link #parse} method. 1375 * 1376 * @return 0 means parse SQL script successfully, otherwise, use {@link #getErrorCount}, {@link #getErrormessage} 1377 * to get detailed error information. 1378 * @see #parse 1379 */ 1380 public int checkSyntax(){ 1381 return doparse(); 1382 } 1383 1384 /** 1385 * Check syntax of the input SQL, doing some kind of semantic analysis without connecting to a real database. 1386 * <p></p> 1387 * This method will do a in-depth analysis of the input SQL such as building the link between table and columns. 1388 * The parse tree of the input SQL is available after calling this method. 1389 * 1390 * The parser checks the syntax of those SQL statements one by one. If syntax error is found in a SQL statement, 1391 * an error will be logged, no parse tree will be built for this SQL statement, 1392 * the error message can be fetched using the {@link #getErrormessage()} method. 1393 * <p></p> 1394 * The syntax error in one SQL statement doesn't prevent the parser continue to check the syntax of the next SQL statement. 1395 * After checking syntax of all SQL statements, use the {@link #getErrorCount()} method to get the total number of errors. 1396 * <p></p> 1397 * A syntax error in a SQL stored procedure will cease this parser to check syntax of the rest SQL statements 1398 * in this stored procedure. 1399 * 1400 * @return 0 means parse SQL script successfully, otherwise, use {@link #getErrorCount}, {@link #getErrormessage} 1401 * to get detailed error information. 1402 * @see #getSyntaxErrors 1403 */ 1404 1405 public int parse(){ 1406 return doparse(); 1407 } 1408 1409 public int validate(){ 1410 if (sqlstatements.size() == 0) return 0; 1411 1412 TRelationValidator relationValidate = new TRelationValidator(globalContext); 1413 for(TCustomSqlStatement sqlStatement:sqlstatements){ 1414 sqlStatement.acceptChildren(relationValidate); 1415 } 1416 return 0; 1417 } 1418 1419 void setdelimiterchar(char ch){ 1420 delimiterchar = ch; 1421 flexer.delimiterchar = ch; 1422 } 1423 char curdelimiterchar; 1424 String userDelimiterStr =""; 1425 1426 boolean includesqlstatementtype(ESqlStatementType search, ESqlStatementType[] src){ 1427 boolean ret = false; 1428 for(int i=0;i<src.length;i++){ 1429 if (src[i] == search){ 1430 ret = true; 1431 break; 1432 } 1433 } 1434 return ret; 1435 } 1436 1437 // private int getfileEncodingType(FileInputStream inputStream){ 1438 // BufferedInputStream fr = new BufferedInputStream(inputStream,8); 1439 // return getfileEncodingType(fr); 1440 // } 1441 1442 private int getfileEncodingType(BufferedInputStream fr){ 1443 int ret = 0; // default, 1: utf-16, 2: utf-32 1444 // BufferedInputStream fr = new BufferedInputStream(inputStream,8); 1445 try { 1446 byte[] bom = new byte[4]; 1447 fr.mark(bom.length+1); 1448 1449 fr.read(bom,0,bom.length); 1450 if ( ((bom[0] == (byte)0xFF) && (bom[1] == (byte)0xFE)) 1451 ||((bom[0] == (byte)0xFE) && (bom[1] == (byte)0xFF)) 1452 ) 1453 { 1454 ret = 1; 1455 if ( ((bom[2] == (byte)0xFF) && (bom[3] == (byte)0xFE)) 1456 ||((bom[2] == (byte)0xFE) && (bom[3] == (byte)0xFF)) 1457 ){ 1458 ret = 2; 1459 } 1460 }else{ 1461 if ((bom[0] == (byte)0xEF) && (bom[1] == (byte)0xBB)&& (bom[2] == (byte)0xBF)){ 1462 ret = 3; //UTF-8,EF BB BF 1463 } 1464 } 1465 fr.reset(); 1466 } catch (FileNotFoundException e) { 1467 // e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1468 } catch (IOException e) { 1469 //e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1470 } 1471 return ret; 1472 } 1473 1474 // private int getfileEncodingType(String fn){ 1475 // int ret = 0; // default, 1: utf-16, 2: utf-32 1476 // try { 1477 // FileInputStream fr = new FileInputStream(fn); 1478 // ret = getfileEncodingType(fr); 1479 // fr.close(); 1480 1481 // } catch (FileNotFoundException e) { 1482 // // e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1483 // } catch (IOException e) { 1484 // //e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1485 // } 1486 // return ret; 1487 // } 1488 1489 int readsql(){ 1490 int ret = 0; 1491 1492 syntaxErrors.clear(); 1493 //syntaxHints.clear(); 1494 1495 if (((!TBaseType.full_edition))||(!TBaseType.need_license_file)){ 1496 if (!TBaseType.need_license_file){ 1497 licenseType = TBaseType.license_type_dist; 1498 userName = "dist"; 1499 }else { 1500 licenseType = TBaseType.license_type_trial; 1501 userName = TBaseType.license_trail_username; 1502 } 1503 // machineId = new HardwareBinder().getMachineIdString(); 1504 }else { 1505 if (!licenseOK){ 1506 errormessage = licenseMessage; 1507 return -1; 1508 } 1509 } 1510 1511 try{ 1512 if (finputstream != null) finputstream.close(); 1513 if (flexer == null){ 1514 ret = -1; 1515 errormessage = "requested database not supported:"+this.dbVendor.toString(); 1516 return ret; 1517 } 1518 if (flexer.yyinput != null) flexer.yyinput.close(); 1519 1520 }catch(IOException e){ 1521 ret = -1; 1522 errormessage = "requested database not supported"; 1523 } 1524 1525 if (sqltext.length() > 0){ 1526 finputstream = new BufferedReader(new StringReader(sqltext), TBaseType.LEXER_INPUT_BUFFER_SIZE); 1527 if ((!TBaseType.full_edition) && (sqltext.length() > TBaseType.query_size_limitation)){ 1528 errormessage = TBaseType.trail_version_query_message; 1529 ret = -1; 1530 } 1531 }else if (sqlfilename.length() > 0){ 1532 try{ 1533 1534 streamFromSqlFile = new FileInputStream(sqlfilename); 1535 // Buffer it for mark/reset support 1536 BufferedInputStream bufferedStream = new BufferedInputStream(streamFromSqlFile,8); 1537 int encodingtype = getfileEncodingType(bufferedStream); 1538 streamFromSqlFile.getChannel().position(0); 1539 1540 if(encodingtype == 1){ 1541 sqlStreamReader = new InputStreamReader(streamFromSqlFile,"UTF-16"); 1542 }else if(encodingtype == 2){ 1543 sqlStreamReader = new InputStreamReader(streamFromSqlFile,"UTF-32"); 1544 }else if(encodingtype == 3){ 1545 // System.out.println("utf-8"); 1546 sqlStreamReader = new InputStreamReader(streamFromSqlFile,"UTF-8"); 1547 } 1548 else{ 1549 if (sqlCharset == null){ 1550 sqlCharset = Charset.defaultCharset().name(); 1551 //System.out.println("Charset used: "+Charset.defaultCharset().name()); 1552 } 1553 sqlStreamReader = new InputStreamReader(streamFromSqlFile, sqlCharset); 1554 // isr = new InputStreamReader(fr,"Cp737"); 1555 } 1556 1557 finputstream = new BufferedReader(sqlStreamReader, TBaseType.LEXER_INPUT_BUFFER_SIZE); 1558 if (encodingtype == 3){ 1559 //EF BB BF was not stripped by the InputStreamReader, so we do it 1560 finputstream.skip(1); 1561 } 1562 1563 if ((!TBaseType.full_edition)){ 1564 File file = new File(sqlfilename); 1565 if (!file.exists() || !file.isFile()) { 1566 ret = -1; 1567 errormessage = "not a valid sql file."; 1568 }else{ 1569 if (file.length() > TBaseType.query_size_limitation){ 1570 errormessage = TBaseType.trail_version_file_message; 1571 ret = -1; 1572 } 1573 } 1574 } 1575 1576 }catch(FileNotFoundException e){ 1577 ret = -1; 1578 errormessage = e.toString(); 1579 } catch (UnsupportedEncodingException e) { 1580 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1581 } catch (IOException e) { 1582 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1583 } 1584 1585 }else if(this.sqlInputStream != null){ 1586 int encodingtype = getfileEncodingType(sqlInputStream); 1587 InputStream fr = sqlInputStream; 1588 1589 InputStreamReader isr = null; 1590 try{ 1591 1592 if(encodingtype == 1){ 1593 isr = new InputStreamReader(fr,"UTF-16"); 1594 }else if(encodingtype == 2){ 1595 isr = new InputStreamReader(fr,"UTF-32"); 1596 }else if(encodingtype == 3){ 1597 // System.out.println("utf-8"); 1598 isr = new InputStreamReader(fr,"UTF-8"); 1599 } 1600 else{ 1601 if (sqlCharset == null){ 1602 sqlCharset = Charset.defaultCharset().name(); 1603 } 1604 1605 isr = new InputStreamReader(fr, sqlCharset); 1606 } 1607 1608 finputstream = new BufferedReader(isr, TBaseType.LEXER_INPUT_BUFFER_SIZE); 1609 if (encodingtype == 3){ 1610 //EF BB BF was not stripped by the InputStreamReader, so we do it 1611 finputstream.skip(1); 1612 } 1613 1614 }catch(FileNotFoundException e){ 1615 ret = -1; 1616 errormessage = e.toString(); 1617 } catch (UnsupportedEncodingException e) { 1618 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1619 } catch (IOException e) { 1620 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1621 } 1622 1623 try { 1624 if ((!TBaseType.full_edition) && (sqlInputStream.available() > TBaseType.query_size_limitation)){ 1625 errormessage = TBaseType.trail_version_query_message; 1626 ret = -1; 1627 } 1628 } catch (IOException e) { 1629 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 1630 } 1631 } 1632 1633 if (finputstream == null) ret = -1; 1634 1635 if (ret == 0) 1636 { 1637 flexer.yyinput = finputstream; 1638 flexer.setSqlCharset(this.sqlCharset); 1639 flexer.reset(); 1640 } 1641 1642 sourcetokenlist.clear(); 1643 sourcetokenlist.curpos = -1; 1644 1645 flexer.resetTokenTable(); 1646 1647 1648 return ret; 1649 } 1650 1651 TSourceToken getanewsourcetoken(){ 1652 TSourceToken pst = null,prevst; 1653 1654 while (true) { 1655 pst = new TSourceToken(""); 1656 if (flexer.yylexwrap(pst) == 0) { pst = null; break;} 1657 1658 pst.setDbvendor(dbVendor); 1659 pst.tokenstatus = ETokenStatus.tsoriginal; 1660 if (pst.tokentype == ETokenType.ttreturn){ 1661 pst.setAstext(towinlinebreak(pst.getAstext())); 1662 } 1663 //combine space && linebreak after a linebreak into one 1664 if ( (pst.tokentype == ETokenType.ttwhitespace) 1665 && (sourcetokenlist.curpos >= 0) ) 1666 { 1667 prevst = sourcetokenlist.get(sourcetokenlist.curpos); 1668 if ( prevst.tokentype == ETokenType.ttreturn ) 1669 { 1670 //can't discard whitespace after linebreak, it will be used 1671 // to judge whether / at the { of the line is a sqlplus cmd or not 1672 // check isvalidplacefordivtosqlpluscmd for more 1673 prevst.setAstext(prevst.getAstext() + pst.getAstext()); 1674 //newst.free; 1675 //newst = nil; 1676 continue; 1677 } 1678 } 1679 1680 if ( (pst.tokentype == ETokenType.ttreturn) 1681 && (sourcetokenlist.curpos >= 0) ) 1682 { 1683 prevst = sourcetokenlist.get(sourcetokenlist.curpos); 1684 1685 if ( prevst.tokentype == ETokenType.ttreturn ) 1686 { 1687 prevst.setAstext(prevst.getAstext() + pst.getAstext()); 1688 //newst.free; 1689 //newst = nil; 1690 continue; 1691 } 1692 1693 if ( prevst.tokentype == ETokenType.ttwhitespace ) 1694 { 1695 //merge previous whitespace with this linebreak 1696 //doesn't work for sqlplus cmd 1697 1698 // prevst.sourcecode = newst.sourcecode; 1699 // prevst.tokentype = newst.tokentype; 1700 // prevst.tokencode = newst.tokencode; 1701 // newst.free; 1702 // newst = nil; 1703 // continue; 1704 1705 //prevst.prevsourcecode = prevst.astext; 1706 //prevst.astext = ""; 1707 } 1708 } 1709 1710 break; 1711 1712 } 1713 1714 if (pst != null){ 1715 pst.container = sourcetokenlist; 1716 sourcetokenlist.curpos = sourcetokenlist.curpos+1; 1717 pst.posinlist = sourcetokenlist.curpos; 1718 if (tokenHandle != null){ 1719 tokenHandle.processToken(pst); 1720 } 1721 } 1722 1723 // System.out.println(pst); 1724 // flexer.setTokenTableValue(pst); 1725 return pst; 1726 1727 } 1728 1729 String towinlinebreak(String s){ 1730 return s; 1731// todo not implemented yet 1732 } 1733 1734void checkconstarinttoken(TSourceToken lcprevtoken){ 1735 TSourceTokenList lcStList = lcprevtoken.container; 1736 if (TBaseType.assigned(lcStList)) 1737 { 1738 TSourceToken lcPPToken = lcStList.nextsolidtoken(lcprevtoken.posinlist,-2,false); 1739 if (TBaseType.assigned(lcPPToken)) 1740 { 1741 1742 if (lcPPToken.tokencode == flexer.getkeywordvalue("constraint")) 1743 { 1744 //System.out.println(lcPPToken); 1745 lcPPToken.tokencode = TBaseType.rw_constraint2; 1746 } 1747 } 1748 } 1749 1750} 1751 1752TSourceToken getprevtoken(TSourceToken ptoken){ 1753 // ETokenType[] lcnonsolidtokenset = {ETokenType.ttwhitespace,ETokenType.ttreturn,ETokenType.ttsimplecomment,ETokenType.ttbracketedcomment} ; 1754 TSourceTokenList lcstlist = ptoken.container; 1755 if (TBaseType.assigned(lcstlist)){ 1756 if ((ptoken.posinlist > 0) && (lcstlist.size() > ptoken.posinlist-1)) 1757 { 1758 if (!( 1759 (lcstlist.get(ptoken.posinlist-1).tokentype == ETokenType.ttwhitespace) 1760 ||(lcstlist.get(ptoken.posinlist-1).tokentype == ETokenType.ttreturn) 1761 ||(lcstlist.get(ptoken.posinlist-1).tokentype == ETokenType.ttsimplecomment) 1762 ||(lcstlist.get(ptoken.posinlist-1).tokentype == ETokenType.ttbracketedcomment) 1763 ) 1764 ) 1765 {return lcstlist.get(ptoken.posinlist-1);} 1766 else{ 1767 { return lcstlist.nextsolidtoken(ptoken.posinlist-1,-1,false);} 1768 } 1769 } 1770 } 1771 1772 return null; 1773} 1774 1775void docommonsqltexttotokenlist(){ 1776 1777 TSourceToken asourcetoken,lcprevst; 1778 int yychar; 1779 1780 asourcetoken = getanewsourcetoken(); 1781 if ( asourcetoken == null ) return; 1782 yychar = asourcetoken.tokencode; 1783 1784 while (yychar > 0) 1785 { 1786 sourcetokenlist.add(asourcetoken); 1787 asourcetoken = getanewsourcetoken(); 1788 if ( asourcetoken == null ) break; 1789 yychar = asourcetoken.tokencode; 1790 } 1791 1792} 1793void dosqltexttotokenlist(){ 1794 // Legacy path: Only called for non-delegated vendors (e.g., dbvfirebird, dbvexasol) 1795 // All delegated vendors (mssql, access, generic, mysql, oracle, etc.) use getOrCreateVendorParser() 1796 docommonsqltexttotokenlist(); 1797 1798 doAfterTokenize(); 1799 1800 TBaseType.resetTokenChain(sourcetokenlist,0); 1801 1802 processTokensInTokenTable(dbVendor); 1803 processTokensBeforeParse(dbVendor); 1804 1805 closeFileStream(); 1806 1807 if (tokenListHandle != null){ 1808 tokenListHandle.processTokenList(sourcetokenlist); 1809 } 1810} 1811 1812void doAfterTokenize(){ 1813 1814 int leftParenCount = 0; 1815 int rightParenCount = 0; 1816 int leftIndex = 0; 1817 int rightIndex = sourcetokenlist.size() - 1; 1818 1819 // Count opening parentheses at the beginning 1820 while (leftIndex < sourcetokenlist.size() && sourcetokenlist.get(leftIndex).tokencode == '(') { 1821 leftParenCount++; 1822 leftIndex++; 1823 } 1824 1825 // Count closing parentheses at the end 1826 while (rightIndex >= 0 && sourcetokenlist.get(rightIndex).tokencode == ')') { 1827 rightParenCount++; 1828 rightIndex--; 1829 } 1830 1831 // Set matching parentheses to be ignored 1832 int parensToIgnore = Math.min(leftParenCount, rightParenCount); 1833 // if there is a semicolon before the right parenthesis, set the semicolon to be ignored 1834 // mantisbt/view.php?id=3690 1835 1836 if ((parensToIgnore > 0) && (sourcetokenlist.get(sourcetokenlist.size() - 1 - (parensToIgnore - 1) - 1).tokencode == ';')){ 1837 // set to whitespace that this semicolon will be ignored during getting raw sql 1838 sourcetokenlist.get(sourcetokenlist.size() - 1 - (parensToIgnore - 1) - 1).tokentype = ETokenType.ttwhitespace; 1839 // set to ignore by yacc that this semicolon will be ignored during parsing 1840 sourcetokenlist.get(sourcetokenlist.size() - 1 - (parensToIgnore - 1) - 1).tokenstatus = ETokenStatus.tsignorebyyacc; 1841 } 1842// for (int i = 0; i < parensToIgnore; i++) { 1843// //sourcetokenlist.get(i).tokenstatus = ETokenStatus.tsignorebyyacc; 1844// sourcetokenlist.get(i).tokencode = TBaseType.lexspace; 1845// 1846// // sourcetokenlist.get(sourcetokenlist.size() - 1 - i).tokenstatus = ETokenStatus.tsignorebyyacc; 1847// sourcetokenlist.get(sourcetokenlist.size() - 1 - i).tokencode = TBaseType.lexspace; 1848// } 1849 1850} 1851 1852 1853 1854void processTokensBeforeParse(EDbVendor dbVendor){ 1855 1856 // 为确保性能,只在snowflake数据库中处理,因为目前只有snowflake数据库中有连续的分号有用户提出这个需求,其他数据库中暂时不处理 1857 if (dbVendor != EDbVendor.dbvsnowflake) return; 1858 1859 // mantisbt/view.php?id=3579 1860 // if there are consecutive semicolon tokens, mark the second semi colon token as deleted token 1861 for(int i=0;i<sourcetokenlist.size();i++){ 1862 TSourceToken st = sourcetokenlist.get(i); 1863 if (st.tokencode == ';'){ 1864 TSourceToken nextToken = st.nextSolidToken(); 1865 if (nextToken != null){ 1866 if (nextToken.tokencode == ';'){ 1867 nextToken.tokenstatus = ETokenStatus.tsdeleted; 1868 } 1869 } 1870 } 1871 } 1872 1873} 1874 1875void processTokensInTokenTable(EDbVendor dbVendor){ 1876 // 获得所有token后,根据需要对token code进行预处理, token table是 TBaseType.TOKEN_TABLE 1877 long[][] TOKEN_TABLE1 = flexer.TOKEN_TABLE; 1878 1879 switch (dbVendor){ 1880 case dbvbigquery: 1881 case dbvsnowflake: 1882 // case 1, DO 关键字如果没有发现对于的 FOR, WHILE 等关键字,把 DO 关键字的token code设置为 TBaseType.ident 1883 1884 if (TOKEN_TABLE1[TBaseType.rrw_do][0] > 0){ 1885 if ((TOKEN_TABLE1[TBaseType.rrw_while][0] == 0)&&(TOKEN_TABLE1[TBaseType.rrw_for][0] == 0)){ 1886 for(int i=0;i<sourcetokenlist.size();i++){ 1887 TSourceToken st = sourcetokenlist.get(i); 1888 if (st.tokencode == TBaseType.rrw_do){ 1889 st.tokencode = TBaseType.ident; 1890 } 1891 } 1892 } 1893 } 1894 1895 break; 1896 } 1897 1898} 1899 1900boolean isDollarFunctionDelimiter(int tokencode, EDbVendor dbVendor){ 1901 return ((tokencode == TBaseType.rrw_postgresql_function_delimiter)&&(dbVendor == EDbVendor.dbvpostgresql)) 1902 ||((tokencode == TBaseType.rrw_greenplum_function_delimiter)&&(dbVendor == EDbVendor.dbvgreenplum)) 1903 ||((tokencode == TBaseType.rrw_redshift_function_delimiter)&&(dbVendor == EDbVendor.dbvredshift)) 1904 ||((tokencode == TBaseType.rrw_snowflake_function_delimiter)&&(dbVendor == EDbVendor.dbvsnowflake)); 1905} 1906 1907/** 1908 * Returns the 1-based line number of the end of the last SQL statement that 1909 * was successfully recognized during raw-statement separation (e.g. via 1910 * vendor-specific {@code do*getrawsqlstatements} routines). 1911 * <p> 1912 * The value corresponds to the ending line of the last validated statement in 1913 * the most recent parse operation. Any trailing, incomplete statement at the 1914 * end of the input is intentionally excluded and will not affect this value. 1915 * <p> 1916 * Notes: 1917 * <ul> 1918 * <li>The line number is relative to the current input provided to the 1919 * parser (not an absolute position in an external, larger file).</li> 1920 * <li>This is useful when splitting huge SQL files by safe statement 1921 * boundaries — callers can cut the source at this line without risking a 1922 * partial statement.</li> 1923 * </ul> 1924 * 1925 * @return the 1-based line number of the last validated statement's ending 1926 * line, or {@code -1} if no statement has been validated yet 1927 */ 1928public int getLastLineNoOfLastStatementBeenValidated(){ 1929 if (lastTokenOfStatementBeenValidated != null){ 1930 return (int)lastTokenOfStatementBeenValidated.lineNo; 1931 } 1932 return -1; 1933} 1934 1935private TSourceToken lastTokenOfStatementBeenValidated; 1936 1937 1938void doongetrawsqlstatementevent(TCustomSqlStatement pcsqlstatement){ 1939 doongetrawsqlstatementevent(pcsqlstatement,false); 1940} 1941 1942void doongetrawsqlstatementevent(TCustomSqlStatement pcsqlstatement, boolean isLastSQL){ 1943 pcsqlstatement.setGsqlparser(this); 1944 pcsqlstatement.parser = this.fparser; 1945 pcsqlstatement.plsqlparser = this.fplsqlparser; 1946 pcsqlstatement.setStartToken(pcsqlstatement.sourcetokenlist.get(0)); 1947 pcsqlstatement.setEndToken(pcsqlstatement.sourcetokenlist.get(pcsqlstatement.sourcetokenlist.size()-1)); 1948 sqlstatements.add(pcsqlstatement); 1949 1950 if (!isLastSQL){ // 最后一个语句没有经过验证,只是在结束的时候强行加入的,很有可能是语法不正确的,因此我们这里只记录非最后的语句 1951 lastTokenOfStatementBeenValidated = pcsqlstatement.getEndToken(); 1952 } 1953 1954 // if stored procedure body is not written in sql or plsql, then, set the token in body to 1955 if ( ((this.dbVendor == EDbVendor.dbvpostgresql)||(this.dbVendor == EDbVendor.dbvgreenplum) ||(this.dbVendor == EDbVendor.dbvredshift) ||(this.dbVendor == EDbVendor.dbvsnowflake) ) 1956 && (pcsqlstatement instanceof TRoutine) 1957 ){ 1958 if (!((TRoutine)pcsqlstatement).isBodyInSQL()){ 1959 TSourceToken st; 1960 boolean inBody = false; 1961 StringBuilder routineBodyBuilder = new StringBuilder(); 1962 for(int i=0;i<pcsqlstatement.sourcetokenlist.size();i++){ 1963 st = pcsqlstatement.sourcetokenlist.get(i); 1964 if (isDollarFunctionDelimiter(st.tokencode,this.dbVendor) 1965// ((st.tokencode == TBaseType.rrw_postgresql_function_delimiter)&&(this.dbVendor == EDbVendor.dbvpostgresql)) 1966// ||((st.tokencode == TBaseType.rrw_greenplum_function_delimiter)&&(this.dbVendor == EDbVendor.dbvgreenplum)) 1967// ||((st.tokencode == TBaseType.rrw_redshift_function_delimiter)&&(this.dbVendor == EDbVendor.dbvredshift)) 1968// ||((st.tokencode == TBaseType.rrw_snowflake_function_delimiter)&&(this.dbVendor == EDbVendor.dbvsnowflake)) 1969 ){ 1970 if (!inBody){ 1971 inBody = true; 1972 routineBodyBuilder.setLength(0); 1973 routineBodyBuilder.append(st.toString()); 1974 }else{ 1975 inBody = false; 1976 routineBodyBuilder.append(st.toString()); 1977 break; 1978 } 1979 continue; 1980 } 1981 1982 if (inBody){ 1983 st.tokencode = TBaseType.sqlpluscmd; 1984 routineBodyBuilder.append(st.toString()); 1985 } 1986 } 1987 1988 ((TRoutine)pcsqlstatement).setRoutineBody(routineBodyBuilder.toString()); 1989 } 1990 } 1991} 1992 1993 boolean checkTokenPairWithEnd(int tokencode){ 1994 return ((tokencode == TBaseType.rrw_if)||(tokencode == TBaseType.rrw_case) 1995 ||(tokencode == TBaseType.rrw_loop)||(tokencode == TBaseType.rrw_repeat) 1996 ||(tokencode == TBaseType.rrw_while)||(tokencode == TBaseType.rrw_for) 1997 ||(tokencode == TBaseType.rrw_case) 1998 ); 1999 } 2000 2001 private TCustomSqlStatement startDaxStmt(TSourceToken currToken,TCustomSqlStatement currStmt){ 2002 TCustomSqlStatement newStmt = null; 2003 if (currToken == null) return null; 2004 if ((currToken.tokencode == '=')&&(currToken.isFirstTokenOfLine())){ 2005 currToken.tokencode = TBaseType.equal_start_expr; 2006 newStmt = new TDaxExprStmt(EDbVendor.dbvdax); 2007 }else if ((currToken.tokencode == TBaseType.rrw_dax_define)&&(currToken.isFirstTokenOfLine())){ 2008 newStmt = new TDaxEvaluateStmt(EDbVendor.dbvdax); 2009 ((TDaxEvaluateStmt)newStmt).setStartWithDefine(true); 2010 }else if ((currToken.tokencode == TBaseType.rrw_dax_evaluate)&&(currToken.isFirstTokenOfLine())){ 2011 if ((currStmt != null)&&(currStmt instanceof TDaxEvaluateStmt)){ 2012 TDaxEvaluateStmt tmp = (TDaxEvaluateStmt)currStmt; 2013 if (tmp.isStartWithDefine()) return null; 2014 } 2015 newStmt = new TDaxEvaluateStmt(EDbVendor.dbvdax); 2016 } 2017 2018 if (newStmt == null){ 2019 // let's check is this the first token of query 2020 boolean isFirst = currToken.isFirstTokenOfLine(); 2021 TSourceToken prevToken = currToken.prevSolidToken(); 2022 if ((isFirst)&&(prevToken == null)){ 2023 newStmt = new TDaxExprStmt(EDbVendor.dbvdax); 2024 } 2025 } 2026 return newStmt; 2027 } 2028 2029int dogetrawsqlstatements(){ 2030 // This method should not be called - all vendors are now delegated to vendor-specific parsers. 2031 // If this is reached, it means a new vendor was added but not included in getOrCreateVendorParser(). 2032 throw new IllegalStateException( 2033 "dogetrawsqlstatements() called for vendor " + dbVendor + 2034 ". All vendors should be delegated via getOrCreateVendorParser(). " + 2035 "Please add this vendor to the switch statement in getOrCreateVendorParser()."); 2036} 2037 2038 /** 2039 * separates the SQL statements in the input SQL script without doing syntax check. 2040 * <p></p> 2041 * Use the {@link #getSqlstatements()} method to get the list of SQL statements. 2042 * The SQL statement object is the instance of the sub-class of {@link TCustomSqlStatement}, get SQL statement type 2043 * via the {@link TCustomSqlStatement#sqlstatementtype} field, get string representation of 2044 * each SQL statement via the {@link TCustomSqlStatement#toString} method. 2045 * <p></p> 2046 * All source tokens in this SQL statement 2047 * is available by using {@link TCustomSqlStatement#sourcetokenlist} filed. 2048 * Since no parse tree is built by calling this method, no further detailed information about the SQL statement is available. 2049 * 2050 * @return 0 if get SQL statements successfully 2051 */ 2052public int getrawsqlstatements(){ 2053 // Clear errors from any previous call on this instance 2054 syntaxErrors.clear(); 2055 2056 // Check for vendor parser delegation (MSSQL, etc.) 2057 // This ensures raw-split logic is maintained in only one place (vendor parser) 2058 SqlParser vp = getOrCreateVendorParser(); 2059 if (vp != null) { 2060 return doDelegatedRawParse(vp); 2061 } 2062 2063 // Legacy path for non-delegated vendors 2064 int ret = readsql(); 2065 if (ret != 0) return ret; 2066 dosqltexttotokenlist(); 2067 2068 return dogetrawsqlstatements(); 2069} 2070 2071 /** 2072 * turns the input SQL into a sequence of token which is the 2073 * basic lexis element of SQL syntax. Token is categorized as keyword, identifier, 2074 * number, operator, whitespace and other types. All source tokens can be fetched 2075 * via the {@link #getSourcetokenlist()} method. 2076 * 2077 */ 2078public void tokenizeSqltext(){ 2079 // Check for vendor parser delegation (MSSQL, etc.) 2080 // This ensures tokenization logic is maintained in only one place (vendor parser) 2081 SqlParser vp = getOrCreateVendorParser(); 2082 if (vp != null) { 2083 doDelegatedTokenize(vp); 2084 return; 2085 } 2086 2087 // Legacy path for non-delegated vendors 2088 getFlexer(); 2089 readsql(); 2090 dosqltexttotokenlist(); 2091} 2092 2093/** 2094 * Delegate tokenization to vendor parser and backfill results. 2095 * Similar to doDelegatedRawParse but only performs tokenization. 2096 */ 2097private void doDelegatedTokenize(SqlParser vendorParser) { 2098 // Build context for vendor parser 2099 ParserContext context = buildContext(); 2100 2101 // Delegate tokenization to vendor parser 2102 SqlParseResult tokenResult = vendorParser.tokenize(context); 2103 2104 // Copy results back to TGSqlParser fields for backward compatibility 2105 if (tokenResult.getSourceTokenList() != null) { 2106 this.sourcetokenlist = tokenResult.getSourceTokenList(); 2107 } 2108 if (tokenResult.getLexer() != null) { 2109 this.flexer = tokenResult.getLexer(); 2110 } 2111} 2112 2113 2114 2115void findAllSyntaxErrorsInPlsql(TCustomSqlStatement psql){ 2116 if (psql.getErrorCount() > 0){ 2117 copyerrormsg(psql); 2118 } 2119 2120 for (int k=0;k<psql.getStatements().size();k++){ 2121 findAllSyntaxErrorsInPlsql(psql.getStatements().get(k)); 2122 } 2123 2124} 2125 2126private TSQLEnv sqlEnv = null; 2127 2128 /** 2129 * SQL environment includes the database metadata such as procedure, function, trigger, table and etc. 2130 * 2131 * In order to link column to table correctly without connecting to database, 2132 * we need to provide a class which implements {@link TSQLEnv} to TGSqlParser. 2133 * this class tells TGSqlParser the relationship between column and table. 2134 * 2135 * <p>Take this SQL for example: 2136 * <pre> 2137 * SELECT Quantity,b.Time,c.Description 2138 * FROM 2139 * (SELECT ID2,Time FROM bTab) b 2140 * INNER JOIN aTab a on a.ID=b.ID 2141 * INNER JOIN cTab c on a.ID=c.ID 2142 * </pre> 2143 * 2144 * <p>General SQL Parser can build relationship between column: ID2 and table: bTable 2145 * correctly without metadata information from database because there is only one table 2146 * in from clause. But it can't judge column: Quantity belong to table: aTab or cTab, 2147 * since no table alias was prefixed to column: Quantity. If no metadata provided, 2148 * General SQL Parser will link column: Quantity to the first valid table (here it is aTab) 2149 * 2150 * <p>If we create a class TRealDatabaseSQLEnv implements {@link TSQLEnv},then 2151 * {@link #setSqlEnv(TSQLEnv)}, General SQL Parser can take this advantage to create 2152 * a correct relationship between column and tables. 2153 * 2154 *<pre> 2155 * class TSQLServerEnv extends TSQLEnv{ 2156 * 2157 * public TSQLServerEnv(){ 2158 * super(EDbVendor.dbvmssql); 2159 * initSQLEnv(); 2160 * } 2161 * 2162 * @Override 2163 * public void initSQLEnv() { 2164 * 2165 * // add a new database: master 2166 * TSQLCatalog sqlCatalog = createSQLCatalog("master"); 2167 * // add a new schema: dbo 2168 * TSQLSchema sqlSchema = sqlCatalog.createSchema("dbo"); 2169 * //add a new table: aTab 2170 * TSQLTable aTab = sqlSchema.createTable("aTab"); 2171 * aTab.addColumn("Quantity1"); 2172 * 2173 * //add a new table: bTab 2174 * TSQLTable bTab = sqlSchema.createTable("bTab"); 2175 * bTab.addColumn("Quantity2"); 2176 * 2177 * //add a new table: cTab 2178 * TSQLTable cTab = sqlSchema.createTable("cTab"); 2179 * cTab.addColumn("Quantity"); 2180 * 2181 * } 2182 * } 2183 * </pre> 2184 * 2185 * @return SQL environment 2186 */ 2187 public TSQLEnv getSqlEnv() { 2188 return sqlEnv; 2189 } 2190 2191 private boolean onlyNeedRawParseTree = false; 2192 2193 public void setOnlyNeedRawParseTree(boolean onlyNeedRawParseTree) { 2194 this.onlyNeedRawParseTree = onlyNeedRawParseTree; 2195 } 2196 2197 public void setSqlEnv(TSQLEnv sqlEnv) { 2198 this.sqlEnv = sqlEnv; 2199 } 2200 2201 2202 // Time tracking variables 2203 private boolean enableTimeLogging = false; 2204 private long rawSqlStatementsTime = 0; 2205 private long parsingTime = 0; 2206 private long semanticAnalysisTime = 0; 2207 private long interpreterTime = 0; 2208 2209 /** 2210 * Enable or disable time logging for parser steps 2211 * @param enable true to enable time logging, false to disable 2212 */ 2213 public void setEnableTimeLogging(boolean enable) { 2214 this.enableTimeLogging = enable; 2215 } 2216 2217 /** 2218 * Check if time logging is enabled 2219 * @return true if time logging is enabled, false otherwise 2220 */ 2221 public boolean isTimeLoggingEnabled() { 2222 return this.enableTimeLogging; 2223 } 2224 2225 /** 2226 * Reset all accumulated time counters to zero 2227 */ 2228 public void resetTimeCounters() { 2229 rawSqlStatementsTime = 0; 2230 parsingTime = 0; 2231 semanticAnalysisTime = 0; 2232 interpreterTime = 0; 2233 } 2234 2235 /** 2236 * Get accumulated time spent getting raw SQL statements in milliseconds 2237 * @return time in milliseconds 2238 */ 2239 public long getRawSqlStatementsTime() { 2240 return rawSqlStatementsTime; 2241 } 2242 2243 /** 2244 * Get accumulated time spent parsing in milliseconds 2245 * @return time in milliseconds 2246 */ 2247 public long getParsingTime() { 2248 return parsingTime; 2249 } 2250 2251 /** 2252 * Get accumulated time spent on semantic analysis in milliseconds 2253 * @return time in milliseconds 2254 */ 2255 public long getSemanticAnalysisTime() { 2256 return semanticAnalysisTime; 2257 } 2258 2259 /** 2260 * Get accumulated time spent in interpreter in milliseconds 2261 * @return time in milliseconds 2262 */ 2263 public long getInterpreterTime() { 2264 return interpreterTime; 2265 } 2266 2267 /** 2268 * Get total accumulated time spent in all steps 2269 * @return total time in milliseconds 2270 */ 2271 public long getTotalTime() { 2272 return rawSqlStatementsTime + parsingTime + semanticAnalysisTime + interpreterTime; 2273 } 2274 2275 /** 2276 * Get or create the vendor-specific parser for delegation. 2277 * The parser is cached in vendorParser field and reused for subsequent calls. 2278 * This allows getFlexer() to lazily create the parser to access its lexer. 2279 * 2280 * @return vendor-specific SqlParser or null if not a delegated vendor 2281 * @since 3.2.0.0 2282 */ 2283 private SqlParser getOrCreateVendorParser() { 2284 // Return cached parser if available 2285 if (vendorParser != null) { 2286 return vendorParser; 2287 } 2288 2289 // Create new vendor parser based on database vendor 2290 switch (dbVendor) { 2291 case dbvmssql: 2292 case dbvazuresql: 2293 case dbvaccess: // Access uses MSSQL lexer/parser 2294 case dbvgeneric: // Generic uses MSSQL lexer/parser 2295 case dbvfirebird: // Firebird uses MSSQL lexer/parser 2296 case dbvexasol: // Exasol uses MSSQL lexer/parser 2297 vendorParser = new gudusoft.gsqlparser.parser.MssqlSqlParser(); 2298 break; 2299 case dbvmysql: 2300 vendorParser = new gudusoft.gsqlparser.parser.MySqlSqlParser(); 2301 break; 2302 case dbvpostgresql: 2303 vendorParser = new gudusoft.gsqlparser.parser.PostgreSqlParser(); 2304 break; 2305 case dbvduckdb: 2306 vendorParser = new gudusoft.gsqlparser.parser.DuckdbSqlParser(); 2307 break; 2308 case dbvoracle: 2309 vendorParser = new gudusoft.gsqlparser.parser.OracleSqlParser(); 2310 break; 2311 case dbvbigquery: 2312 vendorParser = new gudusoft.gsqlparser.parser.BigQuerySqlParser(); 2313 break; 2314 case dbvathena: 2315 vendorParser = new gudusoft.gsqlparser.parser.AthenaSqlParser(); 2316 break; 2317 case dbvcouchbase: 2318 vendorParser = new gudusoft.gsqlparser.parser.CouchbaseSqlParser(); 2319 break; 2320 case dbvdatabricks: 2321 vendorParser = new gudusoft.gsqlparser.parser.DatabricksSqlParser(); 2322 break; 2323 case dbvdax: 2324 vendorParser = new gudusoft.gsqlparser.parser.DaxSqlParser(); 2325 break; 2326 case dbvpowerquery: 2327 vendorParser = new gudusoft.gsqlparser.parser.PowerQuerySqlParser(); 2328 break; 2329 case dbvdb2: 2330 vendorParser = new gudusoft.gsqlparser.parser.Db2SqlParser(); 2331 break; 2332 case dbvdoris: 2333 vendorParser = new gudusoft.gsqlparser.parser.DorisSqlParser(); 2334 break; 2335 case dbvstarrocks: 2336 vendorParser = new gudusoft.gsqlparser.parser.StarrocksSqlParser(); 2337 break; 2338 case dbvflink: 2339 vendorParser = new gudusoft.gsqlparser.parser.FlinkSqlParser(); 2340 break; 2341 case dbvgaussdb: 2342 vendorParser = new gudusoft.gsqlparser.parser.GaussDbSqlParser(); 2343 break; 2344 case dbvedb: 2345 vendorParser = new gudusoft.gsqlparser.parser.EdbSqlParser(); 2346 break; 2347 case dbvdameng: 2348 vendorParser = new gudusoft.gsqlparser.parser.DamengSqlParser(); 2349 break; 2350 case dbvoceanbase: 2351 vendorParser = new gudusoft.gsqlparser.parser.OceanBaseSqlParser(); 2352 break; 2353 case dbvgreenplum: 2354 vendorParser = new gudusoft.gsqlparser.parser.GreenplumSqlParser(); 2355 break; 2356 case dbvhive: 2357 vendorParser = new gudusoft.gsqlparser.parser.HiveSqlParser(); 2358 break; 2359 case dbvhana: 2360 vendorParser = new gudusoft.gsqlparser.parser.HanaSqlParser(); 2361 break; 2362 case dbvimpala: 2363 vendorParser = new gudusoft.gsqlparser.parser.ImpalaSqlParser(); 2364 break; 2365 case dbvinformix: 2366 vendorParser = new gudusoft.gsqlparser.parser.InformixSqlParser(); 2367 break; 2368 case dbvmdx: 2369 vendorParser = new gudusoft.gsqlparser.parser.MdxSqlParser(); 2370 break; 2371 case dbvnetezza: 2372 vendorParser = new gudusoft.gsqlparser.parser.NetezzaSqlParser(); 2373 break; 2374 case dbvodbc: 2375 vendorParser = new gudusoft.gsqlparser.parser.OdbcSqlParser(); 2376 break; 2377 case dbvopenedge: 2378 vendorParser = new gudusoft.gsqlparser.parser.OpenEdgeSqlParser(); 2379 break; 2380 case dbvpresto: 2381 vendorParser = new gudusoft.gsqlparser.parser.PrestoSqlParser(); 2382 break; 2383 case dbvredshift: 2384 vendorParser = new gudusoft.gsqlparser.parser.RedshiftSqlParser(); 2385 break; 2386 case dbvsnowflake: 2387 vendorParser = new gudusoft.gsqlparser.parser.SnowflakeSqlParser(); 2388 break; 2389 case dbvsqlite: 2390 vendorParser = new gudusoft.gsqlparser.parser.SqliteSqlParser(); 2391 break; 2392 case dbvclickhouse: 2393 vendorParser = new gudusoft.gsqlparser.parser.ClickhouseSqlParser(); 2394 break; 2395 case dbvsoql: 2396 vendorParser = new gudusoft.gsqlparser.parser.SoqlSqlParser(); 2397 break; 2398 case dbvsparksql: 2399 vendorParser = new gudusoft.gsqlparser.parser.SparksqlSqlParser(); 2400 break; 2401 case dbvsybase: 2402 vendorParser = new gudusoft.gsqlparser.parser.SybaseSqlParser(); 2403 break; 2404 case dbvteradata: 2405 vendorParser = new gudusoft.gsqlparser.parser.TeradataSqlParser(); 2406 break; 2407 case dbvtrino: 2408 vendorParser = new gudusoft.gsqlparser.parser.TrinoSqlParser(); 2409 break; 2410 case dbvvertica: 2411 vendorParser = new gudusoft.gsqlparser.parser.VerticaSqlParser(); 2412 break; 2413 case dbvansi: 2414 vendorParser = new gudusoft.gsqlparser.parser.AnsiSqlParser(); 2415 break; 2416 default: 2417 return null; 2418 } 2419 return vendorParser; 2420 } 2421 2422 /** 2423 * Prepare this parser for reuse by clearing cached state. 2424 * This is useful when reusing a TGSqlParser instance (e.g., in TExecImmeStmt) 2425 * to avoid state pollution between parsing operations. 2426 * 2427 * Clears: vendorParser, sqlEnv, sqlfilename, and resets parsing options. 2428 * 2429 * @since 3.2.0.0 2430 */ 2431 public void prepareForReuse() { 2432 // Clear cached vendor parser to force recreation 2433 this.vendorParser = null; 2434 // Clear SQL environment to avoid old schema information affecting new parse 2435 this.sqlEnv = null; 2436 // Clear filename in case it was set previously 2437 this.sqlfilename = null; 2438 // Reset parsing options to defaults 2439 this.isSinglePLBlock = false; 2440 // Clear statement list 2441 if (this.sqlstatements != null) { 2442 this.sqlstatements.clear(); 2443 } 2444 // Clear error state 2445 this.syntaxErrors.clear(); 2446 } 2447 2448 /** 2449 * Build a ParserContext from current TGSqlParser state. 2450 * This creates an immutable context for delegation to vendor parsers. 2451 * 2452 * @return immutable ParserContext 2453 * @since 3.2.0.0 2454 */ 2455 private ParserContext buildContext() { 2456 ParserContext.Builder builder = new ParserContext.Builder(this.dbVendor); 2457 2458 // Set SQL text source (only one will be non-null or non-empty) 2459 // CRITICAL: Check for non-null AND non-empty because TGSqlParser initializes sqltext="" 2460 // which would cause vendor parser to use empty string instead of reading from file 2461 if (this.sqltext != null && !this.sqltext.isEmpty()) { 2462 builder.sqlText(this.sqltext); 2463 } 2464 if (this.sqlfilename != null && !this.sqlfilename.isEmpty()) { 2465 builder.sqlFilename(this.sqlfilename); 2466 } 2467 2468 // Set charset 2469 if (this.sqlCharset != null) { 2470 builder.sqlCharset(this.sqlCharset); 2471 } 2472 2473 // Set parsing options 2474 builder.enablePartialParsing(this.isEnablePartialParsing()); 2475 builder.singlePLBlock(this.isSinglePLBlock); 2476 2477 // Mirror OceanBase tenant mode into the context so OceanBaseSqlParser 2478 // (and any other downstream component) can read it without a 2479 // back-reference to TGSqlParser. 2480 builder.oceanBaseTenantMode(this.oceanBaseTenantMode); 2481 2482 // Set gsqlparser reference for backward compatibility 2483 builder.gsqlparser(this); 2484 2485 // Set SQL environment if available 2486 if (this.sqlEnv != null) { 2487 builder.sqlEnv(this.sqlEnv); 2488 } 2489 2490 // Set token handle for callback during tokenization 2491 if (this.tokenHandle != null) { 2492 builder.tokenHandle(this.tokenHandle); 2493 } 2494 2495 return builder.build(); 2496 } 2497 2498 /** 2499 * Returns time statistics as a formatted string 2500 * @return formatted string with time statistics 2501 */ 2502 public String getTimeStatistics() { 2503 if (!enableTimeLogging) { 2504 return "Time logging is disabled"; 2505 } 2506 2507 StringBuilder sb = new StringBuilder(); 2508 sb.append(String.format("Time statistics for TGSqlParser version: %s, released at: %s, dbvendor: %s:\n", TBaseType.versionid,TBaseType.releaseDate, this.dbVendor)); 2509 sb.append(String.format("1. Raw SQL statements: %d ms (%.2f%%)\n", 2510 rawSqlStatementsTime, 2511 getTotalTime() > 0 ? 100.0 * rawSqlStatementsTime / getTotalTime() : 0)); 2512 sb.append(String.format("2. Parsing: %d ms (%.2f%%)\n", 2513 parsingTime, 2514 getTotalTime() > 0 ? 100.0 * parsingTime / getTotalTime() : 0)); 2515 sb.append(String.format("3. Semantic analysis: %d ms (%.2f%%)\n", 2516 semanticAnalysisTime, 2517 getTotalTime() > 0 ? 100.0 * semanticAnalysisTime / getTotalTime() : 0)); 2518 sb.append(String.format("4. Interpreter: %d ms (%.2f%%)\n", 2519 interpreterTime, 2520 getTotalTime() > 0 ? 100.0 * interpreterTime / getTotalTime() : 0)); 2521 sb.append(String.format("Total: %d ms", getTotalTime())); 2522 return sb.toString(); 2523 } 2524 2525 /** 2526 * Delegate only tokenization and raw statement extraction to vendor parser. 2527 * The actual AST parsing is done by the common parsing loop in doparse(). 2528 * This architecture ensures that: 2529 * 1. Vendor-specific tokenization/lexing is handled by vendor parser 2530 * 2. Raw statement extraction (splitting SQL text into statements) is vendor-specific 2531 * 3. AST parsing (building parse tree for each statement) uses common code path 2532 * 2533 * @param vendorParser the vendor-specific parser to use 2534 * @return 0 if successful, non-zero on error 2535 */ 2536 private int doDelegatedRawParse(SqlParser vendorParser) { 2537 long phaseStart, phaseEnd; 2538 2539 // Build context for vendor parser 2540 ParserContext context = buildContext(); 2541 2542 // Delegate only tokenization + raw statement extraction to vendor parser 2543 phaseStart = enableTimeLogging ? System.currentTimeMillis() : 0; 2544 SqlParseResult rawResult = vendorParser.getrawsqlstatements(context); 2545 if (enableTimeLogging) { 2546 phaseEnd = System.currentTimeMillis(); 2547 rawSqlStatementsTime += (phaseEnd - phaseStart); 2548 } 2549 2550 // Copy results back to TGSqlParser fields for backward compatibility 2551 if (rawResult.getSourceTokenList() != null) { 2552 this.sourcetokenlist = rawResult.getSourceTokenList(); 2553 this.sourcetokenlist.setGsqlparser(this); 2554 } 2555 if (rawResult.getLexer() != null) { 2556 this.flexer = rawResult.getLexer(); 2557 } 2558 // Copy parser from vendor parser result - needed for parsestatement() 2559 if (rawResult.getParser() != null) { 2560 this.fparser = rawResult.getParser(); 2561 // CRITICAL: Set gsqlparser on NodeFactory for correct AST construction 2562 this.fparser.getNf().setGsqlParser(this); 2563 } 2564 // Copy secondary parser (for Oracle PL/SQL) and set its NodeFactory 2565 if (rawResult.getSecondaryParser() != null) { 2566 this.fplsqlparser = rawResult.getSecondaryParser(); 2567 // CRITICAL: Also set gsqlparser on secondary parser's NodeFactory 2568 // This is needed for Oracle PL/SQL statements which use fplsqlparser for parsing 2569 this.fplsqlparser.getNf().setGsqlParser(this); 2570 } 2571 if (rawResult.getSqlStatements() != null) { 2572 this.sqlstatements = rawResult.getSqlStatements(); 2573 } 2574 2575 // Copy lastTokenOfStatementBeenValidated for getLastLineNoOfLastStatementBeenValidated() API 2576 this.lastTokenOfStatementBeenValidated = rawResult.getLastTokenOfStatementBeenValidated(); 2577 2578 // Copy any tokenization errors 2579 if (rawResult.getSyntaxErrors() != null) { 2580 for (TSyntaxError error : rawResult.getSyntaxErrors()) { 2581 this.syntaxErrors.add(error); 2582 } 2583 } 2584 2585 return rawResult.getErrorCode(); 2586 } 2587 2588 int doparse() { 2589 int j; 2590 long startTime, endTime; 2591 boolean useDelegatedRawParse = false; 2592 2593 // Clear errors from any previous parse() call on this instance 2594 syntaxErrors.clear(); 2595 2596 // 1. Get raw SQL statements 2597 // For delegated vendors, use vendor parser for tokenization + raw extraction 2598 // The parsing loop below is common for all vendors 2599 SqlParser vp = getOrCreateVendorParser(); 2600 if (vp != null) { 2601 int ret = doDelegatedRawParse(vp); 2602 if (ret != 0) { 2603 // Tokenization/extraction failed, return error 2604 return ret; 2605 } 2606 useDelegatedRawParse = true; 2607 // Continue to common parsing loop below 2608 } 2609 2610 // Legacy path: get raw statements for non-delegated vendors 2611 if (!useDelegatedRawParse) { 2612 startTime = enableTimeLogging ? System.currentTimeMillis() : 0; 2613 2614 int ret = getrawsqlstatements(); 2615 2616 if (enableTimeLogging) { 2617 endTime = System.currentTimeMillis(); 2618 rawSqlStatementsTime += (endTime - startTime); 2619 } 2620 } 2621 2622 boolean isPushGloablStack = false; 2623 //if (ret != 0) return ret; 2624 TFrame firstFrame = null; 2625 2626 globalContext = new TContext(); 2627 2628 if (this.sqlEnv == null) { 2629 this.sqlEnv = new TSQLEnv(this.dbVendor) { 2630 @Override 2631 public void initSQLEnv() { 2632 } 2633 }; 2634 // this.sqlEnv.setEnableGetMetadataFromDDL(false); 2635 } 2636 globalContext.setSqlEnv(this.sqlEnv, this.sqlstatements); 2637 2638 //TStackFrame stackFrame = new TStackFrame(new TGlobalScope()); 2639 if (getFrameStack().size() == 0) { // stack passed from outside gsqlparser will contain frames 2640 //getFrameStack().push(new TStackFrame(new TGlobalScope())); 2641 TGlobalScope globalScope = new TGlobalScope(); 2642 globalScope.resetCurrentStmtIndex(); 2643 2644 globalScope.setSqlEnv(this.sqlEnv); 2645 firstFrame = new TFrame(globalScope); 2646 firstFrame.pushMeToStack(getFrameStack()); 2647 isPushGloablStack = true; 2648 } 2649 2650 // 2. start parsing 2651 startTime = enableTimeLogging ? System.currentTimeMillis() : 0; 2652 2653 for (int i = 0; i < sqlstatements.size(); i++) { 2654 sqlstatements.getRawSql(i).setFrameStack(frameStack); 2655 j = sqlstatements.getRawSql(i).parsestatement(null, false, onlyNeedRawParseTree); 2656 2657 // NOTE: No need for setGsqlparserRecursively() here for delegated vendors. 2658 // The gsqlparser reference is already set on all AST nodes through two mechanisms: 2659 // 1. doDelegatedRawParse() sets fparser.getNf().setGsqlParser(this) and 2660 // fplsqlparser.getNf().setGsqlParser(this) BEFORE parsestatement() is called 2661 // 2. TNodeFactory.createNode() propagates gsqlparser to every node it creates 2662 // The recursive walk was causing 4-5x performance degradation for Oracle parsing 2663 // by redundantly traversing the entire AST after parsing. 2664 2665 TCustomSqlStatement sql0 = null; 2666 if (sqlstatements.get(i).isoracleplsql()) { 2667 // check syntax error in select/insert statement inside plsql 2668 sql0 = sqlstatements.get(i); 2669 findAllSyntaxErrorsInPlsql(sql0); 2670 } 2671 2672 boolean doRecover = TBaseType.ENABLE_ERROR_RECOVER_IN_CREATE_TABLE; 2673 2674 if (doRecover && ((j != 0) || (sqlstatements.get(i).getErrorCount() > 0))) { 2675 if (((sqlstatements.get(i).sqlstatementtype == ESqlStatementType.sstcreatetable) 2676 || ((sqlstatements.get(i).sqlstatementtype == ESqlStatementType.sstcreateindex) && (dbVendor != EDbVendor.dbvcouchbase)) 2677 ) && (!TBaseType.c_createTableStrictParsing) 2678 ) { 2679 // only parse main body of create table, 2680 TCustomSqlStatement errorSqlStatement = (TCustomSqlStatement) sqlstatements.get(i); 2681 2682 int nested = 0; 2683 boolean isIgnore = false, isFoundIgnoreToken = false; 2684 TSourceToken firstIgnoreToken = null; 2685 for (int k = 0; k < errorSqlStatement.sourcetokenlist.size(); k++) { 2686 TSourceToken st = errorSqlStatement.sourcetokenlist.get(k); 2687 if (isIgnore) { 2688 if (st.issolidtoken() && (st.tokencode != ';')) { 2689 isFoundIgnoreToken = true; 2690 if (firstIgnoreToken == null) { 2691 firstIgnoreToken = st; 2692 } 2693 } 2694 if (st.tokencode != ';') { 2695 st.tokencode = TBaseType.sqlpluscmd; 2696 } 2697 continue; 2698 } 2699 if (st.tokencode == (int) ')') { 2700 nested--; 2701 if (nested == 0) { 2702 //let's check is next token is 2703 // as ( select 2704 boolean isSelect = false; 2705 TSourceToken st1 = st.searchToken(TBaseType.rrw_as, 1); 2706 if (st1 != null) { 2707 TSourceToken st2 = st.searchToken((int) '(', 2); 2708 if (st2 != null) { 2709 TSourceToken st3 = st.searchToken(TBaseType.rrw_select, 3); 2710 isSelect = (st3 != null); 2711 } 2712 } 2713 if (!isSelect) isIgnore = true; 2714 } 2715 } 2716 if ((st.tokencode == (int) '(') || (st.tokencode == TBaseType.left_parenthesis_2)) { 2717 nested++; 2718 } 2719 } 2720 2721 if ((dbVendor == EDbVendor.dbvoracle) && ((firstIgnoreToken != null) && (!TBaseType.searchOracleTablePros(firstIgnoreToken.toString())))) { 2722 // if it is not the valid Oracle table properties option, let raise the error. 2723 isFoundIgnoreToken = false; 2724 } 2725 if (isFoundIgnoreToken) { 2726 errorSqlStatement.clearError(); 2727 j = sqlstatements.get(i).parsestatement(null, false); 2728 } 2729 } 2730 2731 if (((sqlstatements.get(i).sqlstatementtype == ESqlStatementType.sstcreatetrigger) 2732 || (sqlstatements.get(i).sqlstatementtype == ESqlStatementType.sstcreatefunction) 2733 || (sqlstatements.get(i).sqlstatementtype == sstcreateprocedure) 2734 ) && (dbVendor == EDbVendor.dbvdb2)) { 2735 // db2 can handle Oracle pl/sql code, so we give it a try here 2736 TCustomSqlStatement stmt = sqlstatements.get(i); 2737 StringBuffer stmtStr = new StringBuffer(1024); 2738 for (int k = 0; k < stmt.sourcetokenlist.size(); k++) { 2739 stmtStr.append(stmt.sourcetokenlist.get(k).getAstext()); 2740 } 2741 2742 TGSqlParser lc_sqlparser = new TGSqlParser(EDbVendor.dbvoracle); 2743 lc_sqlparser.sqltext = stmtStr.toString(); 2744 int iRet = lc_sqlparser.parse(); 2745 if (iRet == 0) { 2746 sqlstatements.remove(i); 2747 sqlstatements.add(i, lc_sqlparser.sqlstatements.get(0)); 2748 continue; 2749 } 2750 } 2751 } 2752 2753 if ((j != 0) || (sqlstatements.get(i).getErrorCount() > 0)) { 2754 copyerrormsg(sqlstatements.get(i)); 2755 2756 if ((isEnablePartialParsing()) && (dbVendor == EDbVendor.dbvsybase) && (sqlstatements.get(i).sqlstatementtype == ESqlStatementType.sstmssqlcreateprocedure)) { 2757 TMssqlCreateProcedure createProcedure = (TMssqlCreateProcedure) sqlstatements.get(i); 2758 2759 StringBuffer storedProcedure = new StringBuffer(1024); 2760 boolean ASKeyword = false; 2761 for (int k = 0; k < createProcedure.sourcetokenlist.size(); k++) { 2762 if ((!ASKeyword) && (createProcedure.sourcetokenlist.get(k).tokencode == TBaseType.rrw_as)) { 2763 ASKeyword = true; 2764 continue; 2765 } 2766 if (ASKeyword) { 2767 storedProcedure.append(createProcedure.sourcetokenlist.get(k).getAstext()); 2768 } 2769 } 2770 2771 TGSqlParser lc_sqlparser = new TGSqlParser(dbVendor); 2772 lc_sqlparser.sqltext = storedProcedure.toString(); 2773 lc_sqlparser.parse(); 2774 for (int k = 0; k < lc_sqlparser.sqlstatements.size(); k++) { 2775 createProcedure.getBodyStatements().add(lc_sqlparser.sqlstatements.get(k)); 2776 } 2777 } 2778 } 2779 2780 // fire SQL Statement handle event if set, if return true, stop parsing 2781 if (sqlStatementHandle != null) { 2782 if (sqlStatementHandle.processSQLStatement(sqlstatements.get(i), this)) break; 2783 } 2784 } 2785 2786 if (enableTimeLogging) { 2787 endTime = System.currentTimeMillis(); 2788 parsingTime += (endTime - startTime); 2789 } 2790 2791 if (isPushGloablStack) { 2792 firstFrame.popMeFromStack(getFrameStack()); 2793 } 2794 2795 // 3. start semantic analysis 2796 startTime = enableTimeLogging ? System.currentTimeMillis() : 0; 2797 2798 // Reset resolver2 for each parse 2799 this.resolver2 = null; 2800 2801 // Determine effective resolver type (instance setting takes precedence over TBaseType) 2802 EResolverType effectiveResolverType = this.resolverType; 2803 if (effectiveResolverType == EResolverType.DEFAULT) { 2804 // Fall back to TBaseType settings for backward compatibility 2805 if (TBaseType.isEnableResolver2()) { 2806 effectiveResolverType = EResolverType.RESOLVER2; 2807 } else if (TBaseType.isEnableResolver()) { 2808 effectiveResolverType = EResolverType.RESOLVER; 2809 } else { 2810 effectiveResolverType = EResolverType.NONE; 2811 } 2812 } 2813 2814 // Run the appropriate resolver based on effective type 2815 if (getErrorCount() == 0) { 2816 switch (effectiveResolverType) { 2817 case RESOLVER: 2818 TSQLResolver resolver = new TSQLResolver(globalContext, sqlstatements); 2819 if (!resolver.resolve()) { 2820 // Handle resolution errors 2821 // addErrors(resolver.getLog().getErrors()); 2822 } 2823 break; 2824 2825 case RESOLVER2: 2826 // Use provided config or create default 2827 TSQLResolverConfig config = this.resolver2Config; 2828 if (config == null) { 2829 config = new TSQLResolverConfig(); 2830 } 2831 // Always set vendor on config (needed for vendor-specific resolution like struct-field fallback) 2832 config.setVendor(dbVendor); 2833 // Pass null as globalContext to match original TSQLResolver2 behavior 2834 this.resolver2 = new TSQLResolver2(null, sqlstatements, config); 2835 // Pass sqlEnv to resolver2 for metadata lookup 2836 if (this.sqlEnv != null) { 2837 this.resolver2.setSqlEnv(this.sqlEnv); 2838 } 2839 if (!this.resolver2.resolve()) { 2840 // Handle resolution errors if needed 2841 } 2842 break; 2843 2844 case NONE: 2845 default: 2846 // No resolution 2847 break; 2848 } 2849 } 2850 2851 if (enableTimeLogging) { 2852 endTime = System.currentTimeMillis(); 2853 semanticAnalysisTime += (endTime - startTime); 2854 } 2855 2856 // 4. start interpreter 2857 startTime = enableTimeLogging ? System.currentTimeMillis() : 0; 2858 2859 if ((TBaseType.ENABLE_INTERPRETER) && (getErrorCount() == 0)) { 2860 TLog.clearLogs(); 2861 TGlobalScope globalScope = new TGlobalScope(sqlEnv); 2862 TLog.enableInterpreterLogOnly(); 2863 2864 TASTEvaluator astEvaluator = new TASTEvaluator(this.sqlstatements, globalScope); 2865 astEvaluator.eval(); 2866 } 2867 2868 if (enableTimeLogging) { 2869 endTime = System.currentTimeMillis(); 2870 interpreterTime += (endTime - startTime); 2871 } 2872 2873 return getErrorCount(); 2874 } 2875 2876 2877void copyerrormsg(TCustomSqlStatement sql){ 2878 for (int i = 0; i<sql.getSyntaxErrors().size(); i++){ 2879 this.syntaxErrors.add(new TSyntaxError( (TSyntaxError)sql.getSyntaxErrors().get(i) )); 2880 } 2881// for (int i = 0; i<sql.getSyntaxHints().size(); i++){ 2882// this.syntaxHints.add(new TSyntaxError( (TSyntaxError)sql.getSyntaxHints().get(i) )); 2883// } 2884} 2885 2886private static String calculateLicenseKey(boolean ignoreMachineId){ 2887 2888 if (userName == null) return null; 2889 if (machineId == null) return null; 2890 2891 byte[] bytesOfMessage=null; 2892 String licenseStr = "I love sql pretty printer, yeah!"+userName.toLowerCase(); 2893 if (!ignoreMachineId){ 2894 licenseStr += machineId.toLowerCase(); 2895 } 2896 try { 2897 bytesOfMessage = licenseStr.getBytes("UTF-8"); 2898 } catch (UnsupportedEncodingException e) { 2899 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 2900 } 2901 2902 MessageDigest md = null; 2903 try { 2904 md = MessageDigest.getInstance("MD5"); 2905 } catch (NoSuchAlgorithmException e) { 2906 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 2907 } 2908 byte[] digest = md.digest(bytesOfMessage); 2909 2910 return null;//HardwareBinder.getHex(digest); 2911} 2912 2913private static boolean validateLicense(){ 2914 2915 int ret = 0; 2916 return ret == 0; 2917 2918} 2919 2920private static boolean check_license_time(){ 2921 2922 boolean ret = false; 2923 2924 String toDate = TBaseType.license_expired_date; 2925 2926 DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT); 2927 Calendar currDtCal = Calendar.getInstance(); 2928 2929 // Zero out the hour, minute, second, and millisecond 2930 currDtCal.set(Calendar.HOUR_OF_DAY, 0); 2931 currDtCal.set(Calendar.MINUTE, 0); 2932 currDtCal.set(Calendar.SECOND, 0); 2933 currDtCal.set(Calendar.MILLISECOND, 0); 2934 2935 Date currDt = currDtCal.getTime(); 2936 2937 Date toDt; 2938 try { 2939 toDt = df.parse(toDate); 2940 } catch (ParseException e) { 2941 toDt = null; 2942 // Print some error message back to the user 2943 } 2944 2945 if (toDt != null){ 2946 int results = toDt.compareTo(currDt); 2947 ret = results > 0; 2948 } 2949 2950 return ret; 2951} 2952 2953 public void setTeradataUtilityType(TeradataUtilityType teradataUtilityType) { 2954 if ((this.getFlexer() != null) && (this.getFlexer() instanceof TLexerTeradata)){ 2955 ((TLexerTeradata)this.getFlexer()).setTeradataUtilityType(teradataUtilityType); 2956 } 2957 } 2958 2959 /** 2960 * Dispose of parser resources and clean up references. 2961 * 2962 * <p>This method releases internal resources used by the parser. 2963 * After calling this method, the parser should not be used further.</p> 2964 * 2965 * <p>If a ManagedSourceBuffer was set, the source text remains in the buffer 2966 * so that tokens can still access it. The user is responsible for calling 2967 * {@link ManagedSourceBuffer#release()} when all tokens are no longer needed.</p> 2968 * 2969 * <p>Note: Tokens created by this parser will remain usable after dispose() 2970 * if a ManagedSourceBuffer was used, as they reference the buffer by ID 2971 * rather than holding direct parser references.</p> 2972 * 2973 * @since 3.1.0.9 2974 */ 2975 public void dispose() { 2976 // Note: We intentionally do NOT remove the source from managed buffer 2977 // This allows tokens to remain usable after parser disposal 2978 // The user must call buffer.release() when done with all tokens 2979 2980 // Clear references to help GC 2981 flexer = null; 2982 fparser = null; 2983 fplsqlparser = null; 2984 finputstream = null; 2985 sqlInputStream = null; 2986 gcurrentsqlstatement = null; 2987 nextStmt = null; 2988 sqlstatements = null; 2989 sourcetokenlist = null; 2990 syntaxErrors = null; 2991 } 2992 2993}