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