001package gudusoft.gsqlparser.dlineage.dataflow.model.xml; 002 003import gudusoft.gsqlparser.dlineage.dataflow.model.ModelBindingManager; 004import gudusoft.gsqlparser.dlineage.dataflow.model.json.Coordinate; 005import gudusoft.gsqlparser.dlineage.util.DlineageUtil; 006import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType; 007import gudusoft.gsqlparser.sqlenv.TSQLEnv; 008import gudusoft.gsqlparser.util.SQLUtil; 009 010import javax.xml.bind.annotation.XmlAttribute; 011import javax.xml.bind.annotation.XmlElement; 012import javax.xml.bind.annotation.XmlTransient; 013import javax.xml.bind.annotation.XmlType; 014import java.util.ArrayList; 015import java.util.LinkedHashSet; 016import java.util.List; 017 018@XmlType( 019 propOrder = {"id", "server", "userName", "database", "schema", "name", "displayName", "alias", "uri", "type", "subType", 020 "processIds", "fileType", "fileFormat", "location", "namespace", "isTarget", "coordinate", "columns", "parent", "more", "fromDDL", "candidateTables", 021 "endpointKind", "endpointIntroduction", "createdInSql", "normalizedName"} 022) 023public class table implements Cloneable { 024 025 private String id; 026 027 private String server; 028 029 private String userName; 030 031 private String database; 032 033 private String schema; 034 035 private String name; 036 037 private String displayName; 038 039 private String alias; 040 041 private String type; 042 043 private String subType; 044 045 private String uri; 046 047 private List<String> processIds; 048 049 private String isTarget; 050 051 private StringBuffer coordinate = new StringBuffer(); 052 053 private List<column> columns; 054 055 private String parent; 056 057 private String fileType; 058 059 private String fileFormat; 060 061 private String location; 062 063 private String namespace; 064 065 @XmlTransient 066 private String starStmt; 067 068 private Boolean more; 069 070 @XmlTransient 071 private String isDetermined; 072 073 private String fromDDL; 074 075 /** 076 * Qualified candidate table names when this one-part reference is ambiguous 077 * across two or more schemas of the default catalog (e.g. {@code testdb.s1.orders}, 078 * {@code testdb.s2.orders}) and no object of that name exists in the default 079 * schema. Empty/absent for normally-resolved references. See 080 * docs/tmp/unqualified-name-default-schema-masks-cross-schema-ambiguity.md. 081 */ 082 private List<String> candidateTables; 083 084 /** 085 * Authoritative endpoint classification (additive). Carried as Strings so a null 086 * value is omitted from XML, keeping existing output byte-identical for endpoints 087 * that aren't classified. Populated from the internal model only when non-default. 088 * See docs/tmp/dlineage-authoritative-endpoint-classification.md. 089 */ 090 private String endpointKind; 091 092 private String endpointIntroduction; 093 094 private String createdInSql; 095 096 private String normalizedName; 097 098 @XmlTransient 099 private LinkedHashSet<String> coordinateItems = new LinkedHashSet<String>(); 100 101 @XmlAttribute(required = false) 102 public String getAlias() { 103 return alias; 104 } 105 106 public void setAlias(String alias) { 107 this.alias = alias; 108 } 109 110 @XmlElement(name = "column", required = false) 111 public List<column> getColumns() { 112 if (this.columns == null) { 113 this.columns = new ArrayList<column>(); 114 } 115 return columns; 116 } 117 118 public void setColumns(List<column> columns) { 119 this.columns = columns; 120 } 121 122 @XmlAttribute(required = false) 123 public String getCoordinate() { 124 String result = coordinate.toString(); 125 if (SQLUtil.isEmpty(result)) 126 return null; 127 return result; 128 } 129 130 public void appendCoordinate(String coordinate) { 131 if (!coordinateItems.contains(coordinate)) { 132 coordinateItems.add(coordinate); 133 rebuildCoordinate(); 134 } 135 } 136 137 private void rebuildCoordinate() { 138 this.coordinate.setLength(0); 139 140 List<String> itemsList = new ArrayList<>(coordinateItems); 141 boolean hasMultiplePairs = itemsList.size() > 1; 142 143 String separator = ""; 144 for (String coordPair : itemsList) { 145 // 只有多对坐标时才过滤无效对 146 if (!hasMultiplePairs || !isBothCoordsInvalid(coordPair)) { 147 this.coordinate.append(separator).append(coordPair); 148 separator = ","; 149 } 150 } 151 } 152 153 /** 154 * 判断一对坐标是否两个都是无效坐标([-1,-1,x] 格式) 155 */ 156 private static boolean isBothCoordsInvalid(String coordPair) { 157 return coordPair != null 158 && coordPair.contains("[-1,-1,") 159 && countOccurrences(coordPair, "[-1,-1,") >= 2; 160 } 161 162 private static int countOccurrences(String str, String target) { 163 if (str == null || target == null || target.isEmpty()) { 164 return 0; 165 } 166 int count = 0; 167 int idx = 0; 168 while ((idx = str.indexOf(target, idx)) != -1) { 169 count++; 170 idx += target.length(); 171 } 172 return count; 173 } 174 175 public void setCoordinate(String coordinate) { 176 if (SQLUtil.isEmpty(coordinate)) { 177 return; 178 } 179 this.coordinate.setLength(0); 180 this.coordinateItems.clear(); 181 182 // 先分割出所有坐标 [line,col,fileIdx] 183 List<String> allCoords = new ArrayList<>(); 184 int start = 0; 185 while (start < coordinate.length()) { 186 int open = coordinate.indexOf('[', start); 187 if (open < 0) break; 188 int close = coordinate.indexOf(']', open); 189 if (close < 0) break; 190 allCoords.add(coordinate.substring(open, close + 1)); 191 start = close + 1; 192 } 193 194 // 计算坐标对数量(每两个坐标组成一对) 195 int pairCount = (allCoords.size() + 1) / 2; 196 197 // 如果只有一对坐标,直接保留 198 if (pairCount <= 1) { 199 this.coordinate.append(coordinate); 200 this.coordinateItems.add(coordinate); 201 return; 202 } 203 204 // 有多对坐标时,过滤掉无效的坐标对(两个坐标都是 [-1,-1,x] 格式) 205 StringBuilder sb = new StringBuilder(); 206 for (int i = 0; i < allCoords.size(); i += 2) { 207 String coord1 = allCoords.get(i); 208 String coord2 = (i + 1 < allCoords.size()) ? allCoords.get(i + 1) : ""; 209 210 // 检查这一对是否都是无效坐标 211 boolean pairIsInvalid = isInvalidCoord(coord1) && isInvalidCoord(coord2); 212 213 if (!pairIsInvalid) { 214 if (sb.length() > 0) { 215 sb.append(","); 216 } 217 sb.append(coord1); 218 this.coordinateItems.add(coord1); 219 if (!coord2.isEmpty()) { 220 sb.append(",").append(coord2); 221 this.coordinateItems.add(coord2); 222 } 223 } 224 } 225 this.coordinate.append(sb.toString()); 226 } 227 228 private static boolean isInvalidCoord(String coord) { 229 if (coord == null || coord.isEmpty()) { 230 return false; 231 } 232 String trimmed = coord.trim(); 233 if (trimmed.indexOf("-1") != -1 && trimmed.startsWith("[") && trimmed.endsWith("]")) { 234 String inner = trimmed.substring(1, trimmed.length() - 1); 235 String[] coords = inner.split(","); 236 if (coords.length >= 2 && "-1".equals(coords[0].trim()) && "-1".equals(coords[1].trim())) { 237 return true; 238 } 239 } 240 return false; 241 } 242 243 public void clearCoordinate() { 244 this.coordinate = new StringBuffer(); 245 } 246 247 @XmlAttribute(required = false) 248 public String getUserName() { 249 return userName; 250 } 251 252 public void setUserName(String userName) { 253 this.userName = userName; 254 } 255 256 @XmlAttribute(required = false) 257 public String getServer() { 258 return server; 259 } 260 261 public void setServer(String server) { 262 this.server = server; 263 } 264 265 @XmlAttribute(required = false) 266 public String getName() { 267 return name; 268 } 269 270 public void setName(String name) { 271 this.name = name; 272 } 273 274 @XmlAttribute(required = false) 275 public String getDisplayName() { 276 return displayName; 277 } 278 279 public void setDisplayName(String displayName) { 280 this.displayName = displayName; 281 } 282 283 @XmlAttribute(required = false) 284 public String getId() { 285 return id; 286 } 287 288 public void setId(String id) { 289 this.id = id; 290 } 291 292 @XmlAttribute(required = false) 293 public List<String> getProcessIds() { 294 return processIds; 295 } 296 297 public void setProcessIds(List<String> processIds) { 298 this.processIds = processIds; 299 } 300 301 @XmlElement(name = "candidate", required = false) 302 public List<String> getCandidateTables() { 303 return candidateTables; 304 } 305 306 public void setCandidateTables(List<String> candidateTables) { 307 this.candidateTables = candidateTables; 308 } 309 310 @XmlAttribute(required = false) 311 public String getType() { 312 return type; 313 } 314 315 public void setType(String type) { 316 this.type = type; 317 } 318 319 @XmlAttribute(required = false) 320 public String getUri() { 321 return uri; 322 } 323 324 public void setUri(String uri) { 325 this.uri = uri; 326 } 327 328 @XmlAttribute(required = false) 329 public String getFileType() { 330 return fileType; 331 } 332 333 public void setFileType(String fileType) { 334 this.fileType = fileType; 335 } 336 337 @XmlAttribute(required = false) 338 public String getFileFormat() { 339 return fileFormat; 340 } 341 342 public void setFileFormat(String fileFormat) { 343 this.fileFormat = fileFormat; 344 } 345 346 @XmlAttribute(required = false) 347 public String getLocation() { 348 return location; 349 } 350 351 public void setLocation(String location) { 352 this.location = location; 353 } 354 355 @XmlAttribute(required = false) 356 public String getNamespace() { 357 return namespace; 358 } 359 360 public void setNamespace(String namespace) { 361 this.namespace = namespace; 362 } 363 364 @XmlTransient 365 public String getStarStmt() { 366 return starStmt; 367 } 368 369 public void setStarStmt(String starStmt) { 370 this.starStmt = starStmt; 371 } 372 373 @XmlTransient 374 public String getIsDetermined() { 375 return isDetermined; 376 } 377 378 public void setIsDetermined(String isDetermined) { 379 this.isDetermined = isDetermined; 380 } 381 382 public boolean isFunction() { 383 return "function".equals(type) || "function".equals(subType); 384 } 385 386 public boolean isView() { 387 return "view".equals(type); 388 } 389 390 public boolean isDatabaseType() { 391 return "database".equals(type); 392 } 393 394 public boolean isSchemaType() { 395 return "schema".equals(type); 396 } 397 398 public boolean isSequence() { 399 return "sequence".equals(type); 400 } 401 402 public boolean isStage() { 403 return "stage".equals(type); 404 } 405 406 public boolean isDataSource() { 407 return "dataSource".equals(type); 408 } 409 410 public boolean isStream() { 411 return "stream".equals(type); 412 } 413 414 public boolean isVariable() { 415 return "variable".equals(type); 416 } 417 418 public boolean isCursor() { 419 return "cursor".equals(type); 420 } 421 422 public boolean isFile() { 423 return "file".equals(type) || "path".equals(type); 424 } 425 426 public boolean isTable() { 427 return "table".equals(type) || "pseudoTable".equals(type) || "constantTable".equals(type); 428 } 429 430 public boolean isPseudoTable() { 431 return "pseudoTable".equals(type); 432 } 433 434 public boolean isConstantTable() { 435 return "pseudoTable".equals(type); 436 } 437 438 public boolean isResultSet() { 439 return type != null && !isView() && !isCursor() && !isTable() && !isStage() && !isSequence() && !isDataSource() && !isDatabaseType() && !isSchemaType() && !isStream() && !isVariable() && !isFile(); 440 } 441 442 @XmlAttribute(name = "isTarget", required = false) 443 public String getIsTarget() { 444 return isTarget; 445 } 446 447 public boolean isTarget() { 448 return "true".equals(isTarget); 449 } 450 451 @XmlAttribute(required = false) 452 public String getParent() { 453 return parent; 454 } 455 456 public void setParent(String parent) { 457 this.parent = parent; 458 } 459 460 @XmlAttribute(required = false) 461 public String getDatabase() { 462 return database; 463 } 464 465 public void setDatabase(String database) { 466 if (SQLUtil.parseNames(database).size() > 1) { 467 database = "\"" + database + "\""; 468 } 469 this.database = database; 470 } 471 472 @XmlAttribute(required = false) 473 public String getSchema() { 474 return schema; 475 } 476 477 public void setSchema(String schema) { 478 if (SQLUtil.parseNames(schema).size() > 1) { 479 schema = "\"" + schema + "\""; 480 } 481 this.schema = schema; 482 } 483 484 @XmlAttribute(required = false) 485 public String getSubType() { 486 return subType; 487 } 488 489 public void setSubType(String subType) { 490 this.subType = subType; 491 } 492 493 public String getFullName() { 494 if (isDatabaseType()) { 495 return database; 496 } 497 StringBuilder fullName = new StringBuilder(); 498 if (!SQLUtil.isEmpty(database)) { 499 fullName.append(database).append("."); 500 } 501 if (!SQLUtil.isEmpty(schema)) { 502 fullName.append(schema).append("."); 503 } 504 if (fullName.length() > 0) { 505 fullName.append(getTableNameOnly()); 506 } else { 507 fullName.append(name); 508 } 509 return fullName.toString(); 510 } 511 512 public String getFullSchemaName() { 513 StringBuilder fullName = new StringBuilder(); 514 if (!SQLUtil.isEmpty(database)) { 515 if(ModelBindingManager.getGlobalVendor()!=null) { 516 fullName.append(DlineageUtil.getIdentifierNormalName(database, ESQLDataObjectType.dotCatalog)).append("."); 517 } 518 else{ 519 fullName.append(database).append("."); 520 } 521 } 522 if (!SQLUtil.isEmpty(schema)) { 523 if(ModelBindingManager.getGlobalVendor()!=null) { 524 fullName.append(DlineageUtil.getIdentifierNormalName(schema, ESQLDataObjectType.dotSchema)); 525 } 526 else { 527 fullName.append(schema).append("."); 528 } 529 } 530 String fullSchemaName = fullName.toString(); 531 if (fullSchemaName.endsWith(".")) { 532 fullSchemaName = fullSchemaName.substring(0, fullSchemaName.length() - 1); 533 } 534 if (fullSchemaName.length() == 0) { 535 fullSchemaName = TSQLEnv.DEFAULT_SCHEMA_NAME; 536 } 537 return fullSchemaName; 538 } 539 540 public String getTableNameOnly() { 541 if (name.indexOf("@") != -1 && SQLUtil.trimColumnStringQuote(name.substring(name.lastIndexOf("@") + 1).trim()).equals(SQLUtil.trimColumnStringQuote(database))) { 542 List<String> segments = SQLUtil.parseNames(name.substring(0, name.lastIndexOf("@")).trim()); 543 if (segments.size() > 2) { 544 return SQLUtil.mergeSegments(segments, 2); 545 } 546 return segments.get(segments.size() - 1); 547 } else { 548 List<String> segments = SQLUtil.parseNames(name); 549 if (segments.size() > 2) { 550 return SQLUtil.mergeSegments(segments, 2); 551 } 552 return segments.get(segments.size() - 1); 553 } 554 } 555 556 public void setIsTarget(String isTarget) { 557 this.isTarget = isTarget; 558 } 559 560 public int getOccurrencesNumber() { 561 return PositionUtil.getOccurrencesNumber(coordinate.toString()); 562 } 563 564 public Coordinate getStartPos(int index) { 565 return PositionUtil.getStartPos(coordinate.toString(), index); 566 } 567 568 public Coordinate getEndPos(int index) { 569 return PositionUtil.getEndPos(coordinate.toString(), index); 570 } 571 572 public Boolean getMore() { 573 return more; 574 } 575 576 public void setMore(Boolean more) { 577 this.more = more; 578 } 579 580 @XmlAttribute(required = false) 581 public String getFromDDL() { 582 return fromDDL; 583 } 584 585 public void setFromDDL(String fromDDL) { 586 this.fromDDL = fromDDL; 587 } 588 589 @XmlAttribute(required = false) 590 public String getEndpointKind() { 591 return endpointKind; 592 } 593 594 public void setEndpointKind(String endpointKind) { 595 this.endpointKind = endpointKind; 596 } 597 598 @XmlAttribute(required = false) 599 public String getEndpointIntroduction() { 600 return endpointIntroduction; 601 } 602 603 public void setEndpointIntroduction(String endpointIntroduction) { 604 this.endpointIntroduction = endpointIntroduction; 605 } 606 607 @XmlAttribute(required = false) 608 public String getCreatedInSql() { 609 return createdInSql; 610 } 611 612 public void setCreatedInSql(String createdInSql) { 613 this.createdInSql = createdInSql; 614 } 615 616 @XmlAttribute(required = false) 617 public String getNormalizedName() { 618 return normalizedName; 619 } 620 621 public void setNormalizedName(String normalizedName) { 622 this.normalizedName = normalizedName; 623 } 624 625 @Override 626 public Object clone() throws CloneNotSupportedException { 627 return super.clone(); 628 } 629}