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