001package gudusoft.gsqlparser.util; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.TGSqlParser; 005import gudusoft.gsqlparser.dlineage.dataflow.model.ModelBindingManager; 006import gudusoft.gsqlparser.dlineage.dataflow.model.SubType; 007import gudusoft.gsqlparser.dlineage.dataflow.model.xml.table; 008import gudusoft.gsqlparser.pp.para.GFmtOpt; 009import gudusoft.gsqlparser.pp.para.GFmtOptFactory; 010import gudusoft.gsqlparser.pp.para.styleenums.TCaseOption; 011import gudusoft.gsqlparser.pp.stmtformatter.FormatterFactory; 012import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType; 013import gudusoft.gsqlparser.sqlenv.IdentifierService; 014import gudusoft.gsqlparser.sqlenv.TSQLEnv; 015 016import java.io.*; 017import java.nio.charset.Charset; 018import java.security.MessageDigest; 019import java.util.*; 020import java.util.stream.Collectors; 021 022public class SQLUtil { 023 private static final Logger logger = LoggerFactory.getLogger(SQLUtil.class); 024 025 026 public static String formatSql( EDbVendor dbVendor, String inputQuery ) 027 { 028 String Result = inputQuery; 029 TGSqlParser sqlparser = new TGSqlParser( dbVendor ); 030 sqlparser.sqltext = inputQuery; 031 int ret = sqlparser.parse( ); 032 if ( ret == 0 ) 033 { 034 GFmtOpt option = GFmtOptFactory.newInstance(); 035 option.caseFuncname = TCaseOption.CoNoChange; 036 Result = FormatterFactory.pp(sqlparser, option); 037 } 038 return Result; 039 } 040 041 public static boolean isEmpty(String value) { 042 return value == null || value.trim().length() == 0; 043 } 044 045 public static String getFileContent(File file) { 046 String charset = null; 047 String sqlfilename = file.getAbsolutePath(); 048 int read = 0; 049 try { 050 FileInputStream fr = new FileInputStream(sqlfilename); 051 byte[] bom = new byte[4]; 052 fr.read(bom, 0, bom.length); 053 054 if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) && (bom[2] == (byte) 0xFE) 055 && (bom[3] == (byte) 0xFF)) { 056 charset = "UTF-32BE"; 057 read = 4; 058 } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00) 059 && (bom[3] == (byte) 0x00)) { 060 charset = "UTF-32LE"; 061 read = 4; 062 } else if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) { 063 charset = "UTF-8"; 064 read = 3; 065 } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) { 066 charset = "UTF-16BE"; 067 read = 2; 068 } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) { 069 charset = "UTF-16LE"; 070 read = 2; 071 } else { 072 charset = "UTF-8"; 073 read = 0; 074 } 075 076 fr.close(); 077 } catch (IOException e) { 078 logger.error("Check file encoding failed.", e); 079 } 080 081 Long filelength = file.length(); 082 byte[] filecontent = new byte[filelength.intValue()]; 083 try { 084 InputStream in = new BufferedInputStream(new FileInputStream(sqlfilename)); 085 in.read(filecontent); 086 in.close(); 087 } catch (IOException e) { 088 logger.error("read file content failed.", e); 089 } 090 091 byte[] content = new byte[filelength.intValue() - read]; 092 System.arraycopy(filecontent, read, content, 0, content.length); 093 094 try { 095 String fileContent = new String(content, charset == null ? Charset.defaultCharset().name() : charset); 096 return fileContent.replace((char) 160, (char) 32); 097 } catch (UnsupportedEncodingException e) { 098 logger.error("The OS does not support " + charset == null ? Charset.defaultCharset().name() : charset, e); 099 return null; 100 } 101 } 102 103 public static String getInputStreamContent(InputStream is, boolean close) { 104 try { 105 ByteArrayOutputStream out = new ByteArrayOutputStream(4096); 106 byte[] tmp = new byte[4096]; 107 while (true) { 108 int r = is.read(tmp); 109 if (r == -1) 110 break; 111 out.write(tmp, 0, r); 112 } 113 byte[] bytes = out.toByteArray(); 114 if (close) { 115 is.close(); 116 } 117 out.close(); 118 String content = new String(bytes); 119 return content; 120 } catch (IOException e) { 121 logger.error("read inputStream failed.", e); 122 } 123 return null; 124 } 125 126 public static String getFileContent(String filePath) { 127 if (filePath == null) 128 return ""; 129 File file = new File(filePath); 130 if (!file.exists() || file.isDirectory()) 131 return ""; 132 return getFileContent(file); 133 } 134 135 public static String trimColumnStringQuote(String string) { 136 try { 137 if (string == null) 138 return string; 139 140 if (string.indexOf('.') != -1) { 141 List<String> splits = parseNames(string); 142 if (splits.size() > 1) { 143 StringBuilder buffer = new StringBuilder(); 144 for (int i = 0; i < splits.size(); i++) { 145 String segment = splits.get(i); 146 if (parseNames(trimColumnStringQuote(segment)).size() > 1) { 147 buffer.append(segment); 148 } else { 149 buffer.append(trimColumnStringQuote(segment)); 150 } 151 if (i < splits.size() - 1) { 152 buffer.append("."); 153 } 154 } 155 string = buffer.toString(); 156 return string; 157 } 158 } 159 if (string.length() < 2) { 160 return string; 161 } 162 if (string.startsWith("'") && string.endsWith("'")) 163 return string.substring(1, string.length() - 1); 164 else if (string.startsWith("\"") && string.endsWith("\"")) 165 return string.substring(1, string.length() - 1); 166 else if (string.startsWith("`") && string.endsWith("`")) 167 return string.substring(1, string.length() - 1); 168 else if (string.startsWith("[") && string.endsWith("]")) 169 return string.substring(1, string.length() - 1); 170 return string; 171 }catch (Exception e){ 172 return string; 173 } 174 } 175 176// public static List<String> parseNames(String nameString, EDbVendor vendor) { 177// List<String> names = new ArrayList<String>(); 178// if (nameString.startsWith("`") && nameString.endsWith("`")){ 179// nameString = nameString.substring(1,nameString.length()-1); 180// } 181// String[] splits = nameString.trim().split("\\."); 182// 183// for (int i = 0; i < splits.length; i++) { 184// String split = splits[i].trim(); 185// if (TSQLEnv.isDelimitedIdentifier(vendor, split) && !TSQLEnv.endsWithDelimitedIdentifier(vendor, split)) { 186// StringBuilder buffer = new StringBuilder(); 187// buffer.append(splits[i]); 188// while (i < splits.length - 1 189// && !TSQLEnv.endsWithDelimitedIdentifier(vendor, split = splits[++i].trim())) { 190// buffer.append("."); 191// buffer.append(splits[i]); 192// } 193// 194// buffer.append("."); 195// buffer.append(splits[i]); 196// 197// names.add(buffer.toString()); 198// continue; 199// } 200// names.add(splits[i]); 201// } 202// return names; 203// } 204 205 public static List<String> parseNames(String nameString) { 206 return parseNames(nameString, null); 207 } 208 209 // Pre-compiled patterns for better performance 210 private static final java.util.regex.Pattern DOT_PATTERN = java.util.regex.Pattern.compile("\\s*\\.\\s*"); 211 212// // LRU Cache for parseNames results - thread-safe with limited size 213 private static final java.util.Map<String, List<String>> PARSE_NAMES_CACHE = 214 Collections.synchronizedMap(new java.util.LinkedHashMap<String, List<String>>(256, 0.75f, true) { 215 @Override 216 protected boolean removeEldestEntry(java.util.Map.Entry<String, List<String>> eldest) { 217 return size() > 10000; // Keep cache under 10000 entries 218 } 219 }); 220 221 /** 222 * 解析以点号分隔的 SQL 标识符或表达式,并返回各层级片段。 223 * <p> 224 * 用途: 225 * <ul> 226 * <li>将类似 catalog.schema.table.column 的全限定名拆分为有序片段;</li> 227 * <li>在拆分时识别并保留引号/括号内的点号,不做误分割;</li> 228 * <li>根据不同数据库厂商(如 BigQuery)的定界符规则进行处理。</li> 229 * </ul> 230 * 工作机制: 231 * <ol> 232 * <li>按「可选空格 + '.' + 可选空格」初步切分;</li> 233 * <li>对以下情况进行片段合并直至遇到闭合符号: 234 * <ul> 235 * <li>单引号字符串:'...'</li> 236 * <li>双引号定界标识符:"..."</li> 237 * <li>反引号定界标识符:`...`</li> 238 * <li>方括号定界标识符:[...]</li> 239 * <li>函数/表达式括号:(...)</li> 240 * </ul> 241 * </li> 242 * <li>BigQuery(vendor == dbvbigquery)且包含反引号时,会先去除反引号再进行切分。</li> 243 * <li>入参为 null 返回空列表;发生异常时返回仅包含原始字符串的列表。</li> 244 * </ol> 245 * 示例: 246 * <pre> 247 * "dbo.Employee.Name" -> ["dbo", "Employee", "Name"] 248 * "[Sales DB].[Employee].[Name]" -> ["[Sales DB]", "[Employee]", "[Name]"] 249 * "\"My.Schema\".\"My.Table\"" -> ["\"My.Schema\"", "\"My.Table\""] 250 * "`project.dataset.table`" (BigQuery) -> ["project", "dataset", "table"] 251 * "OPENJSON(aptd.test.ActiviteTypeIDs)" -> ["OPENJSON(aptd.test.ActiviteTypeIDs)"] 252 * </pre> 253 * 254 * @param nameString 待解析的标识符或表达式字符串 255 * @param vendor 数据库厂商(用于处理厂商特定定界符,如 BigQuery 的反引号),可为 null 256 * @return 解析后的片段列表;顺序与层级一致 257 */ 258 public static List<String> parseNames(String nameString, EDbVendor vendor) { 259 if(nameString == null){ 260 return Collections.emptyList(); 261 } 262 263 // Cache lookup - use a simple cache key 264 String cacheKey = vendor == null ? nameString : (vendor.ordinal() + ":" + nameString); 265 List<String> cached = PARSE_NAMES_CACHE.get(cacheKey); 266 if (cached != null) { 267 return new ArrayList<String>(cached); 268 } 269 270 String name = nameString.trim(); 271 272 // Fast path: simple names without special characters 273 // Check once for all special characters in a single pass 274 boolean hasSpecialChar = false; 275 boolean hasSingleQuote = false; 276 boolean hasDoubleQuote = false; 277 boolean hasBacktick = false; 278 boolean hasParenthesis = false; 279 boolean hasBracket = false; 280 281 for (int i = 0, len = name.length(); i < len; i++) { 282 char c = name.charAt(i); 283 switch (c) { 284 case '\'': 285 hasSingleQuote = true; 286 hasSpecialChar = true; 287 break; 288 case '"': 289 hasDoubleQuote = true; 290 hasSpecialChar = true; 291 break; 292 case '`': 293 hasBacktick = true; 294 hasSpecialChar = true; 295 break; 296 case '(': 297 case ')': 298 hasParenthesis = true; 299 hasSpecialChar = true; 300 break; 301 case '[': 302 case ']': 303 hasBracket = true; 304 hasSpecialChar = true; 305 break; 306 } 307 } 308 309 List<String> names = new ArrayList<String>(4); // Pre-size for typical case 310 311 // Handle BigQuery special case 312 if (vendor == EDbVendor.dbvbigquery && hasBacktick) { 313 String[] parts = DOT_PATTERN.split(name.replace("`", "")); 314 for (String part : parts) { 315 names.add(part); 316 } 317 putCache(cacheKey, names); 318 return names; 319 } 320 321 try { 322 String[] splits = DOT_PATTERN.split(nameString); 323 324 // Fast path: no special characters 325 if (!hasSpecialChar) { 326 for (String split : splits) { 327 names.add(split); 328 } 329 putCache(cacheKey, names); 330 return names; 331 } 332 333 // Handle special character cases 334 if (hasSingleQuote) { 335 if ("'.'".equals(name)) { 336 names = Arrays.asList("."); 337 putCache(cacheKey, names); 338 return names; 339 } 340 parseWithDelimiter(splits, names, '\'', '\''); 341 } else if (hasParenthesis) { 342 parseWithParenthesis(splits, names); 343 } else if (hasBacktick) { 344 if ("`.`".equals(name)) { 345 names = Arrays.asList("."); 346 putCache(cacheKey, names); 347 return names; 348 } 349 parseWithDelimiter(splits, names, '`', '`'); 350 } else if (hasDoubleQuote) { 351 if ("\".\"".equals(name)) { 352 names = Arrays.asList("."); 353 putCache(cacheKey, names); 354 return names; 355 } 356 parseWithDelimiter(splits, names, '"', '"'); 357 } else if (hasBracket) { 358 if ("[.]".equals(name)) { 359 names = Arrays.asList("."); 360 putCache(cacheKey, names); 361 return names; 362 } 363 parseWithDelimiter(splits, names, '[', ']'); 364 } else { 365 for (String split : splits) { 366 names.add(split); 367 } 368 } 369 } catch (Throwable e) { 370 names.clear(); 371 names.add(nameString); 372 } 373 374 putCache(cacheKey, names); 375 return names; 376 } 377 378 protected static List<String> putCache(String cacheKey, List<String> names) { 379 return PARSE_NAMES_CACHE.put(cacheKey, new ArrayList<String>(names)); 380 } 381 382 /** 383 * Helper method to parse name segments with paired delimiters (quotes, brackets, etc.) 384 */ 385 private static void parseWithDelimiter(String[] splits, List<String> names, char startDelim, char endDelim) { 386 for (int i = 0; i < splits.length; i++) { 387 String split = splits[i].trim(); 388 if (split.length() > 0 && split.charAt(0) == startDelim && 389 (split.length() == 1 || split.charAt(split.length() - 1) != endDelim)) { 390 // Found start delimiter without matching end - need to merge segments 391 StringBuilder buffer = new StringBuilder(split.length() * 2); 392 buffer.append(splits[i]); 393 394 while (i < splits.length - 1) { 395 split = splits[++i].trim(); 396 buffer.append(".").append(splits[i]); 397 if (split.length() > 0 && split.charAt(split.length() - 1) == endDelim) { 398 break; 399 } 400 } 401 names.add(buffer.toString()); 402 } else if (startDelim == endDelim && countUnenclosedChar(split, startDelim) % 2 == 1) { 403 // The delimiter opens in the middle of the segment, e.g. the 404 // expression SCHEMA_NAME(id) + '.' + st.name splits into 405 // "SCHEMA_NAME(id) + '" / "' + st" / "name". Merge segments 406 // until the delimiters are balanced, otherwise the dot inside 407 // the quoted literal produces unterminated fragments such as 408 // "' + st.name" (MantisBT issue 4496). 409 StringBuilder buffer = new StringBuilder(split.length() * 2); 410 buffer.append(splits[i]); 411 while (i < splits.length - 1 && countUnenclosedChar(buffer.toString(), startDelim) % 2 == 1) { 412 buffer.append(".").append(splits[++i]); 413 } 414 names.add(buffer.toString()); 415 } else { 416 names.add(splits[i]); 417 } 418 } 419 } 420 421 /** 422 * Counts occurrences of the given delimiter that are not enclosed in a 423 * different delimited-identifier region, so the apostrophe in 424 * [O'Reilly] or "O'Brien" is not mistaken for an opening string literal. 425 */ 426 private static int countUnenclosedChar(String s, char delim) { 427 int count = 0; 428 char enclosing = 0; 429 for (int i = 0, len = s.length(); i < len; i++) { 430 char c = s.charAt(i); 431 if (enclosing != 0) { 432 if (c == (enclosing == '[' ? ']' : enclosing)) { 433 enclosing = 0; 434 } 435 } else if (c == delim) { 436 count++; 437 } else if (c == '\'' || c == '"' || c == '`' || c == '[') { 438 enclosing = c; 439 } 440 } 441 return count; 442 } 443 444 /** 445 * Helper method to parse name segments with parentheses 446 */ 447 private static void parseWithParenthesis(String[] splits, List<String> names) { 448 for (int i = 0; i < splits.length; i++) { 449 String split = splits[i].trim(); 450 int openParen = split.indexOf('('); 451 int closeParen = split.indexOf(')'); 452 453 if (openParen != -1 && closeParen == -1) { 454 // Found opening parenthesis without closing - need to merge segments 455 StringBuilder buffer = new StringBuilder(split.length() * 2); 456 buffer.append(splits[i]); 457 458 while (i < splits.length - 1) { 459 split = splits[++i].trim(); 460 buffer.append(".").append(splits[i]); 461 if (split.indexOf(')') != -1) { 462 break; 463 } 464 } 465 names.add(buffer.toString()); 466 } else { 467 names.add(splits[i]); 468 } 469 } 470 } 471 472 public static void main(String[] args) { 473 SQLUtil.parseNames("OPENJSON(aptd.test.ActiviteTypeIDs)"); 474 } 475 476 public static void writeToFile(File file, InputStream source, boolean close) { 477 BufferedInputStream bis = null; 478 BufferedOutputStream fouts = null; 479 try { 480 bis = new BufferedInputStream(source); 481 if (!file.exists()) { 482 if (!file.getParentFile().exists()) { 483 file.getParentFile().mkdirs(); 484 } 485 file.createNewFile(); 486 } 487 fouts = new BufferedOutputStream(new FileOutputStream(file)); 488 byte b[] = new byte[1024]; 489 int i = 0; 490 while ((i = bis.read(b)) != -1) { 491 fouts.write(b, 0, i); 492 } 493 fouts.flush(); 494 fouts.close(); 495 if (close) 496 bis.close(); 497 } catch (IOException e) { 498 logger.error("Write file failed.", e); 499 try { 500 if (fouts != null) 501 fouts.close(); 502 } catch (IOException f) { 503 logger.error("Close output stream failed.", f); 504 } 505 if (close) { 506 try { 507 if (bis != null) 508 bis.close(); 509 } catch (IOException f) { 510 logger.error("Close input stream failed.", f); 511 } 512 } 513 } 514 } 515 516 public static void writeToFile(File file, String string) throws IOException { 517 518 if (!file.exists()) { 519 if (!file.getParentFile().exists()) { 520 file.getParentFile().mkdirs(); 521 } 522 file.createNewFile(); 523 } 524 PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file))); 525 if (string != null) 526 out.print(string); 527 out.close(); 528 } 529 530 public static void appendToFile(File file, String string) throws IOException { 531 532 if (!file.exists()) { 533 if (!file.getParentFile().exists()) { 534 file.getParentFile().mkdirs(); 535 } 536 file.createNewFile(); 537 } 538 PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file, true))); 539 if (string != null) 540 out.println(string); 541 out.close(); 542 } 543 544 public static void deltree(File root) { 545 if (root == null || !root.exists()) { 546 return; 547 } 548 549 if (root.isFile()) { 550 root.delete(); 551 return; 552 } 553 554 File[] children = root.listFiles(); 555 if (children != null) { 556 for (int i = 0; i < children.length; i++) { 557 deltree(children[i]); 558 } 559 } 560 561 root.delete(); 562 } 563 564 public static InputStream getInputStreamWithoutBom(String file) throws IOException { 565 UnicodeInputStream stream = null; 566 FileInputStream fis = new FileInputStream(file); 567 stream = new UnicodeInputStream(fis, null); 568 return stream; 569 } 570 571 public static class UnicodeInputStream extends InputStream { 572 PushbackInputStream internalIn; 573 boolean isInited = false; 574 String defaultEnc; 575 String encoding; 576 577 private static final int BOM_SIZE = 4; 578 579 public UnicodeInputStream(InputStream in, String defaultEnc) { 580 internalIn = new PushbackInputStream(in, BOM_SIZE); 581 this.defaultEnc = defaultEnc; 582 } 583 584 public String getDefaultEncoding() { 585 return defaultEnc; 586 } 587 588 public String getEncoding() { 589 if (!isInited) { 590 try { 591 init(); 592 } catch (IOException ex) { 593 IllegalStateException ise = new IllegalStateException("Init method failed."); 594 ise.initCause(ise); 595 throw ise; 596 } 597 } 598 return encoding; 599 } 600 601 protected void init() throws IOException { 602 if (isInited) 603 return; 604 605 byte bom[] = new byte[BOM_SIZE]; 606 int n, unread; 607 n = internalIn.read(bom, 0, bom.length); 608 609 if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) && (bom[2] == (byte) 0xFE) 610 && (bom[3] == (byte) 0xFF)) { 611 encoding = "UTF-32BE"; 612 unread = n - 4; 613 } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00) 614 && (bom[3] == (byte) 0x00)) { 615 encoding = "UTF-32LE"; 616 unread = n - 4; 617 } else if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) { 618 encoding = "UTF-8"; 619 unread = n - 3; 620 } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) { 621 encoding = "UTF-16BE"; 622 unread = n - 2; 623 } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) { 624 encoding = "UTF-16LE"; 625 unread = n - 2; 626 } else { 627 encoding = defaultEnc; 628 unread = n; 629 } 630 631 if (unread > 0) 632 internalIn.unread(bom, (n - unread), unread); 633 634 isInited = true; 635 } 636 637 public void close() throws IOException { 638 internalIn.close(); 639 } 640 641 public int read() throws IOException { 642 return internalIn.read(); 643 } 644 } 645 646 public static boolean compareIdentifier(EDbVendor dbVendor, ESQLDataObjectType sqlDataObjectType, 647 String identifier1, String identifier2) { 648 List<String> segments1 = parseNames(identifier1); 649 List<String> segments2 = parseNames(identifier2); 650 651 if (segments1.size() != segments2.size()) 652 return false; 653 654 boolean supportCatalog = TSQLEnv.supportCatalog(dbVendor); 655 boolean supportSchema = TSQLEnv.supportSchema(dbVendor); 656 657 if(supportCatalog && supportSchema) { 658 if (sqlDataObjectType == ESQLDataObjectType.dotColumn) { 659 if (segments1.size() > 4) { 660 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments1.get(0), 661 segments2.get(0)) 662 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments1.get(1), 663 segments2.get(1)) 664 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments1.get(2), 665 segments2.get(2)) 666 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, mergeSegments(segments1, 3), 667 mergeSegments(segments2, 3)); 668 } else if (segments1.size() == 4) { 669 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments1.get(0), 670 segments2.get(0)) 671 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments1.get(1), 672 segments2.get(1)) 673 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments1.get(2), 674 segments2.get(2)) 675 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments1.get(3), 676 segments2.get(3)); 677 } else if (segments1.size() == 3) { 678 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments1.get(0), 679 segments2.get(0)) 680 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments1.get(1), 681 segments2.get(1)) 682 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments1.get(2), 683 segments2.get(2)); 684 } else if (segments1.size() == 2) { 685 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments1.get(0), 686 segments2.get(0)) 687 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments1.get(1), 688 segments2.get(1)); 689 } else if (segments1.size() == 1) { 690 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments1.get(0), 691 segments2.get(0)); 692 } 693 } else if (sqlDataObjectType == ESQLDataObjectType.dotTable 694 || sqlDataObjectType == ESQLDataObjectType.dotSynonyms 695 || sqlDataObjectType == ESQLDataObjectType.dotOraclePackage 696 || sqlDataObjectType == ESQLDataObjectType.dotFunction 697 || sqlDataObjectType == ESQLDataObjectType.dotProcedure 698 || sqlDataObjectType == ESQLDataObjectType.dotTrigger) { 699 if (segments1.size() > 3) { 700 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments1.get(0), 701 segments2.get(0)) 702 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments1.get(1), 703 segments2.get(1)) 704 && TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, mergeSegments(segments1, 2), mergeSegments(segments2, 2)); 705 } else if (segments1.size() == 3) { 706 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments1.get(0), 707 segments2.get(0)) 708 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments1.get(1), 709 segments2.get(1)) 710 && TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, segments1.get(2), segments2.get(2)); 711 } else if (segments1.size() == 2) { 712 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments1.get(0), 713 segments2.get(0)) 714 && TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, segments1.get(1), segments2.get(1)); 715 } else if (segments1.size() == 1) { 716 return TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, segments1.get(0), segments2.get(0)); 717 } 718 } else if (sqlDataObjectType == ESQLDataObjectType.dotSchema) { 719 if (segments1.size() > 2) { 720 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments1.get(0), 721 segments2.get(0)) 722 && TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, mergeSegments(segments1, 1), mergeSegments(segments2, 1)); 723 } else if (segments1.size() == 2) { 724 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments1.get(0), 725 segments2.get(0)) 726 && TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, segments1.get(1), segments2.get(1)); 727 } else if (segments1.size() == 1) { 728 return TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, segments1.get(0), segments2.get(0)); 729 } 730 } else if (sqlDataObjectType == ESQLDataObjectType.dotCatalog) { 731 if (segments1.size() > 1) { 732 return TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, mergeSegments(segments1, 0), mergeSegments(segments2, 0)); 733 } else if (segments1.size() == 1) { 734 return TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, segments1.get(0), segments2.get(0)); 735 } 736 } 737 } 738 else if(supportCatalog){ 739 if (sqlDataObjectType == ESQLDataObjectType.dotColumn) { 740 if (segments1.size() > 3) { 741 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments1.get(0), 742 segments2.get(0)) 743 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments1.get(1), 744 segments2.get(1)) 745 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, mergeSegments(segments1, 2), 746 mergeSegments(segments2, 2)); 747 } else if (segments1.size() == 3) { 748 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments1.get(0), 749 segments2.get(0)) 750 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments1.get(1), 751 segments2.get(1)) 752 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments1.get(2), 753 segments2.get(2)); 754 } else if (segments1.size() == 2) { 755 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments1.get(0), 756 segments2.get(0)) 757 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments1.get(1), 758 segments2.get(1)); 759 } else if (segments1.size() == 1) { 760 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments1.get(0), 761 segments2.get(0)); 762 } 763 } else if (sqlDataObjectType == ESQLDataObjectType.dotTable 764 || sqlDataObjectType == ESQLDataObjectType.dotSynonyms 765 || sqlDataObjectType == ESQLDataObjectType.dotOraclePackage 766 || sqlDataObjectType == ESQLDataObjectType.dotFunction 767 || sqlDataObjectType == ESQLDataObjectType.dotProcedure 768 || sqlDataObjectType == ESQLDataObjectType.dotTrigger) { 769 if (segments1.size() > 2) { 770 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments1.get(0), 771 segments2.get(0)) 772 && TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, mergeSegments(segments1, 1), mergeSegments(segments2, 1)); 773 } else if (segments1.size() == 2) { 774 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments1.get(0), 775 segments2.get(0)) 776 && TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, segments1.get(1), segments2.get(1)); 777 } else if (segments1.size() == 1) { 778 return TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, segments1.get(0), segments2.get(0)); 779 } 780 } else if (sqlDataObjectType == ESQLDataObjectType.dotCatalog || sqlDataObjectType == ESQLDataObjectType.dotSchema) { 781 if (segments1.size() > 1) { 782 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, mergeSegments(segments1, 0), mergeSegments(segments2, 0)); 783 } else if (segments1.size() == 1) { 784 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments1.get(0), segments2.get(0)); 785 } 786 } 787 } 788 else if(supportSchema){ 789 if (sqlDataObjectType == ESQLDataObjectType.dotColumn) { 790 if (segments1.size() > 3) { 791 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments1.get(0), 792 segments2.get(0)) 793 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments1.get(1), 794 segments2.get(1)) 795 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, mergeSegments(segments1, 2), 796 mergeSegments(segments2, 2)); 797 } else if (segments1.size() == 3) { 798 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments1.get(0), 799 segments2.get(0)) 800 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments1.get(1), 801 segments2.get(1)) 802 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments1.get(2), 803 segments2.get(2)); 804 } else if (segments1.size() == 2) { 805 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments1.get(0), 806 segments2.get(0)) 807 && TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments1.get(1), 808 segments2.get(1)); 809 } else if (segments1.size() == 1) { 810 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments1.get(0), 811 segments2.get(0)); 812 } 813 } else if (sqlDataObjectType == ESQLDataObjectType.dotTable 814 || sqlDataObjectType == ESQLDataObjectType.dotSynonyms 815 || sqlDataObjectType == ESQLDataObjectType.dotOraclePackage 816 || sqlDataObjectType == ESQLDataObjectType.dotFunction 817 || sqlDataObjectType == ESQLDataObjectType.dotProcedure 818 || sqlDataObjectType == ESQLDataObjectType.dotTrigger) { 819 if (segments1.size() > 2) { 820 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments1.get(0), 821 segments2.get(0)) 822 && TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, mergeSegments(segments1, 1), mergeSegments(segments2, 1)); 823 } else if (segments1.size() == 2) { 824 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments1.get(0), 825 segments2.get(0)) 826 && TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, segments1.get(1), segments2.get(1)); 827 } else if (segments1.size() == 1) { 828 return TSQLEnv.compareIdentifier(dbVendor, sqlDataObjectType, segments1.get(0), segments2.get(0)); 829 } 830 } else if (sqlDataObjectType == ESQLDataObjectType.dotCatalog || sqlDataObjectType == ESQLDataObjectType.dotSchema) { 831 if (segments1.size() > 1) { 832 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotSchema, mergeSegments(segments1, 0), mergeSegments(segments2, 0)); 833 } else if (segments1.size() == 1) { 834 return TSQLEnv.compareIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments1.get(0), segments2.get(0)); 835 } 836 } 837 } 838 return false; 839 } 840 841 public static String normalizeIdentifier(EDbVendor dbVendor, ESQLDataObjectType objectType, String identifier) { 842 if (identifier == null) 843 return null; 844 String originIdentifier = identifier; 845 identifier = IdentifierService.normalizeStatic(dbVendor, objectType, identifier); 846 boolean collationSensitive = false; 847 switch (objectType) { 848 case dotCatalog: 849 case dotSchema: 850 collationSensitive = TSQLEnv.catalogCollationCaseSensitive.get(dbVendor); 851 break; 852 case dotFunction: 853 case dotProcedure: 854 case dotTrigger: 855 case dotTable: 856 case dotSynonyms: 857 case dotOraclePackage: 858 collationSensitive = TSQLEnv.tableCollationCaseSensitive.get(dbVendor); 859 break; 860 case dotColumn: 861 collationSensitive = TSQLEnv.columnCollationCaseSensitive.get(dbVendor); 862 break; 863 default: 864 collationSensitive = TSQLEnv.defaultCollationCaseSensitive.get(dbVendor); 865 break; 866 } 867 if(parseNames(identifier).size()>1) { 868 return collationSensitive ? originIdentifier : originIdentifier.toUpperCase(); 869 } 870 else { 871 return collationSensitive ? identifier : identifier.toUpperCase(); 872 } 873 } 874 875 public static String getIdentifierNormalColumnName(EDbVendor dbVendor, String name) { 876 return getIdentifierNormalName(dbVendor, name, ESQLDataObjectType.dotColumn); 877 } 878 879 public static String getIdentifierNormalTableName(String name) { 880 return getIdentifierNormalName(ModelBindingManager.getGlobalVendor(), name, ESQLDataObjectType.dotTable); 881 } 882 883 public static String getIdentifierNormalTableName(EDbVendor dbVendor, String name) { 884 return getIdentifierNormalName(dbVendor, name, ESQLDataObjectType.dotTable); 885 } 886 887 /** 888 * 规范化多段限定名(Multi-Segment Qualified Name)并返回规范化后的完整限定名。 889 * 890 * <h3>核心功能</h3> 891 * <p>本方法是 SQL 标识符规范化的<b>多段名处理版本</b>,接受包含多段(catalog.schema.table.column)的限定名, 892 * 根据数据库厂商特性、层级支持能力和对象类型,智能解析和规范化每个段,最后返回用点号连接的完整规范化名称。</p> 893 * 894 * <h3>处理流程</h3> 895 * <ol> 896 * <li><b>厂商特定语法展开</b>: 897 * <ul> 898 * <li>MSSQL/Azure SQL: 将 ".." 语法展开为 ".dbo." 或全局配置的 schema</li> 899 * <li>示例:{@code "db..table"} → {@code "db.dbo.table"}</li> 900 * </ul> 901 * </li> 902 * <li><b>解析多段名</b>:使用 {@link #parseNames(String)} 将限定名按点号分割成段列表</li> 903 * <li><b>智能段类型推断</b>:根据以下因素决定每段的实际类型: 904 * <ul> 905 * <li>数据库厂商的层级支持能力(supportCatalog/supportSchema)</li> 906 * <li>目标对象类型({@code sqlDataObjectType})</li> 907 * <li>实际段数</li> 908 * </ul> 909 * </li> 910 * <li><b>逐段规范化</b>:对每段调用 {@link #normalizeIdentifier(EDbVendor, ESQLDataObjectType, String)}, 911 * 应用厂商特定的大小写规则、引号处理等</li> 912 * <li><b>重组限定名</b>:用点号连接所有规范化后的段,返回完整的规范化限定名</li> 913 * </ol> 914 * 915 * <h3>智能段类型推断示例</h3> 916 * <p>假设数据库<b>同时支持 catalog 和 schema</b>,目标类型为 {@code dotTable}:</p> 917 * <ul> 918 * <li>3段名 {@code "a.b.c"} → 解析为 {@code catalog.schema.table}</li> 919 * <li>2段名 {@code "a.b"} → 解析为 {@code schema.table}</li> 920 * <li>1段名 {@code "a"} → 解析为 {@code table}</li> 921 * </ul> 922 * 923 * <p>假设数据库<b>仅支持 catalog</b>(如 MySQL),目标类型为 {@code dotTable}:</p> 924 * <ul> 925 * <li>2段名 {@code "a.b"} → 解析为 {@code catalog.table}</li> 926 * <li>1段名 {@code "a"} → 解析为 {@code table}</li> 927 * </ul> 928 * 929 * <p>假设数据库<b>仅支持 schema</b>(如 PostgreSQL),目标类型为 {@code dotTable}:</p> 930 * <ul> 931 * <li>2段名 {@code "a.b"} → 解析为 {@code schema.table}</li> 932 * <li>1段名 {@code "a"} → 解析为 {@code table}</li> 933 * </ul> 934 * 935 * <h3>对于 dotColumn 类型的特殊处理</h3> 936 * <p>列名可能有4段(catalog.schema.table.column),方法会根据实际段数自动调整解析策略:</p> 937 * <ul> 938 * <li>4段名 {@code "db.sch.tbl.col"} → {@code catalog.schema.table.column}</li> 939 * <li>3段名 {@code "sch.tbl.col"} → {@code schema.table.column}</li> 940 * <li>2段名 {@code "tbl.col"} → {@code table.column}</li> 941 * <li>1段名 {@code "col"} → {@code column}</li> 942 * </ul> 943 * 944 * <h3>处理超长限定名</h3> 945 * <p>当段数超过标准层级时(如表名有4段或更多),方法会使用 {@link #mergeSegments(List, int)} 946 * 将多余的尾部段合并为单个段(保留点号),然后作为最后一段进行规范化。</p> 947 * <p>示例:5段列名 {@code "db.sch.tbl.nested.col"} → 合并后4段处理: 948 * {@code "db.sch.tbl.nested.col"} (最后两段合并为 {@code "nested.col"})</p> 949 * 950 * <h3>方法目的与使用场景</h3> 951 * <ul> 952 * <li><b>目的</b>:为多段限定名提供统一的规范化接口,生成可用于比较、索引和查找的标准化名称</li> 953 * <li><b>输出特性</b>:返回<b>多段名</b>(用点号连接),保留层级结构信息</li> 954 * <li><b>使用场景</b>: 955 * <ul> 956 * <li>环境层(TSQLEnv)处理完整限定名</li> 957 * <li>名称比较和匹配(需要保留层级信息)</li> 958 * <li>快速索引查找(NameKey 构造)</li> 959 * </ul> 960 * </li> 961 * </ul> 962 * 963 * <h3>与相关方法的区别</h3> 964 * <ul> 965 * <li><b>{@link #normalizeIdentifier(EDbVendor, ESQLDataObjectType, String)}</b>: 966 * 单段规范化,不解析限定名,直接处理引号和大小写,返回单段名</li> 967 * <li><b>{@code IdentifierService.keyForMap(String, ESQLDataObjectType)}</b>: 968 * 仅接受单段名,用于生成 Map 的 key,会抛出异常如果输入是多段名</li> 969 * <li><b>{@code IdentifierService.normalizeQualifiedName(String, ESQLDataObjectType)}</b>: 970 * 新架构中的等价方法,提供相同的多段名规范化功能</li> 971 * </ul> 972 * 973 * <h3>厂商特定行为</h3> 974 * <ul> 975 * <li><b>MySQL</b>: 仅支持 catalog(即 database),反引号引用,大小写根据系统变量决定</li> 976 * <li><b>PostgreSQL</b>: 仅支持 schema,双引号引用,未加引号的标识符转小写</li> 977 * <li><b>SQL Server</b>: 支持 catalog+schema,方括号或双引号引用,".." 展开为 ".dbo."</li> 978 * <li><b>Oracle</b>: 仅支持 schema,双引号引用,未加引号的标识符转大写</li> 979 * <li><b>Snowflake</b>: 支持 catalog+schema,双引号引用,大小写保留但匹配不敏感</li> 980 * <li><b>BigQuery</b>: 支持 catalog+schema(项目+数据集),反引号引用,大小写不敏感,可能内部转小写</li> 981 * </ul> 982 * 983 * @param dbVendor 数据库厂商类型(决定层级支持、引号风格、大小写规则) 984 * @param name 原始标识符或多段限定名(可能包含引号/反引号;可能包含 catalog/schema 前缀; 985 * 可能包含厂商特定语法如 MSSQL 的 "..") 986 * @param sqlDataObjectType 期望的对象类型(例如 dotTable, dotColumn, dotSchema, dotCatalog), 987 * 用于指导段类型推断和规范化规则应用 988 * @return 规范化后的完整限定名,用点号连接各段;如果输入为 {@code null},返回 {@code null}; 989 * 如果输入为空字符串或无法解析,返回空字符串 990 * 991 * @see #normalizeIdentifier(EDbVendor, ESQLDataObjectType, String) 单段规范化方法 992 * @see #parseNames(String) 多段名解析方法 993 * @see #mergeSegments(List, int) 段合并工具方法 994 * @see TSQLEnv#normalizeIdentifier(EDbVendor, ESQLDataObjectType, String) 995 * @see IdentifierService#normalizeQualifiedName(String, ESQLDataObjectType) Phase 0 新架构中的等价方法 996 * 997 * @since 3.1.0.8 998 */ 999 public static String getIdentifierNormalName(EDbVendor dbVendor, String name, 1000 ESQLDataObjectType sqlDataObjectType) { 1001 if (name == null) { 1002 return null; 1003 } 1004 if (dbVendor == EDbVendor.dbvmssql || dbVendor == EDbVendor.dbvazuresql) { 1005 if (name.indexOf("..") != -1) { 1006 if (ModelBindingManager.getGlobalSchema() != null) { 1007 name = name.replace("..", "." + ModelBindingManager.getGlobalSchema() + "."); 1008 } else { 1009 name = name.replace("..", ".dbo."); 1010 } 1011 } 1012 } 1013 List<String> segments = parseNames(name); 1014 StringBuilder builder = new StringBuilder(); 1015 boolean supportCatalog = TSQLEnv.supportCatalog(dbVendor); 1016 boolean supportSchema = TSQLEnv.supportSchema(dbVendor); 1017 1018 if(supportCatalog && supportSchema) { 1019 if (sqlDataObjectType == ESQLDataObjectType.dotColumn) { 1020 if (segments.size() > 4) { 1021 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments.get(0))) 1022 .append(".") 1023 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments.get(1))) 1024 .append(".") 1025 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments.get(2))) 1026 .append(".") 1027 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, mergeSegments(segments, 3))); 1028 } else if (segments.size() == 4) { 1029 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments.get(0))) 1030 .append(".") 1031 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments.get(1))) 1032 .append(".") 1033 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments.get(2))) 1034 .append(".") 1035 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments.get(3))); 1036 } else if (segments.size() == 3) { 1037 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments.get(0))) 1038 .append(".") 1039 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments.get(1))) 1040 .append(".") 1041 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments.get(2))); 1042 } else if (segments.size() == 2) { 1043 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments.get(0))) 1044 .append(".") 1045 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments.get(1))); 1046 } else if (segments.size() == 1) { 1047 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments.get(0))); 1048 } 1049 } else if (sqlDataObjectType == ESQLDataObjectType.dotTable 1050 || sqlDataObjectType == ESQLDataObjectType.dotSynonyms 1051 || sqlDataObjectType == ESQLDataObjectType.dotOraclePackage 1052 || sqlDataObjectType == ESQLDataObjectType.dotFunction 1053 || sqlDataObjectType == ESQLDataObjectType.dotProcedure 1054 || sqlDataObjectType == ESQLDataObjectType.dotTrigger) { 1055 if (segments.size() > 3) { 1056 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments.get(0))) 1057 .append(".") 1058 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments.get(1))) 1059 .append(".").append(normalizeIdentifier(dbVendor, sqlDataObjectType, mergeSegments(segments, 2))); 1060 } else if (segments.size() == 3) { 1061 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments.get(0))) 1062 .append(".") 1063 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments.get(1))) 1064 .append(".").append(normalizeIdentifier(dbVendor, sqlDataObjectType, segments.get(2))); 1065 } else if (segments.size() == 2) { 1066 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments.get(0))) 1067 .append(".").append(normalizeIdentifier(dbVendor, sqlDataObjectType, segments.get(1))); 1068 } else if (segments.size() == 1) { 1069 builder.append(normalizeIdentifier(dbVendor, sqlDataObjectType, segments.get(0))); 1070 } 1071 } else if (sqlDataObjectType == ESQLDataObjectType.dotSchema) { 1072 if (segments.size() > 2) { 1073 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments.get(0))) 1074 .append(".").append(normalizeIdentifier(dbVendor, sqlDataObjectType, mergeSegments(segments, 1))); 1075 } else if (segments.size() == 2) { 1076 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments.get(0))) 1077 .append(".").append(normalizeIdentifier(dbVendor, sqlDataObjectType, segments.get(1))); 1078 } else if (segments.size() == 1) { 1079 builder.append(normalizeIdentifier(dbVendor, sqlDataObjectType, segments.get(0))); 1080 } 1081 } else if (sqlDataObjectType == ESQLDataObjectType.dotCatalog) { 1082 if (segments.size() > 1) { 1083 builder.append(normalizeIdentifier(dbVendor, sqlDataObjectType, mergeSegments(segments, 0))); 1084 } else if (segments.size() == 1) { 1085 builder.append(normalizeIdentifier(dbVendor, sqlDataObjectType, segments.get(0))); 1086 } 1087 } 1088 } 1089 else if(supportCatalog){ 1090 if (sqlDataObjectType == ESQLDataObjectType.dotColumn) { 1091 if (segments.size() > 3) { 1092 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments.get(0))) 1093 .append(".") 1094 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments.get(1))) 1095 .append(".") 1096 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, mergeSegments(segments, 2))); 1097 } else if (segments.size() == 3) { 1098 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments.get(0))) 1099 .append(".") 1100 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments.get(1))) 1101 .append(".") 1102 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments.get(2))); 1103 } else if (segments.size() == 2) { 1104 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments.get(0))) 1105 .append(".") 1106 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments.get(1))); 1107 } else if (segments.size() == 1) { 1108 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments.get(0))); 1109 } 1110 } else if (sqlDataObjectType == ESQLDataObjectType.dotTable 1111 || sqlDataObjectType == ESQLDataObjectType.dotSynonyms 1112 || sqlDataObjectType == ESQLDataObjectType.dotOraclePackage 1113 || sqlDataObjectType == ESQLDataObjectType.dotFunction 1114 || sqlDataObjectType == ESQLDataObjectType.dotProcedure 1115 || sqlDataObjectType == ESQLDataObjectType.dotTrigger) { 1116 if (segments.size() > 2) { 1117 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments.get(0))) 1118 .append(".").append(normalizeIdentifier(dbVendor, sqlDataObjectType, mergeSegments(segments, 1))); 1119 } else if (segments.size() == 2) { 1120 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments.get(0))) 1121 .append(".").append(normalizeIdentifier(dbVendor, sqlDataObjectType, segments.get(1))); 1122 } else if (segments.size() == 1) { 1123 builder.append(normalizeIdentifier(dbVendor, sqlDataObjectType, segments.get(0))); 1124 } 1125 } else if (sqlDataObjectType == ESQLDataObjectType.dotSchema || sqlDataObjectType == ESQLDataObjectType.dotCatalog) { 1126 if (segments.size() > 1) { 1127 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, mergeSegments(segments, 0))); 1128 } else if (segments.size() == 1) { 1129 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotCatalog, segments.get(0))); 1130 } 1131 } 1132 } 1133 else if(supportSchema){ 1134 if (sqlDataObjectType == ESQLDataObjectType.dotColumn) { 1135 if (segments.size() > 3) { 1136 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments.get(0))) 1137 .append(".") 1138 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments.get(1))) 1139 .append(".") 1140 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, mergeSegments(segments, 2))); 1141 } else if (segments.size() == 3) { 1142 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments.get(0))) 1143 .append(".") 1144 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments.get(1))) 1145 .append(".") 1146 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments.get(2))); 1147 } else if (segments.size() == 2) { 1148 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotTable, segments.get(0))) 1149 .append(".") 1150 .append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments.get(1))); 1151 } else if (segments.size() == 1) { 1152 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotColumn, segments.get(0))); 1153 } 1154 } else if (sqlDataObjectType == ESQLDataObjectType.dotTable 1155 || sqlDataObjectType == ESQLDataObjectType.dotSynonyms 1156 || sqlDataObjectType == ESQLDataObjectType.dotOraclePackage 1157 || sqlDataObjectType == ESQLDataObjectType.dotFunction 1158 || sqlDataObjectType == ESQLDataObjectType.dotProcedure 1159 || sqlDataObjectType == ESQLDataObjectType.dotTrigger) { 1160 if (segments.size() > 2) { 1161 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments.get(0))) 1162 .append(".").append(normalizeIdentifier(dbVendor, sqlDataObjectType, mergeSegments(segments, 1))); 1163 } else if (segments.size() == 2) { 1164 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments.get(0))) 1165 .append(".").append(normalizeIdentifier(dbVendor, sqlDataObjectType, segments.get(1))); 1166 } else if (segments.size() == 1) { 1167 builder.append(normalizeIdentifier(dbVendor, sqlDataObjectType, segments.get(0))); 1168 } 1169 } else if (sqlDataObjectType == ESQLDataObjectType.dotSchema || sqlDataObjectType == ESQLDataObjectType.dotCatalog) { 1170 if (segments.size() > 1) { 1171 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotSchema, mergeSegments(segments, 0))); 1172 } else if (segments.size() == 1) { 1173 builder.append(normalizeIdentifier(dbVendor, ESQLDataObjectType.dotSchema, segments.get(0))); 1174 } 1175 } 1176 } 1177 return builder.toString(); 1178 } 1179 1180 public static String mergeSegments(List<String> segments, int index) { 1181 StringBuilder buffer = new StringBuilder(); 1182 for (int i = index; i < segments.size(); i++) { 1183 buffer.append(segments.get(i)); 1184 if(i<segments.size()-1) { 1185 buffer.append("."); 1186 } 1187 } 1188 return buffer.toString(); 1189 } 1190 1191// public static String getIdentifierNormalName(EDbVendor vendor, String name) { 1192// if (isEmpty(name)) { 1193// return null; 1194// } 1195// name = name.replaceAll("(?i)null\\.", ""); 1196// switch (vendor) { 1197// case dbvbigquery: 1198// return replaceIdentifierNormalName(name, "`.*?`", "`", true).replaceAll("\\.\\s+", "."); 1199// case dbvcouchbase: 1200// case dbvhive: 1201// case dbvimpala: 1202// case dbvmysql: 1203// return replaceIdentifierNormalName(name, "`.*?`", "`", true).replaceAll("\\.\\s+", "."); 1204// case dbvdax: 1205// return replaceIdentifierNormalName(name, "'.*?'", "'", true).replaceAll("\\.\\s+", "."); 1206// case dbvdb2: 1207// case dbvhana: 1208// case dbvinformix: 1209// case dbvnetezza: 1210// case dbvoracle: 1211// case dbvsnowflake: 1212// case dbvsybase: 1213// case dbvteradata: 1214// case dbvvertica: 1215// return replaceIdentifierNormalName(name, "\".*?\"", "\"", true).replaceAll("\\.\\s+", "."); 1216// case dbvpostgresql: 1217// case dbvgreenplum: 1218// case dbvredshift: 1219// return replaceIdentifierNormalName(name, "\".*?\"", "\"", false).replaceAll("\\.\\s+", "."); 1220// case dbvmssql: 1221// return replaceIdentifierNormalName(name, "([\"\\[']).*?([\"\\]'])", "[\"\\[\\]']", true) 1222// .replaceAll("\\.\\s+", "."); 1223// default: 1224// return replaceIdentifierNormalName(name, "\".*?\"", "\"", true).replaceAll("\\.\\s+", "."); 1225// } 1226// } 1227// 1228// private static String replaceIdentifierNormalName(String content, String match, String replace, boolean toUpper) { 1229// Pattern pattern = Pattern.compile(match, Pattern.CASE_INSENSITIVE | Pattern.DOTALL); 1230// Matcher matcher = pattern.matcher(content); 1231// StringBuilder buffer = new StringBuilder(); 1232// int start = 0; 1233// while (matcher.find()) { 1234// int matchStart = matcher.start(); 1235// int macthEnd = matcher.end(); 1236// if (start < matchStart) { 1237// if (toUpper) { 1238// buffer.append(content.substring(start, matchStart).toUpperCase()); 1239// } else { 1240// buffer.append(content.substring(start, matchStart).toLowerCase()); 1241// } 1242// } 1243// buffer.append(matcher.group().replaceAll(replace, "")); 1244// start = macthEnd; 1245// } 1246// if (start < content.length()) { 1247// if (toUpper) { 1248// buffer.append(content.substring(start).toUpperCase()); 1249// } else { 1250// buffer.append(content.substring(start).toLowerCase()); 1251// } 1252// } 1253// return buffer.toString(); 1254// } 1255 1256 public static boolean isTempTable(table table) { 1257 if(SubType.temp_table.name().equals(table.getSubType())) { 1258 return true; 1259 } 1260 String tableName = table.getName(); 1261 List<String> segments = parseNames(tableName); 1262 if (tableName.startsWith("@") || segments.get(segments.size() - 1).startsWith("@")) { 1263 return true; 1264 } 1265 if (tableName.startsWith("#") || segments.get(segments.size() - 1).startsWith("#")) { 1266 return true; 1267 } 1268 return false; 1269 } 1270 1271// public static boolean isTempTable(String tableName) { 1272// List<String> segments = parseNames(tableName); 1273// if (tableName.startsWith("@") || segments.get(segments.size() - 1).startsWith("@")) { 1274// return true; 1275// } 1276// if (tableName.startsWith("#") || segments.get(segments.size() - 1).startsWith("#")) { 1277// return true; 1278// } 1279// return false; 1280// } 1281 1282// public static boolean isTempTable(TTable table, EDbVendor vendor) { 1283// switch (vendor) { 1284// case dbvmssql: 1285// return table.getName().startsWith("#"); 1286// default: 1287// return false; 1288// } 1289// } 1290 1291 public static File[] listFiles(File sqlFiles) { 1292 List<File> children = new ArrayList<File>(); 1293 if (sqlFiles != null) 1294 listFiles(sqlFiles, children); 1295 Collections.sort(children); 1296 return children.toArray(new File[0]); 1297 } 1298 1299 public static File[] listFiles(File sqlFiles, FileFilter filter) { 1300 List<File> children = new ArrayList<File>(); 1301 if (sqlFiles != null) 1302 listFiles(sqlFiles, children, filter); 1303 Collections.sort(children); 1304 return children.toArray(new File[0]); 1305 } 1306 1307 private static Set<String> extensions = new HashSet<String>(Arrays.asList("java", "class", "php", "c", "cpp", "properties", "iml", 1308 "yml", "xml", "md", "jpg", "png", "gif", "bmp", "tiff", "tif", "svg", "pdf", "mp3", "mp4", "bak", "jar", "gz", 1309 "tar", "log", "conf", "dll", "exe", "so", "sh", "bat", "cdr", "docx", "doc", "xps", "xlsx", "xls", "ppt", "pptx", 1310 "rar", "7z", "ttf", "caj", "dwg", "dwf", "dxf", "ico", "epub", "webp", "heic", "html", "htm", "vsd", "vsdx", "rtf", "ico")); 1311 1312 public static void listFiles(File rootFile, List<File> children) { 1313 if (rootFile.isFile()) 1314 children.add(rootFile); 1315 else { 1316 File[] files = rootFile.listFiles(t -> { 1317 int dotIndex = t.getName().lastIndexOf("."); 1318 if (dotIndex == -1) { 1319 return true; 1320 } 1321 String extension = t.getName().toLowerCase().substring(dotIndex + 1); 1322 return !extensions.contains(extension); 1323 }); 1324 if (files != null) { 1325 for (int i = 0; i < files.length; i++) { 1326 listFiles(files[i], children); 1327 } 1328 } 1329 } 1330 } 1331 1332 public static void listFiles(File rootFile, List<File> children, FileFilter filter) { 1333 if (rootFile.isFile() && filter.accept(rootFile)) 1334 children.add(rootFile); 1335 else { 1336 File[] files = rootFile.listFiles(filter); 1337 if (files != null) { 1338 for (int i = 0; i < files.length; i++) { 1339 listFiles(files[i], children, filter); 1340 } 1341 } 1342 } 1343 } 1344 1345 public static String readFile(File file) { 1346 StringBuilder stringBuilder = new StringBuilder(); 1347 BufferedReader reader = null; 1348 try { 1349 reader = new BufferedReader(new FileReader(file)); 1350 1351 String line = null; 1352 String ls = System.getProperty("line.separator"); 1353 while ((line = reader.readLine()) != null) { 1354 stringBuilder.append(line); 1355 stringBuilder.append(ls); 1356 } 1357 stringBuilder.deleteCharAt(stringBuilder.length() - 1); 1358 } catch (IOException e) { 1359 logger.error("read file failed.", e); 1360 } finally { 1361 if (reader != null) { 1362 try { 1363 reader.close(); 1364 } catch (IOException e) { 1365 logger.error("close reader failed.", e); 1366 } 1367 } 1368 } 1369 return stringBuilder.toString(); 1370 } 1371 1372 public static String stringToMD5(String plainText) { 1373 byte[] secretBytes = null; 1374 try { 1375 secretBytes = MessageDigest.getInstance("md5").digest(plainText.getBytes("UTF-8")); 1376 StringBuffer sb = new StringBuffer(); 1377 for (int i = 0; i < secretBytes.length; ++i) { 1378 sb.append(Integer.toHexString((secretBytes[i] & 0xFF) | 0x100).substring(1, 3)); 1379 } 1380 return sb.toString(); 1381 } catch (Exception e) { 1382 logger.error("get text md5 value failed.", e); 1383 } 1384 return null; 1385 } 1386 1387 public static String trimSingleQuote(String columnAlias) { 1388 if(columnAlias.startsWith("'") && columnAlias.endsWith("'")) { 1389 return columnAlias.substring(1, columnAlias.length() - 1); 1390 } 1391 return columnAlias; 1392 } 1393 1394 public static String endTrim(String input) { 1395 if (input == null) { 1396 return null; 1397 } 1398 1399 int end = input.length(); 1400 while (end > 0 && Character.isWhitespace(input.charAt(end - 1))) { 1401 end--; 1402 } 1403 1404 return input.substring(0, end); 1405 } 1406 1407 public static void endTrim(StringBuilder buffer) { 1408 int length = buffer.length(); 1409 while (length > 0 && Character.isWhitespace(buffer.charAt(length - 1))) { 1410 length--; // 逐步减少长度 1411 } 1412 buffer.setLength(length); // 直接截断尾部空白字符 1413 } 1414 1415 public static String joinNonEmpty(String... parts) { 1416 return Arrays.stream(parts) 1417 .filter(s -> s != null && !s.trim().isEmpty()) 1418 .collect(Collectors.joining("/")); 1419 } 1420}