001package gudusoft.gsqlparser; 002 003 004import java.io.BufferedReader; 005import java.io.IOException; 006import java.util.ArrayList; 007import java.util.Arrays; 008import java.util.HashMap; 009import java.util.Locale; 010 011/** 012 * Base lexer of all databases - Core tokenization engine for SQL parsing. 013 * 014 * The lexer reads SQL text character by character and produces tokens that represent 015 * the syntactic units of SQL. This process involves several key components and stages: 016 * 017 * <h3>1. Input Management and Buffering</h3> 018 * <ul> 019 * <li><b>yyinput (BufferedReader)</b>: Primary input source for SQL text</li> 020 * <li><b>yyline (char[])</b>: Current line buffer read from input via readln()</li> 021 * <li><b>buf (char[])</b>: Reversed line buffer for character-by-character processing</li> 022 * <li><b>bufptr</b>: Current position in buf, decrements as characters are consumed</li> 023 * </ul> 024 * 025 * <h3>2. Token Text Formation Process</h3> 026 * <pre> 027 * SQL Input → readln() → yyline[] → reversed into buf[] → get_char() → yytextbuf[] 028 * ↓ 029 * yylex() processing 030 * ↓ 031 * yylvalstr (String) 032 * ↓ 033 * TSourceToken.astext 034 * </pre> 035 * 036 * <h4>Key Variables in Token Text Storage:</h4> 037 * <ul> 038 * <li><b>yytextbuf (char[])</b>: Accumulator buffer for current token being formed</li> 039 * <li><b>yytextlen</b>: Current length of text in yytextbuf</li> 040 * <li><b>yytextbufsize</b>: Allocated size of yytextbuf (dynamically grows)</li> 041 * <li><b>yylvalstr (String)</b>: Final token text string created from yytextbuf</li> 042 * <li><b>literalbuf (StringBuilder)</b>: Special buffer for string literals and complex tokens</li> 043 * </ul> 044 * 045 * <h3>3. Position Tracking System</h3> 046 * 047 * The lexer maintains precise position information for every token: 048 * <ul> 049 * <li><b>yylineno</b>: Current line number (1-based)</li> 050 * <li><b>yycolno</b>: Current column number (0-based)</li> 051 * <li><b>offset</b>: Absolute character offset from start of input</li> 052 * <li><b>yylineno_p, yycolno_p, offset_p</b>: Previous position values for token start</li> 053 * </ul> 054 * 055 * <h3>4. Token Creation Workflow</h3> 056 * <ol> 057 * <li>Characters are read via get_char() from buf[] into yytextbuf[]</li> 058 * <li>yylex() identifies token boundaries and type</li> 059 * <li>Token text is extracted: yylvalstr = new String(yytextbuf, 0, yytextlen)</li> 060 * <li>yylexwrap() creates TSourceToken with: 061 * <ul> 062 * <li>astext = yylvalstr (full token text copy)</li> 063 * <li>lineNo = yylineno_p (start line)</li> 064 * <li>columnNo = yycolno_p (start column)</li> 065 * <li>offset = offset_p (absolute position)</li> 066 * </ul> 067 * </li> 068 * </ol> 069 * 070 * <h3>5. Memory Management and Text Copying</h3> 071 * 072 * <b>Current Implementation (Eager Loading):</b> 073 * <ul> 074 * <li>Every token immediately copies its text from yytextbuf to TSourceToken.astext</li> 075 * <li>Original SQL text in yyline is discarded after processing each line</li> 076 * <li>No direct link maintained between token and original input position</li> 077 * </ul> 078 * 079 * <h3>6. Tracing Back to Original Position</h3> 080 * 081 * <b>Currently Possible:</b> 082 * <ul> 083 * <li>Token stores lineNo, columnNo, and offset</li> 084 * <li>These can theoretically locate position in original input</li> 085 * </ul> 086 * 087 * <b>Current Limitations:</b> 088 * <ul> 089 * <li>Original input text is not retained after line processing</li> 090 * <li>yyline buffer is overwritten for each new line</li> 091 * <li>No mechanism to retrieve original text from position alone</li> 092 * </ul> 093 * 094 * @author Gudu Software 095 */ 096public class TCustomLexer { 097 098 // 在 lexer level 创建 token table, 按照 token code存储所有 token 的一些关键信息,主要用于处理一个关键字token被用作column,table name的情况 099 public static int MAX_TOKEN_SIZE = 2048; // 所有可能的token的数量 100 public static int MAX_TOKEN_COLUMN_SIZE = 10; 101 102 // 定义一个具有 MAX_TOKEN_SIZE 个元素的常量数组,每个元素有 MAX_TOKEN_COLUMN_SIZE 列, 列的类型为整数 103 // column 0: 代表该token出现的次数 104 // column 1: 代表该token第一次出现的 x position 105 // column 2: 代表该token第一次出现的 y position 106 // column 3: 代表该token最后一次出现的 x position 107 // column 4: 代表该token最后一次出现的 y position 108 // column 5: 代表该token第一次出现的 position in the token list 109 // column 6: 代表该token最后一次出现的 position in the token list 110 111 public static int COLUMN0_COUNT = 0; 112 public static int COLUMN1_FIRST_X = 1; 113 public static int COLUMN2_FIRST_Y = 2; 114 public static int COLUMN3_LAST_X = 3; 115 public static int COLUMN4_LAST_Y = 4; 116 public static int COLUMN5_FIRST_POS = 5; 117 public static int COLUMN6_LAST_POS = 6; 118 119 /** 120 * Pre-allocated strings for single ASCII characters (0-127). 121 * Used to avoid creating new String objects for common single-char tokens 122 * like '(', ')', ',', ';', '+', '-', '*', '/', etc. 123 * This significantly reduces GC pressure in the lexer hot path. 124 */ 125 private static final String[] SINGLE_CHAR_STRINGS = new String[128]; 126 static { 127 for (int i = 0; i < 128; i++) { 128 SINGLE_CHAR_STRINGS[i] = String.valueOf((char) i); 129 } 130 } 131 132 public long[][] TOKEN_TABLE = new long[MAX_TOKEN_SIZE][MAX_TOKEN_COLUMN_SIZE]; 133 134 /** 135 * Tracks which tokenIds have been written to TOKEN_TABLE during current parse. 136 * Used for incremental reset - only clear entries that were actually used. 137 */ 138 private int[] usedTokenIds = new int[512]; // Typical SQL uses <200 distinct token types 139 private int usedTokenCount = 0; 140 141 /** 142 * Reset TOKEN_TABLE by only clearing entries that were used (incremental clear). 143 * This is O(usedTokenCount) instead of O(MAX_TOKEN_SIZE * MAX_TOKEN_COLUMN_SIZE). 144 * For typical SQL with ~100 distinct token types, this saves clearing ~20,000 entries. 145 */ 146 public void resetTokenTable() { 147 for (int i = 0; i < usedTokenCount; i++) { 148 int tokenId = usedTokenIds[i]; 149 for (int j = 0; j < MAX_TOKEN_COLUMN_SIZE; j++) { 150 TOKEN_TABLE[tokenId][j] = 0L; 151 } 152 } 153 usedTokenCount = 0; 154 } 155 156 // define a function to set value when token is found, input is token id, a token with TSourceToken type 157 public void setTokenTableValue( TSourceToken token) { 158 if (token == null) return; 159 int tokenId = token.tokencode; 160 161 if (tokenId < 0 || tokenId >= MAX_TOKEN_SIZE) { 162 return; 163 } 164 if (TOKEN_TABLE[tokenId][COLUMN0_COUNT] == 0) { 165 // Track this tokenId for incremental reset 166 if (usedTokenCount < usedTokenIds.length) { 167 usedTokenIds[usedTokenCount++] = tokenId; 168 } 169 TOKEN_TABLE[tokenId][COLUMN0_COUNT] = 1; 170 TOKEN_TABLE[tokenId][COLUMN1_FIRST_X] = token.lineNo; 171 TOKEN_TABLE[tokenId][COLUMN2_FIRST_Y] = token.columnNo; 172 TOKEN_TABLE[tokenId][COLUMN3_LAST_X] = token.lineNo; 173 TOKEN_TABLE[tokenId][COLUMN4_LAST_Y] = token.columnNo; 174 TOKEN_TABLE[tokenId][COLUMN5_FIRST_POS] = token.posinlist; 175 TOKEN_TABLE[tokenId][COLUMN6_LAST_POS] = token.posinlist; 176 } else { 177 TOKEN_TABLE[tokenId][COLUMN0_COUNT] += 1; 178 TOKEN_TABLE[tokenId][COLUMN3_LAST_X] = token.lineNo; 179 TOKEN_TABLE[tokenId][COLUMN4_LAST_Y] = token.columnNo; 180 TOKEN_TABLE[tokenId][COLUMN6_LAST_POS] = token.posinlist; 181 } 182 } 183 184 public BufferedReader yyinput; 185 long yylineno,yycolno,offset,yylineno_p,yycolno_p,offset_p; 186 int bufptr,yystate,yysstate,yylstate,yytextlen,yyretval, yytextbufsize, 187 yymatches,yysleng; 188 char[] yyline; 189 /** 190 * Reusable buffer for readln() to reduce per-line allocations. 191 * Expands as needed for long lines and stays expanded for reuse. 192 */ 193 private char[] lineReadBuffer = new char[4096]; 194 /** 195 * Actual content length in lineReadBuffer/yyline. 196 * Used instead of yyline.length since lineReadBuffer is reused without copying. 197 */ 198 private int yylineLen; 199 String yylvalstr; 200 public String dolqstart = "";//postgresql, start part of Dollar-quoted String Constants 201 char yylastchar,yyactchar,yytablechar; 202 boolean yydone,yyreject; 203 char[] yytextbuf; 204 char[] buf; 205 int bufsize; 206 boolean endOfInput; 207 208 //StringBuffer literalbuf; 209 StringBuilder literalbuf; 210 int literallen,literalalloc,xcdepth,nchars,slashstar,dashdash; 211 boolean isqmarktoident; 212 public boolean insqlpluscmd; 213 char dummych1,dummych2,dummych3; 214 boolean utf8NoBreakSpaceReady = false; 215 216 int nestedLessThan = 0; 217 218 boolean isReadyForFunctionBody = false, isInFunctionBody = false; 219 int functionBodyDelimiterIndex = -1; 220 ArrayList<String> functionBodyDelimiter = new ArrayList<>(); 221 222 public static int keyword_type_reserved = 0x0001; 223 public static int keyword_type_keyword = 0x0002; 224 public static int keyword_type_identifier = 0x0004; 225 public static int keyword_type_column = 0x0008; 226 227 public char delimiterchar; 228 public String defaultDelimiterStr; 229 public String tmpDelimiter; 230 231 final static int intial_bufsize = 16384; 232 final static char lf = (char)10; 233 final static int max_chars = 65536*10*2; 234 final static int max_rules = 256*2*10; 235 int max_matches = 1024*20*10*2; 236 237 238 // 下面这些常量按照在 l 文件中出现的次序,必须以 +2 的方式递加. 为什么以 +2 的方式递加 原因忘了,尚未搞清楚。 239 final static int init = 2; 240 final static int xc = 4; 241 final static int xd = 6; 242 final static int xq = 8; 243 final static int xqq = 10; //oracle 244 final static int xdolq = 10;//postgresql 245 final static int xdbracket = 10; 246 final static int xdbrace = 12; 247 final static int xbacktick = 12; 248 249 final static int xbracketrs = 12; //redshift 250 final static int xdolq_clickhouse = 14;//clickhouse: 7th %start in its .l, after xdbracket(10) and xbacktick(12) 251 final static int xqtriple = 14;//bigquery 252 final static int xdtriple = 16;//bigquery 253 254 255 256 //https://docs.microsoft.com/en-us/sql/sql-server/maximum-capacity-specifications-for-sql-server 257 final static int namedatalen = 8060;//255; 258 259 final static int cmtslashstar = 257; 260 final static int cmtdoublehyphen = 258; 261 final static int lexspace = 259; 262 final static int lexnewline = 260; 263 final static int fconst = 261; 264 final static int sconst = 262; 265 final static int iconst = 263; 266 final static int ident = 264; 267 final static int op = 265; 268 final static int cmpop = 266; 269 final static int bind_v = 267; 270 final static int assign_sign = 268; 271 final static int double_dot = 269; 272 final static int label_begin = 270; 273 final static int label_end = 271; 274 final static int substitution_v = 272; 275 final static int filepath_sign = TBaseType.filepath_sign; 276 final static int sqlpluscmd = 273; 277 final static int atversion = TBaseType.atversion; //databricks 278 final static int error = 274; 279 final static int variable = 275; 280 final static int mslabel = 276; 281 public final static int bconst = TBaseType.bconst; //postgresql 282 final static int leftjoin_op = 277; 283 final static int odbc_esc_prefix = 277; 284 final static int rightjoin_op = 278; 285 final static int odbc_esc_terminator = 278; 286 final static int db2label = 279; 287 public final static int xconst = TBaseType.xconst; //postgresql 288 final static int ref_arrow = 280; 289 final static int rw_scriptoptions = 281; 290 public final static int UNICODE_ENCODE_ID = 281; 291 final static int mysqllabel = 282; 292 final static int NAMED_PARAMETER_SIGN = 282; //oracle,db2,snowflake CALL update_order (5000, NEW_STATUS => 'Shipped') 293 final static int QUOTED_IDENT = 282;//used in mdx 294 final static int BTEQCMD = 282; 295 final static int concatenationop = 283; 296 final static int pipe_greater = TBaseType.pipe_greater; // StarRocks pipe operator |> 297 final static int rw_not_deferrable = 284; 298 final static int rw_for1 = 285; 299 final static int stmt_delimiter = 286; 300 final static int AMP_QUOTED_ID = 285; //used in mdx 301 final static int AMP_UNQUOTED_ID = 286; //used in mdx 302 final static int m_clause = 287; 303 final static int MySQL_CHARSET_NAME = 287; 304 final static int typecast = TBaseType.typecast;//postgresql 305 final static int k_clause = 288; 306 final static int slash_dot = 288; 307 final static int outer_join = 289; 308 309 final static int not_equal = 290; 310 311 final static int param = TBaseType.param; 312 final static int mysql_null = TBaseType.rrw_mysql_null; 313 314 final static int rw_locktable = 296; 315 final static int rw_foreign2 = 297; 316 final static int rw_constraint2 = 298; 317 final static int rw_primary2 = 299; 318 final static int rw_unique2 = 300; 319 final static int NEXT_PARAM = TBaseType.NEXT_PARAM; 320 final static int POSITIONAL_PARAM = TBaseType.POSITIONAL_PARAM; 321 final static int NAMED_PARAM = TBaseType.NAMED_PARAM; 322 323 final static int castoperator = TBaseType.castoperator; 324 final static int twocolons = TBaseType.twocolons; 325 final static int compoundAssignmentOperator = TBaseType.compoundAssignmentOperator; 326 final static int postgresql_function_delimiter = TBaseType.rrw_postgresql_function_delimiter; 327 final static int greenplum_function_delimiter = TBaseType.rrw_greenplum_function_delimiter; 328 329 final static int redshift_function_delimiter = TBaseType.rrw_redshift_function_delimiter; 330 final static int snowflake_function_delimiter = TBaseType.rrw_snowflake_function_delimiter; 331 332 333 334 int[] yypos;// = new int[max_rules + 1]; // 1 based in delphi, Position 0 was not used here 335 int[] yystack;// = new int[max_matches + 1]; // 1 based in delphi, Position 0 was not used here 336 // ArrayList yystack; 337 338 //String keywordvaluefile,keywordfile,yyk_file,yym_file,yykl_file; 339 //String yykh_file,yyml_file,yymh_file,yytl_file,yyth_file,yytint_file,yyt_file; 340 341 EDbVendor dbvendor; 342 TSourceToken prevToken = null; 343 344 public void setSqlCharset(String sqlCharset) { 345 this.sqlCharset = sqlCharset; 346 } 347 348 public String getSqlCharset() { 349 return sqlCharset; 350 } 351 352 private String sqlCharset = null; 353 354 /** 355 * Check if token code represents a single character operator 356 */ 357 protected boolean isSingleCharOperator(int tokenCode) { 358 return tokenCode == '(' || tokenCode == ')' || 359 tokenCode == '[' || tokenCode == ']' || 360 tokenCode == '{' || tokenCode == '}' || 361 tokenCode == ',' || tokenCode == ';' || 362 tokenCode == '.' || tokenCode == ':' || 363 tokenCode == '+' || tokenCode == '-' || 364 tokenCode == '*' || tokenCode == '/' || 365 tokenCode == '%' || tokenCode == '=' || 366 tokenCode == '<' || tokenCode == '>' || 367 tokenCode == '!' || tokenCode == '&' || 368 tokenCode == '|' || tokenCode == '^' || 369 tokenCode == '~' || tokenCode == '?'; 370 } 371 372 /** 373 * Check if token code represents a keyword 374 */ 375 protected boolean isKeyword(int tokenCode) { 376 // Check if it's in the reserved word range 377 return tokenCode >= TBaseType.rrw_select && tokenCode < TBaseType.rrw_abort; 378 } 379 380 public TCustomLexer(){ 381 //this.yyinput = pbuf; 382 yytextbufsize = intial_bufsize - 1; 383 yytextbuf = new char[intial_bufsize]; 384 checkyytextbuf(yytextbufsize); 385 386 bufsize = intial_bufsize - 1; 387 buf = new char[intial_bufsize]; 388 checkbuf(bufsize); 389 390 //literalbuf = new StringBuffer(); 391 literalbuf = new StringBuilder(); 392 //keywordList = new TreeMap(); 393 delimiterchar = ';'; 394 tmpDelimiter = ""; 395 396 xcdepth = 0; 397 nchars = 0; 398 isqmarktoident = true; 399 400 yylvalstr = ""; 401 yysstate = 0; 402 yylstate = 0; 403 yymatches = 0; 404 yysleng = 0; 405 bufptr = 0; 406 yylineno = 0; 407 yycolno = 0; 408 offset = -1; 409 yylineno_p = 1; 410 yycolno_p = 1; 411 offset_p = 0; 412 413 yypos = new int[max_rules + 1]; 414 max_matches = TBaseType.LEXER_INIT_MAX_MATCHES; 415 yystack = new int[max_matches + 1]; 416 417 prevToken = null; 418 } 419 420 /* 421 * this function is not used. 422 private void getkeywordvaluefromfile(){ 423 int i; 424 keywordValueList.clear(); 425 for(i=0; i<keywordlist.length; i++){ 426 // System.out.println(keywordlist[i]); 427 String[] ss = keywordlist[i].split("[=]"); 428 keywordValueList.put(ss[0].toUpperCase(),ss[1]); 429 } 430 } 431 */ 432 433public int iskeyword(String str){ 434 return -1; 435} 436 437public boolean isAtBeginOfLine(){ 438 return (yyretval == lexnewline || yyretval == 0); 439} 440 441//public boolean canBeColumnName(int tokencode){ 442// return false; 443//} 444 445 446public String getStringByCode(int tokenCode){ 447 return null; 448} 449 450 public int getkeywordvalue(String keyword){ 451 return 0; 452 } 453 454 455 /** 456 * @deprecated , please use keywordChecker.isKeyword() instead. 457 * 458 * because there are so many non-reserved keywords in some databases, it's not suitable to put those 459 * non-reserved keywords in lexer and parser. 460 * 461 * @param keyword 462 * @param keywordValueList 463 * @param keywordTypeList 464 * @return 465 */ 466 public static EKeywordType getKeywordType(String keyword, HashMap<String, Integer> keywordValueList,HashMap<Integer, Integer> keywordTypeList){ 467 EKeywordType ret = EKeywordType.NOT_A_KEYWORD; 468 Integer s = keywordValueList.get(keyword.toUpperCase(Locale.ENGLISH)); 469 if( s == null) return ret; 470 471 Integer i = keywordTypeList.get(s); 472 if (i == 1) return EKeywordType.RESERVED_WORD; 473 else if (i == 2) return EKeywordType.NON_RESERVED_KEYWORD; 474 else return ret; 475 } 476 477 /** 478 * 如果是ascii 字符,直接返回,如果是unicode 字符,需要进行转换。否则 String.charAt() 返回的unicode字符不是我们想要的字符, 479 * 例如中文的括号,我们实际需要的ascii的括号 480 * 481 * @param pYylvalstr 482 * @param index 483 * @return 484 */ 485 char lexer_charAt(String pYylvalstr,int index){ 486 char ret = pYylvalstr.charAt(index); 487 if (ret > 255){ 488 // this is a unicode code 489 if ((ret == 0xFF08)){ 490 // https://www.utf8-chartable.de/unicode-utf8-table.pl?start=65280&number=128 491 // Unicode code point for FULLWIDTH LEFT PARENTHESIS (, 0xFF08 492 //System.out.println(c); 493 ret = '('; 494 } 495 if ( (ret == 0xFF09)){ 496 // https://www.utf8-chartable.de/unicode-utf8-table.pl?start=65280&number=128 497 // Unicode code point for FULLWIDTH RIGHT PARENTHESIS ), 0xFF09 498 // System.out.println(c); 499 ret = ')'; 500 } 501 } 502 return ret; 503 } 504 void totablechar(){ 505 //System.out.println("char:"+yyactchar+" ,hex:"+String.format("%04x", (int) yyactchar)); 506 //System.out.println(String.format("0x%08X", (int)yyactchar)+", "+(char)yyactchar); 507 508 if (((int) yyactchar == 0) && !endOfInput) { 509 yytablechar = (char)255; 510 return; 511 } 512 513 if ((int)(yyactchar) < 228){ // 228 is ä in unicode 514 yytablechar = yyactchar; 515 if ((((int)(yyactchar) == 160)&&(utf8NoBreakSpaceReady))||(yyactchar == 0xA0)){ 516 yytablechar = (char)32; 517 } 518 utf8NoBreakSpaceReady = false; 519// if (yyactchar == 0x27){ 520// insideSingleQuoteStr = !insideSingleQuoteStr; 521// } 522 }else{ 523 yytablechar = (char)'a';//(char)255; 524 525 if ((int)(yyactchar) == 914) { // c2 a0, utf-8 NO-BREAK SPACE 526 utf8NoBreakSpaceReady = true; 527 yytablechar = (char) 32; 528 }else if ((yyactchar == 0x2018)||(yyactchar == 0x2019)){ 529 if (stringLiteralStartWithUnicodeSingleQuote){ 530 // WHERE Name LIKE ‘Acme%’ 531 // 如上,如果string literal 以unicode quote 开始,则不管当前是否在string literal中,新碰到的unicode quote都看成是string literal的结尾符, 532 yytablechar = 0x27; // treat Unicode Character 'LEFT SINGLE QUOTATION MARK' as the ascii char ', but don't change it 533 }else { 534 if (insideSingleQuoteStr){ 535 // don't change the unicode quote char 536 }else { 537 yytablechar = 0x27; // treat Unicode Character 'LEFT SINGLE QUOTATION MARK' as the ascii char ', but don't change it 538 } 539 } 540 541 }else if ((yyactchar == 0x200B)||(yyactchar == 0x3000)||(yyactchar >= 0x2000 && yyactchar <= 0x200A)){ 542 // Unicode code point 0x200B: treat Unicode Character ZERO WIDTH SPACE as the ascii char space, but don't change it 543 // Unicode code point 0x3000: treat Unicode Character IDEOGRAPHIC SPACE (UTF-8: e3 80 80) as the ascii char space, but don't change it 544 // Unicode code points 0x2000-0x200A: General Punctuation space characters (EN QUAD, EM QUAD, EN SPACE, EM SPACE, THREE-PER-EM SPACE, etc.) 545 yytablechar = 0x20; 546 }else if (yyactchar == 0xFF08){ 547 yytablechar = '('; // treat Unicode code point for FULLWIDTH LEFT PARENTHESIS as the ascii char (, but don't change it 548 }else if (yyactchar == 0xFF09){ 549 yytablechar = ')'; // treat Unicode code point for FULLWIDTH RIGHT PARENTHESIS as the ascii char ), but don't change it 550 }else if (yyactchar == 0xFF0C){ 551 yytablechar = ','; // treat Unicode code point for FULLWIDTH COMMA as the ascii char comma, but don't change it 552 }else { 553 utf8NoBreakSpaceReady = false; 554 } 555 } 556 } 557 558 String getyytext(){ 559 return new String(yytextbuf,0,yytextlen); 560 } 561 562 563 void checkyytextbuf(int size){ 564 while ( size >= yytextbufsize){ 565 yytextbufsize = yytextbufsize * 2 > intial_bufsize ? yytextbufsize * 2: intial_bufsize; 566 char[] tmp = new char[yytextbufsize]; 567 System.arraycopy(yytextbuf,0,tmp,0, yytextbuf.length); 568 yytextbuf = tmp; 569 } 570 } 571 572 void checkbuf(int size){ 573 // System.out.println("while begin2"+" size:"+size+" bufsize:"+bufsize); 574 while ( size >= bufsize){ 575 bufsize = bufsize * 2 > intial_bufsize ? bufsize * 2: intial_bufsize; 576 char[] tmp = new char[bufsize]; 577 System.arraycopy(buf,0,tmp,0, buf.length); 578 buf = tmp; 579 } 580 // System.out.println("while end2"); 581 } 582 583 boolean eof(BufferedReader pbuf){ 584 try{ 585 return !pbuf.ready(); 586 }catch(IOException e){ 587 return true; 588 } 589 } 590 591 void yynew(){ 592 if (yylastchar != (char)0){ 593 if(yylastchar == lf){ 594 yylstate = 1; 595 }else{ 596 yylstate = 0; 597 } 598 } 599 600 yystate = yysstate + yylstate; 601 checkyytextbuf(0); 602 yytextlen = 0; 603 yymatches = 0; 604 yydone = false; 605 } 606 607 void yyscan(){ 608 yyactchar = get_char(); 609 checkyytextbuf(yytextlen + 1); 610 yytextlen++; 611 yytextbuf[yytextlen - 1] = yyactchar; 612 } 613 614 void yymark(int n){ 615 if (n > max_rules ){ 616 System.out.println("n > max_rules "); 617 } 618 yypos[n] = yytextlen; 619 } 620 621 void yymatch(int n){ 622 yymatches++; 623 if(yymatches > max_matches){ 624 625 int new_yystack[] = new int[max_matches*2+1]; 626 System.arraycopy(yystack, 0, new_yystack, 0, max_matches); 627 yystack = new_yystack; 628 max_matches = max_matches * 2; 629 630 // this is valid in JDK 1.6, proguard will report warning and stop 631 // yystack = Arrays.copyOf(yystack,max_matches+1); 632 633 } 634 yystack [yymatches] = n; 635 } 636 637 int yyfind(){ 638 //return -1 mean not found 639 int ret = -1; 640 641 yyreject = false; 642 643 while (( yymatches > 0 ) && ( yypos[yystack[yymatches]] == 0 )) { 644 yymatches-- ; 645 } 646 647 648 if (yymatches > 0){ 649 yysleng = yytextlen; 650 ret = yystack[yymatches]; 651 yyless( yypos[ret] ); 652 yypos[ret] = 0; 653 if (yytextlen >0){ 654 yylastchar = yytextbuf [yytextlen-1]; 655 }else{ 656 yylastchar = (char)0; 657 } 658 }else{ 659 yyless( 0 ); 660 yylastchar = (char)0; 661 } 662 663 return ret; 664 } 665 666 boolean yydefault(){ 667 boolean ret; 668 669 yyreject = false; 670 yyactchar = get_char(); 671 if (yyactchar != (char)0){ 672 //put_char( yyactchar ); 673 ret = true; 674 }else{ 675 yylstate = 1; 676 ret = false; 677 } 678 yylastchar = yyactchar; 679 return ret; 680 } 681 void yyless(int n){ 682 for(int i= yytextlen; i> n; i--){ 683 unget_char(yytextbuf[i - 1]); 684 } 685 checkyytextbuf(n); 686 yytextlen = n; 687 } 688 void returni(int n){ 689 yyretval = n; 690 yydone = true; 691 } 692 void returnc(char c){ 693 yyretval = (int)c; 694 yydone = true; 695 } 696 void yyclear(){ 697 bufptr = 0; 698 yysstate = 0; 699 yylstate = 1; 700 yylastchar = (char)0; 701 yytextlen = 0; 702 yylineno = 0; 703 yycolno = 0; 704 offset = -1; 705 // yystext := ''; 706 707 yylineno_p = 1; 708 yycolno_p = 1; 709 offset_p = 0; 710 711 } 712 713 714 boolean yywrap(){ 715 return true; 716 } 717 int getyysstate(){ 718 return yysstate; 719 } 720 void start(int pstate){ 721 yysstate = pstate; 722 if (pstate == xq){ 723 insideSingleQuoteStr = true; 724 if ((yylvalstr.charAt(0) == 0x2018)||(yylvalstr.charAt(0) == 0x2019)){ 725 stringLiteralStartWithUnicodeSingleQuote = true; 726 }else{ 727 stringLiteralStartWithUnicodeSingleQuote = false; 728 } 729 }else{ 730 insideSingleQuoteStr = false; 731 } 732 } 733 734 735 void unget_char(char pchar){ 736 // get_char() uses NUL as its end-of-input sentinel and does not 737 // advance yycolno/offset when returning it. Pushing that sentinel 738 // back used to decrement both counters as if a real source character 739 // had been consumed. A following yyless() split at EOF could then 740 // assign the next real token the preceding token's source span (for 741 // example, DB2 "k=?" gave '?' the '=' offset). Only ignore the EOF 742 // sentinel: a real NUL input character does advance the position and 743 // must still be restored. 744 if (pchar == (char)0 && endOfInput) { 745 endOfInput = false; 746 return; 747 } 748 if(bufptr == max_chars) 749 { 750 System.out.println("input buffer overflow"); 751 } 752 // if (bufptr > 0) { 753 bufptr++; 754 yycolno--; 755 offset--; 756 checkbuf(bufptr+1); 757 buf[bufptr] = pchar; 758 // } 759 760 } 761 762 public void reset(){ 763 insideSingleQuoteStr = false; 764 nestedLessThan = 0; 765 } 766 767 public boolean insideSingleQuoteStr = false; 768 public boolean stringLiteralStartWithUnicodeSingleQuote = false; 769 770 771 // Previous implementation of readln, 2025-05-04 772 // char[] readln() throws IOException { 773 // int c; 774 // char[] buffer = new char[80]; 775 // int bufferSize = 0; 776 777 // while ((c = yyinput.read()) != -1) { 778 // if (bufferSize >= buffer.length) { 779 // char[] newBuffer = new char[buffer.length * 2]; 780 // System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); 781 // buffer = newBuffer; 782 // } 783 784 // buffer[bufferSize++] = (char)c; 785 786 // if (c == '\n' || c == '\r') { 787 // break; 788 // } 789 // } 790 791 // if (bufferSize > 0 && buffer[bufferSize - 1] == '\r') { 792 // yyinput.mark(1); 793 // c = yyinput.read(); 794 // if (c == '\n') { 795 // if (bufferSize >= buffer.length) { 796 // char[] newBuffer = new char[buffer.length + 1]; 797 // System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); 798 // buffer = newBuffer; 799 // } 800 // buffer[bufferSize++] = '\n'; 801 // } else { 802 // yyinput.reset(); 803 // } 804 // } 805 806 // if (bufferSize == 0) { 807 // return null; 808 // }else{ 809 // char[] result = new char[bufferSize]; 810 // System.arraycopy(buffer, 0, result, 0, bufferSize); 811 // return result; 812 // } 813 // } 814 815/** 816 * High-performance line reader with optimal buffer management. 817 * Reuses lineReadBuffer across calls to reduce per-line allocations. 818 * @return char array containing the line including line ending, or null if end of stream 819 */ 820char[] readln() throws IOException { 821 if (yyinput == null) return null; 822 823 int position = 0; 824 int c; 825 826 // Read characters until line ending or EOF 827 while ((c = yyinput.read()) != -1) { 828 // Expand buffer if needed (expanded buffer stays for reuse) 829 if (position >= lineReadBuffer.length) { 830 char[] newBuffer = new char[lineReadBuffer.length * 2]; 831 System.arraycopy(lineReadBuffer, 0, newBuffer, 0, lineReadBuffer.length); 832 lineReadBuffer = newBuffer; 833 } 834 835 // Store character 836 lineReadBuffer[position++] = (char)c; 837 838 // Check for line endings 839 if (c == '\n') { 840 break; // LF - end of line 841 } else if (c == '\r') { 842 // Need to check for CR+LF sequence 843 yyinput.mark(1); 844 c = yyinput.read(); 845 846 if (c == '\n') { 847 // CR+LF sequence - include LF in result 848 if (position >= lineReadBuffer.length) { 849 char[] newBuffer = new char[lineReadBuffer.length + 1]; 850 System.arraycopy(lineReadBuffer, 0, newBuffer, 0, lineReadBuffer.length); 851 lineReadBuffer = newBuffer; 852 } 853 lineReadBuffer[position++] = '\n'; 854 } else { 855 // CR only - reset stream to keep the character after CR 856 yyinput.reset(); 857 } 858 break; 859 } 860 } 861 862 // Return null if no characters were read (end of stream) 863 if (position == 0) { 864 yylineLen = 0; 865 return null; 866 } 867 868 // Return lineReadBuffer directly, avoiding per-line array allocation. 869 // yylineLen holds the actual content length (replaces yyline.length semantic). 870 yylineLen = position; 871 return lineReadBuffer; 872} 873 874 char get_char(){ 875 876 char ret ; 877 boolean readlineok = true; 878 879 if ((bufptr == 0) && !eof(yyinput) ) 880 { 881 try{ 882 endOfInput = false; 883 yyline = readln();//yyinput.readLine(); 884 // System.out.println("readln: "+yyline); 885 if (yyline == null){ 886 readlineok = false; 887 } else{ 888 yylineno++; 889 yycolno = 0; 890 // Use yylineLen instead of yyline.length since lineReadBuffer is reused 891 bufptr = yylineLen; 892 checkbuf(bufptr+1); 893 for(int k=1;k<=bufptr;k++){ 894 buf[k] = yyline[bufptr - k]; 895 } 896 } 897 }catch(IOException e){ 898 readlineok = false; 899 } 900 } 901 902 if (! readlineok){ 903 endOfInput = true; 904 return (char)0; 905 } 906 907 if (bufptr > 0){ 908 bufptr--; 909 yycolno++; 910 offset++; 911 912 return buf[bufptr+1]; 913 //return yyline.charAt(yyline.length() - (bufptr + 1)); 914 }else{ 915 // bufptr--; 916 endOfInput = true; 917 return (char)0; 918 } 919 920 } 921 922 void startlit(){ 923 literalbuf.setLength(0); 924 literallen = 0; 925 literalalloc = 0; 926 } 927 928 void addlit(String ytext, int yleng){ 929 literallen = literallen + yleng; 930 literalbuf.append(ytext,0,yleng); 931 } 932 933 void addlitchar(char ychar){ 934 literallen++; 935 literalbuf.append(ychar); 936 } 937 938 String litbufdup(){ 939 return literalbuf.toString();//.intern(); 940 } 941 942 boolean isopchar(char ch){ 943 switch (ch) { 944 case '~': 945 case '!': 946 case '@': 947 case '#': 948 case '^': 949 case '&': 950 case '|': 951 case '`': 952 case '?': 953 case '$': 954 case '%': 955 return true; 956 default: 957 return false; 958 } 959 } 960 961 boolean isselfchar(char ch){ 962 switch (ch) { 963 case ',': 964 case '(': 965 case ')': 966 case '[': 967 case ']': 968 case '.': 969 case ';': 970 case '$': 971 case ':': 972 case '+': 973 case '-': 974 case '*': 975 case '/': 976 case '%': 977 case '^': 978 case '<': 979 case '>': 980 case '=': 981 case '!': 982 case '{': 983 case '}': 984 return true; 985 default: 986 return false; 987 } 988 } 989 990 boolean charinarray(char c, char[] a){ 991 int len = a.length; 992 for (int i = 0; i < len; i++) { 993 if (a[i] == c) 994 return true; 995 } 996 return false; 997 } 998 999 void setlengthofliteralbuf(int plen){ 1000 literalbuf.setLength(plen); 1001 } 1002 1003 void yyaction(int yyruleno){ 1004 } 1005 1006 int yylex(){ 1007 return 0; 1008 } 1009 1010 1011 public int yylexwrap(TSourceToken psourcetoken) { 1012 // Get token code and handle EOF 1013 if ((psourcetoken.tokencode = yylex()) == 0) return 0; 1014 1015 // Store token text - use shared strings for single ASCII chars to reduce allocations 1016 if (yylvalstr == null) { 1017 if (yytextlen == 1 && yytextbuf[0] < 128) { 1018 yylvalstr = SINGLE_CHAR_STRINGS[yytextbuf[0]]; 1019 } else { 1020 yylvalstr = new String(yytextbuf, 0, yytextlen); 1021 } 1022 } 1023 psourcetoken.setAstext(yylvalstr); 1024 1025 // Record token position information 1026 psourcetoken.lineNo = yylineno_p; 1027 psourcetoken.columnNo = yycolno_p; 1028 psourcetoken.offset = offset_p; 1029 yylineno_p = yylineno; 1030 yycolno_p = yycolno + 1; 1031 offset_p = offset + 1; 1032 1033 // Track token in token table for analysis 1034 setTokenTableValue(psourcetoken); 1035 1036 // Handle token types based on token code 1037 switch (psourcetoken.tokencode) { 1038 case cmtdoublehyphen: 1039 psourcetoken.tokentype = ETokenType.ttsimplecomment; 1040 if (dbvendor == EDbVendor.dbvmdx && psourcetoken.toString().startsWith("/")) { 1041 psourcetoken.tokentype = ETokenType.ttCPPComment; 1042 } 1043 break; 1044 1045 case cmtslashstar: 1046 psourcetoken.tokentype = ETokenType.ttbracketedcomment; 1047 break; 1048 1049 case lexspace: 1050 psourcetoken.tokentype = ETokenType.ttwhitespace; 1051 break; 1052 1053 case lexnewline: 1054 psourcetoken.tokentype = ETokenType.ttreturn; 1055 break; 1056 1057 case bind_v: 1058 psourcetoken.tokentype = ETokenType.ttbindvar; 1059 if (dbvendor == EDbVendor.dbvoracle) { 1060 psourcetoken.setAstext(psourcetoken.getAstext().replace(TBaseType.newline, "")); 1061 } 1062 break; 1063 1064 case stmt_delimiter: 1065 psourcetoken.tokentype = ETokenType.ttstmt_delimiter; 1066 psourcetoken.tokencode = cmtslashstar; 1067 break; 1068 1069 case concatenationop: 1070 psourcetoken.tokentype = ETokenType.ttconcatenationop; 1071 break; 1072 1073 case variable: 1074 psourcetoken.tokentype = ETokenType.ttsqlvar; 1075 break; 1076 1077 case fconst: 1078 case iconst: 1079 psourcetoken.tokentype = ETokenType.ttnumber; 1080 break; 1081 1082 case sconst: 1083 psourcetoken.tokentype = ETokenType.ttsqstring; 1084 psourcetoken.dolqstart = dolqstart; 1085 dolqstart = ""; 1086 break; 1087 1088 case ident: 1089 case QUOTED_IDENT: 1090 handleIdentifierToken(psourcetoken); 1091 break; 1092 1093 case cmpop: 1094 handleComparisonOperator(psourcetoken); 1095 break; 1096 1097 case op: 1098 handleOperatorToken(psourcetoken); 1099 break; 1100 1101 default: 1102 handleDefaultToken(psourcetoken); 1103 break; 1104 } 1105 1106 prevToken = psourcetoken; 1107 return psourcetoken.tokencode; 1108 } 1109 1110 // Helper methods to better organize the complex token handling logic 1111 private void handleIdentifierToken(TSourceToken psourcetoken) { 1112 psourcetoken.tokentype = ETokenType.ttidentifier; 1113 String tokenText = psourcetoken.toString().trim(); 1114 1115 if (tokenText.startsWith("\"")) { 1116 psourcetoken.tokentype = ETokenType.ttdqstring; 1117 } else if (tokenText.startsWith("[")) { 1118 if (dbvendor == EDbVendor.dbvmssql || dbvendor == EDbVendor.dbvsybase || dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq) { 1119 psourcetoken.tokentype = ETokenType.ttdbstring; 1120 } 1121 } else if (tokenText.startsWith("{")) { 1122 if (dbvendor == EDbVendor.dbvmssql || dbvendor == EDbVendor.dbvsybase || dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq) { 1123 psourcetoken.tokentype = ETokenType.ttbrstring; 1124 if (tokenText.toLowerCase().startsWith("{escape")) { 1125 psourcetoken.tokencode = TBaseType.rrw_sqlserver_odbc_escape; 1126 } 1127 } 1128 } else if (tokenText.startsWith("&")) { 1129 if (dbvendor == EDbVendor.dbvmdx) { 1130 if (psourcetoken.tokencode == QUOTED_IDENT) { 1131 psourcetoken.tokencode = AMP_QUOTED_ID; 1132 } else if (psourcetoken.tokencode == ident) { 1133 psourcetoken.tokencode = AMP_UNQUOTED_ID; 1134 } 1135 } 1136 } else if (tokenText.startsWith(".")) { 1137 if (dbvendor == EDbVendor.dbvteradata) { 1138 psourcetoken.tokentype = ETokenType.ttBTEQCmd; 1139 } 1140 } 1141 } 1142 1143 private void handleComparisonOperator(TSourceToken psourcetoken) { 1144 psourcetoken.tokentype = ETokenType.ttmulticharoperator; 1145 String token = yylvalstr; 1146 1147 // Oracle 26c vector distance operators (3-char, Oracle only) 1148 if (dbvendor == EDbVendor.dbvoracle) { 1149 if (token.equals("<=>")) { 1150 psourcetoken.tokencode = TBaseType.vector_cosine_distance; 1151 return; 1152 } else if (token.equals("<->")) { 1153 psourcetoken.tokencode = TBaseType.vector_euclidean_distance; 1154 return; 1155 } else if (token.equals("<#>")) { 1156 psourcetoken.tokencode = TBaseType.vector_dot_product; 1157 return; 1158 } 1159 } 1160 1161 if ((token.startsWith("!") && token.endsWith("=")) || 1162 (token.startsWith("^") && token.endsWith("=")) || 1163 (token.startsWith("~") && token.endsWith("=")) || 1164 (token.startsWith("<") && token.endsWith(">"))) { 1165 1166 psourcetoken.tokencode = TBaseType.not_equal; 1167 1168 // Handle MySQL NULL-safe equal 1169 if (token.indexOf("=", 1) > 0 && 1170 token.startsWith("<") && token.endsWith(">")) { 1171 psourcetoken.tokencode = (int)'='; 1172 } 1173 } else if (token.startsWith(">") && token.endsWith("=")) { 1174 psourcetoken.tokencode = TBaseType.great_equal; 1175 } else if (token.startsWith("<") && token.endsWith("=")) { 1176 psourcetoken.tokencode = TBaseType.less_equal; 1177 } else if ((token.startsWith("!") && token.endsWith("<")) || 1178 (token.startsWith("^") && token.endsWith("<"))) { 1179 psourcetoken.tokencode = TBaseType.not_less; 1180 } else if ((token.startsWith("!") && token.endsWith(">")) || 1181 (token.startsWith("^") && token.endsWith(">"))) { 1182 psourcetoken.tokencode = TBaseType.not_great; 1183 } else if (token.length() == 2 && token.charAt(0) == ':' && token.charAt(1) == '=') { 1184 psourcetoken.tokencode = assign_sign; 1185 } 1186 } 1187 1188 private void handleOperatorToken(TSourceToken psourcetoken) { 1189 psourcetoken.tokentype = ETokenType.ttmulticharoperator; 1190 String token = yylvalstr; 1191 int tokenLength = token.length(); 1192 char firstChar = tokenLength > 0 ? token.charAt(0) : '\0'; 1193 char secondChar = tokenLength > 1 ? token.charAt(1) : '\0'; 1194 1195 // Handle question mark specially 1196 if (token.equals("?") && isqmarktoident) { 1197 handleQuestionMark(psourcetoken); 1198 return; 1199 } 1200 1201 // Handle special two-character operators 1202 if (tokenLength == 2) { 1203 if (handleTwoCharOperator(psourcetoken, firstChar, secondChar)) { 1204 return; 1205 } 1206 } 1207 1208 // Handle special three-character operators 1209 if (tokenLength == 3) { 1210 if (handleThreeCharOperator(psourcetoken, firstChar, secondChar, token.charAt(2))) { 1211 return; 1212 } 1213 } 1214 1215 // Handle comparison operators 1216 if (handleComparisonOp(psourcetoken, token)) { 1217 return; 1218 } 1219 1220 // Handle single character operators 1221 if (tokenLength == 1) { 1222 handleSingleCharOperator(psourcetoken, firstChar); 1223 } 1224 } 1225 1226 private boolean handleTwoCharOperator(TSourceToken psourcetoken, char firstChar, char secondChar) { 1227 switch (firstChar) { 1228 case '<': 1229 if (secondChar == '<') { 1230 return handleLeftShiftOperator(psourcetoken); 1231 } else if (secondChar == '@') { 1232 psourcetoken.tokencode = TBaseType.JSON_RIGHT_CONTAIN; 1233 return true; 1234 } 1235 break; 1236 1237 case '>': 1238 if (secondChar == '>') { 1239 return handleRightShiftOperator(psourcetoken); 1240 } 1241 break; 1242 1243 case '=': 1244 if (secondChar == '>') { 1245 if (dbvendor == EDbVendor.dbvodbc) { 1246 psourcetoken.tokencode = TBaseType.great_equal; 1247 } else if (dbvendor == EDbVendor.dbvpostgresql || dbvendor == EDbVendor.dbvgaussdb || dbvendor == EDbVendor.dbvedb) { 1248 psourcetoken.tokencode = TBaseType.assign_sign; 1249 } else { 1250 psourcetoken.tokencode = NAMED_PARAMETER_SIGN; 1251 } 1252 return true; 1253 } else if (secondChar == '*') { 1254 if (dbvendor == EDbVendor.dbvmssql || dbvendor == EDbVendor.dbvsybase || dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq) { 1255 psourcetoken.tokencode = rightjoin_op; 1256 } 1257 return true; 1258 } else if (secondChar == '<') { 1259 if (dbvendor == EDbVendor.dbvodbc) { 1260 psourcetoken.tokencode = TBaseType.less_equal; 1261 } 1262 return true; 1263 } else if (secondChar == '=') { 1264 if (dbvendor == EDbVendor.dbvsparksql || dbvendor == EDbVendor.dbvclickhouse) { 1265 psourcetoken.tokencode = '='; 1266 } 1267 return true; 1268 } 1269 break; 1270 1271 case '-': 1272 if (secondChar == '>') { 1273 if (dbvendor == EDbVendor.dbvpostgresql || dbvendor == EDbVendor.dbvgaussdb || dbvendor == EDbVendor.dbvedb 1274 || dbvendor == EDbVendor.dbvgreenplum || dbvendor == EDbVendor.dbvmysql) { 1275 psourcetoken.tokencode = TBaseType.JSON_GET_OBJECT; 1276 } else { 1277 psourcetoken.tokencode = ref_arrow; 1278 } 1279 return true; 1280 } else if (secondChar == '=') { 1281 if (dbvendor == EDbVendor.dbvmssql || dbvendor == EDbVendor.dbvsybase || dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq) { 1282 psourcetoken.tokencode = compoundAssignmentOperator; 1283 } 1284 return true; 1285 } 1286 break; 1287 1288 case '.': 1289 if (secondChar == '.') { 1290 if (dbvendor == EDbVendor.dbvdb2 || dbvendor == EDbVendor.dbvoracle 1291 || dbvendor == EDbVendor.dbvmysql || dbvendor == EDbVendor.dbvhana) { 1292 psourcetoken.tokencode = double_dot; 1293 } 1294 return true; 1295 } 1296 break; 1297 1298 case '*': 1299 if (secondChar == '=') { 1300 if (dbvendor == EDbVendor.dbvmssql || dbvendor == EDbVendor.dbvsybase || dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq) { 1301 psourcetoken.tokencode = leftjoin_op; 1302 } 1303 return true; 1304 } else if (secondChar == '*') { 1305 if (dbvendor == EDbVendor.dbvteradata || dbvendor == EDbVendor.dbvnetezza) { 1306 psourcetoken.tokencode = TBaseType.exponentiate; 1307 } 1308 return true; 1309 } 1310 break; 1311 1312 case '|': 1313 if (secondChar == '|') { 1314 if (dbvendor == EDbVendor.dbvmysql) { 1315 psourcetoken.tokencode = TBaseType.logical_or; 1316 } else if (isStringConcatVendor(dbvendor)) { 1317 psourcetoken.tokencode = TBaseType.concatenationop; 1318 } 1319 return true; 1320 } else if (secondChar == '>') { 1321 if (dbvendor == EDbVendor.dbvsparksql) { 1322 psourcetoken.tokencode = TBaseType.sparksql_pipe_arrow; 1323 } else { 1324 psourcetoken.tokencode = TBaseType.pipe_greater; 1325 } 1326 return true; 1327 } else if (secondChar == '/') { 1328 if (dbvendor == EDbVendor.dbvredshift) { 1329 psourcetoken.tokencode = TBaseType.square_root; 1330 } 1331 return true; 1332 } 1333 break; 1334 1335 case '&': 1336 if (secondChar == '&') { 1337 if (dbvendor == EDbVendor.dbvmysql) { 1338 psourcetoken.tokencode = TBaseType.logical_and; 1339 } 1340 return true; 1341 } 1342 break; 1343 1344 case '?': 1345 if (secondChar == '|') { 1346 psourcetoken.tokencode = TBaseType.JSON_ANY_EXIST; 1347 return true; 1348 } else if (secondChar == '&') { 1349 psourcetoken.tokencode = TBaseType.JSON_ALL_EXIST; 1350 return true; 1351 } 1352 break; 1353 1354 case '@': 1355 if (secondChar == '>') { 1356 psourcetoken.tokencode = TBaseType.JSON_LEFT_CONTAIN; 1357 return true; 1358 } 1359 break; 1360 1361 case '#': 1362 if (secondChar == '>') { 1363 psourcetoken.tokencode = TBaseType.JSON_GET_OBJECT_AT_PATH; 1364 return true; 1365 } 1366 break; 1367 1368 case ':': 1369 if (secondChar == '=') { 1370 psourcetoken.tokencode = assign_sign; 1371 return true; 1372 } 1373 break; 1374 } 1375 1376 // Handle compound assignment operators 1377 if ((firstChar == '+' || firstChar == '-' || firstChar == '*' || 1378 firstChar == '/' || firstChar == '%' || firstChar == '&' || 1379 firstChar == '^' || firstChar == '|') && secondChar == '=') { 1380 if (dbvendor == EDbVendor.dbvmssql || dbvendor == EDbVendor.dbvsybase || dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq) { 1381 psourcetoken.tokencode = compoundAssignmentOperator; 1382 return true; 1383 } else if (dbvendor == EDbVendor.dbvmysql && firstChar == '^' && secondChar == '=') { 1384 psourcetoken.tokencode = not_equal; 1385 return true; 1386 } 1387 } 1388 1389 return false; 1390 } 1391 1392 private boolean handleThreeCharOperator(TSourceToken psourcetoken, char firstChar, char secondChar, char thirdChar) { 1393 if (firstChar == '-' && secondChar == '>' && thirdChar == '>') { 1394 psourcetoken.tokencode = TBaseType.JSON_GET_TEXT; 1395 return true; 1396 } else if (firstChar == '#' && secondChar == '>' && thirdChar == '>') { 1397 psourcetoken.tokencode = TBaseType.JSON_GET_TEXT_AT_PATH; 1398 return true; 1399 } else if (firstChar == '|' && secondChar == '|' && thirdChar == '/') { 1400 if (dbvendor == EDbVendor.dbvredshift) { 1401 psourcetoken.tokencode = TBaseType.cube_root; 1402 return true; 1403 } 1404 } 1405 return false; 1406 } 1407 1408 private boolean handleComparisonOp(TSourceToken psourcetoken, String token) { 1409 if ((token.startsWith("!") && token.endsWith("=")) || 1410 (token.startsWith("^") && token.endsWith("=")) || 1411 (token.startsWith("<") && token.endsWith(">"))) { 1412 psourcetoken.tokencode = TBaseType.not_equal; 1413 return true; 1414 } else if (token.startsWith(">") && token.endsWith("=")) { 1415 psourcetoken.tokencode = TBaseType.great_equal; 1416 return true; 1417 } else if (token.startsWith("<") && token.endsWith("=")) { 1418 psourcetoken.tokencode = TBaseType.less_equal; 1419 return true; 1420 } else if ((token.startsWith("!") && token.endsWith("<")) || 1421 (token.startsWith("^") && token.endsWith("<"))) { 1422 psourcetoken.tokencode = TBaseType.not_less; 1423 return true; 1424 } else if ((token.startsWith("!") && token.endsWith(">")) || 1425 (token.startsWith("^") && token.endsWith(">"))) { 1426 psourcetoken.tokencode = TBaseType.not_great; 1427 return true; 1428 } 1429 return false; 1430 } 1431 1432 private void handleSingleCharOperator(TSourceToken psourcetoken, char ch) { 1433 switch (ch) { 1434 case '~': 1435 if (dbvendor == EDbVendor.dbvmysql || dbvendor == EDbVendor.dbvredshift || 1436 dbvendor == EDbVendor.dbvsnowflake) { 1437 psourcetoken.tokencode = (int)'~'; 1438 } 1439 break; 1440 1441 case '#': 1442 if (dbvendor == EDbVendor.dbvmssql) { 1443 psourcetoken.tokencode = (int)'#'; 1444 } 1445 break; 1446 1447 case '&': 1448 if (dbvendor == EDbVendor.dbvmysql || dbvendor == EDbVendor.dbvvertica || 1449 dbvendor == EDbVendor.dbvsparksql) { 1450 psourcetoken.tokencode = (int)'&'; 1451 } 1452 break; 1453 1454 case '|': 1455 if (dbvendor == EDbVendor.dbvmysql || dbvendor == EDbVendor.dbvvertica) { 1456 psourcetoken.tokencode = (int)'|'; 1457 } 1458 break; 1459 } 1460 } 1461 1462 private void handleQuestionMark(TSourceToken psourcetoken) { 1463 if (dbvendor == EDbVendor.dbvpostgresql || dbvendor == EDbVendor.dbvgaussdb || dbvendor == EDbVendor.dbvedb || 1464 dbvendor == EDbVendor.dbvgreenplum) { 1465 psourcetoken.tokencode = TBaseType.JSON_EXIST; 1466 } else if (dbvendor == EDbVendor.dbvodbc) { 1467 psourcetoken.tokencode = '?'; 1468 } else if (dbvendor == EDbVendor.dbvsnowflake) { 1469 psourcetoken.tokencode = bind_v; 1470 psourcetoken.tokentype = ETokenType.ttquestionmark; 1471 } else { 1472 psourcetoken.tokencode = ident; 1473 } 1474 } 1475 1476 private boolean handleLeftShiftOperator(TSourceToken psourcetoken) { 1477 if (dbvendor == EDbVendor.dbvoracle || dbvendor == EDbVendor.dbvmssql || 1478 dbvendor == EDbVendor.dbvsybase || dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq || dbvendor == EDbVendor.dbvpostgresql || 1479 dbvendor == EDbVendor.dbvgaussdb || dbvendor == EDbVendor.dbvedb || dbvendor == EDbVendor.dbvaccess || 1480 dbvendor == EDbVendor.dbvgreenplum || dbvendor == EDbVendor.dbvsnowflake) { 1481 psourcetoken.tokencode = label_begin; 1482 } else if (dbvendor == EDbVendor.dbvmysql) { 1483 psourcetoken.tokencode = TBaseType.rrw_left_shift; 1484 } else if (dbvendor == EDbVendor.dbvredshift) { 1485 psourcetoken.tokencode = TBaseType.bitwise_shift_left; 1486 } else if (dbvendor == EDbVendor.dbvnetezza) { 1487 psourcetoken.tokencode = TBaseType.rrw_netezza_op_less_less; 1488 } 1489 return true; 1490 } 1491 1492 private boolean handleRightShiftOperator(TSourceToken psourcetoken) { 1493 if (dbvendor == EDbVendor.dbvoracle || dbvendor == EDbVendor.dbvmssql || 1494 dbvendor == EDbVendor.dbvsybase || dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq || dbvendor == EDbVendor.dbvpostgresql || 1495 dbvendor == EDbVendor.dbvgaussdb || dbvendor == EDbVendor.dbvedb || dbvendor == EDbVendor.dbvgreenplum || 1496 dbvendor == EDbVendor.dbvaccess || dbvendor == EDbVendor.dbvsnowflake) { 1497 psourcetoken.tokencode = label_end; 1498 } else if (dbvendor == EDbVendor.dbvmysql) { 1499 psourcetoken.tokencode = TBaseType.rrw_right_shift; 1500 } else if (dbvendor == EDbVendor.dbvredshift) { 1501 psourcetoken.tokencode = TBaseType.bitwise_shift_right; 1502 } else if (dbvendor == EDbVendor.dbvnetezza) { 1503 psourcetoken.tokencode = TBaseType.rrw_netezza_op_great_great; 1504 } 1505 return true; 1506 } 1507 1508 private boolean isStringConcatVendor(EDbVendor vendor) { 1509 return vendor == EDbVendor.dbvdb2 || vendor == EDbVendor.dbvnetezza || 1510 vendor == EDbVendor.dbvpostgresql || vendor == EDbVendor.dbvgaussdb || vendor == EDbVendor.dbvedb || 1511 vendor == EDbVendor.dbvredshift || vendor == EDbVendor.dbvgreenplum || 1512 vendor == EDbVendor.dbvbigquery || vendor == EDbVendor.dbvsnowflake || 1513 vendor == EDbVendor.dbvsparksql || vendor == EDbVendor.dbvvertica; 1514 } 1515 1516 private void handleDefaultToken(TSourceToken psourcetoken) { 1517 psourcetoken.tokentype = ETokenType.ttkeyword; 1518 1519 if (psourcetoken.tokencode < 255) { 1520 // Single character operators (ASCII characters) 1521 psourcetoken.setAstext(Character.toString(yylvalstr.charAt(0))); 1522 psourcetoken.tokentype = ETokenType.ttsinglecharoperator; 1523 1524 switch (psourcetoken.tokencode) { 1525 case ',': 1526 psourcetoken.tokentype = ETokenType.ttcomma; 1527 break; 1528 case '(': 1529 psourcetoken.tokentype = ETokenType.ttleftparenthesis; 1530 break; 1531 case ')': 1532 psourcetoken.tokentype = ETokenType.ttrightparenthesis; 1533 break; 1534 case '[': 1535 psourcetoken.tokentype = ETokenType.ttleftbracket; 1536 break; 1537 case ']': 1538 psourcetoken.tokentype = ETokenType.ttrightbracket; 1539 break; 1540 case '.': 1541 psourcetoken.tokentype = ETokenType.ttperiod; 1542 break; 1543 case ';': 1544 psourcetoken.tokentype = ETokenType.ttsemicolon; 1545 break; 1546 case '$': 1547 psourcetoken.tokentype = ETokenType.ttdolorsign; 1548 break; 1549 case ':': 1550 psourcetoken.tokentype = ETokenType.ttcolon; 1551 break; 1552 case '+': 1553 psourcetoken.tokentype = ETokenType.ttplussign; 1554 break; 1555 case '-': 1556 psourcetoken.tokentype = ETokenType.ttminussign; 1557 break; 1558 case '*': 1559 psourcetoken.tokentype = ETokenType.ttasterisk; 1560 break; 1561 case '/': 1562 psourcetoken.tokentype = ETokenType.ttslash; 1563 break; 1564 case '^': 1565 psourcetoken.tokentype = ETokenType.ttcaret; 1566 break; 1567 case '<': 1568 psourcetoken.tokentype = ETokenType.ttlessthan; 1569 break; 1570 case '>': 1571 psourcetoken.tokentype = ETokenType.ttgreaterthan; 1572 break; 1573 case '=': 1574 psourcetoken.tokentype = ETokenType.ttequals; 1575 break; 1576 case '@': 1577 if (delimiterchar == '@') { 1578 psourcetoken.tokencode = (int)';'; 1579 psourcetoken.tokentype = ETokenType.ttsemicolon; 1580 } else { 1581 psourcetoken.tokentype = ETokenType.ttatsign; 1582 } 1583 break; 1584 case '~': 1585 psourcetoken.tokentype = ETokenType.tttilde; 1586 break; 1587 case '&': 1588 psourcetoken.tokentype = ETokenType.ttampersand; 1589 break; 1590 case '|': 1591 psourcetoken.tokentype = ETokenType.ttverticalbar; 1592 break; 1593 case '?': 1594 if (isqmarktoident && dbvendor != EDbVendor.dbvodbc && 1595 dbvendor != EDbVendor.dbvpostgresql && dbvendor != EDbVendor.dbvgaussdb && dbvendor != EDbVendor.dbvedb && dbvendor != EDbVendor.dbvduckdb) { 1596 psourcetoken.tokencode = ident; 1597 } 1598 break; 1599 } 1600 } else if (dbvendor == EDbVendor.dbvhive && psourcetoken.tokencode == TBaseType.hive_equal) { 1601 psourcetoken.tokentype = ETokenType.ttequals; 1602 } 1603 } 1604 1605}