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