001 002package gudusoft.gsqlparser.dlineage.dataflow.model; 003 004import java.util.ArrayList; 005import java.util.List; 006import java.util.Map; 007import java.util.Set; 008 009import gudusoft.gsqlparser.EDbVendor; 010import gudusoft.gsqlparser.EExpressionType; 011import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType; 012import gudusoft.gsqlparser.TSourceToken; 013import gudusoft.gsqlparser.dlineage.util.DlineageUtil; 014import gudusoft.gsqlparser.dlineage.util.Pair3; 015import gudusoft.gsqlparser.nodes.TConstant; 016import gudusoft.gsqlparser.nodes.TExpression; 017import gudusoft.gsqlparser.nodes.TFunctionCall; 018import gudusoft.gsqlparser.nodes.TObjectName; 019import gudusoft.gsqlparser.nodes.TResultColumn; 020import gudusoft.gsqlparser.nodes.TResultColumnList; 021import gudusoft.gsqlparser.sqlenv.TSQLEnv; 022import gudusoft.gsqlparser.util.SQLUtil; 023 024public class TableColumn extends Column{ 025 026 @Override 027 public gudusoft.gsqlparser.sqlenv.CanonKey getIdentifierKey() { 028 // Variables, cursors and system pseudo-columns are not catalog columns. 029 return table == null || table.isVariable() || table.isCursor() || table.isConstant() || isPseduo 030 ? null : super.getIdentifierKey(); 031 } 032 033 034 private Table table; 035 036 private long id; 037 protected String name; 038 039 protected Pair3<Long, Long, String> startPosition; 040 protected Pair3<Long, Long, String> endPosition; 041 042 private TObjectName columnObject; 043 044 private boolean showStar; 045 046 private boolean expandStar = true; 047 048 private Integer columnIndex; 049 050 private boolean isVariant; 051 052 private boolean isPseduo = false; 053 054 private boolean notBindStarColumn = false; 055 056 private String displayName; 057 058 private String dataType; 059 060 private Boolean primaryKey = false; 061 062 private Boolean unqiueKey = false; 063 064 private Boolean indexKey = false; 065 066 private Boolean foreignKey = false; 067 068 private boolean isStruct = false; 069 070 private List<Transform> transforms; 071 072 private List<Object> candidateParents; 073 074 private TableColumn sourceColumn; 075 076 /** For a field path node, the node it is a field or an array element of: its declared column or a field. */ 077 private TableColumn parentColumn; 078 079 /** For a field path node, its path under the declared column, such as {@code customer.address.city}. */ 080 private gudusoft.gsqlparser.common.structured.StructuredColumnPath fieldPath; 081 082 /** 083 * The kind of type the DDL declares this column or field with (STRUCT, ARRAY, MAP, scalar); null when no DDL 084 * declares it. A subscript read of a MAP reads its value node, of an ARRAY its element node. 085 */ 086 private gudusoft.gsqlparser.common.structured.DeclaredTypeNode.Kind declaredKind; 087 088 public TableColumn(Table view, TObjectName columnObject, int index) { 089 this(view, columnObject); 090 this.columnIndex = index; 091 } 092 093 /** 094 * A field path node: a field or an array element of a STRUCT or ARRAY column, a child of {@code parent} in the 095 * column's tree (the declared column, or the field above). Its identity is structural, the declared column and 096 * the path segments, so it carries no name key; its name is the path as v1 output writes it, 097 * {@code customer.address.city} or {@code items[*].item_name}, for display only. 098 * 099 * @param table the table of the declared column 100 * @param columnObject the name whose position the node reports: the field's definition, or the read that made it 101 * @param parent the node this one is a field or an array element of 102 * @param fieldPath the path under the declared column 103 */ 104 TableColumn(Table table, TObjectName columnObject, TableColumn parent, 105 gudusoft.gsqlparser.common.structured.StructuredColumnPath fieldPath) { 106 this(table, columnObject); 107 this.parentColumn = parent; 108 this.fieldPath = fieldPath; 109 this.name = fieldPath.toDisplayString(); 110 this.isStruct = true; 111 setIdentifierKey(null); 112 parent.children.add(this); 113 table.addFieldPathColumn(this); 114 } 115 116 /** The field path nodes directly below this column or field, in the order they were made. */ 117 private final List<TableColumn> children = new ArrayList<TableColumn>(0); 118 119 public TableColumn(Table table, TObjectName columnObject) { 120 if (table == null || columnObject == null) 121 throw new IllegalArgumentException("TableColumn arguments can't be null."); 122 123 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 124 125 this.columnObject = columnObject; 126 setIdentifierKey(DlineageUtil.columnKey(columnObject)); 127 setStarColumn(DlineageUtil.isStarName(columnObject)); 128 129 TSourceToken startToken = columnObject.getStartToken(); 130 TSourceToken endToken = columnObject.getEndToken(); 131 this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo, 132 startToken.columnNo, ModelBindingManager.getGlobalHash()); 133 this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo, 134 endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash()); 135 136 if (columnObject.getColumnNameOnly() != null 137 && !"".equals(columnObject.getColumnNameOnly())) { 138 if (columnObject.getPartToken() != null && columnObject.getPropertyToken() != null) { 139 if(supportProperty() && columnObject.getSourceTable() == table.getTableObject()) { 140 // Use resolver info to build struct name without table alias prefix 141 String structName = null; 142 gudusoft.gsqlparser.resolver2.model.ColumnSource cs = columnObject.getColumnSource(); 143 if (cs != null && cs.isStructFieldAccess() && cs.hasFieldPath()) { 144 structName = cs.getFieldPath().toFullReference(cs.getExposedName()); 145 } 146 if (structName == null) { 147 gudusoft.gsqlparser.resolver2.model.StructFieldHint hint = columnObject.getStructFieldHint(); 148 if (hint != null && hint.getFieldPath() != null) { 149 structName = hint.toFullReference(); 150 } 151 } 152 if (structName != null) { 153 this.name = structName; 154 } else { 155 // Non-struct qualified name: keep original toString() behavior 156 this.name = columnObject.toString(); 157 } 158 } 159 else if (gudusoft.gsqlparser.nodes.structured.NestedAccessProfile.forVendor(ModelBindingManager.getGlobalVendor()).readsFieldChains()) { 160 // Trino family: payload.address.city reads the column payload (Mantis 4811, 4822). An INSERT or 161 // CTAS column named after such a read takes its last field instead (ModelFactory.targetFieldLeaf). 162 this.name = DlineageUtil.decodeColumnIdentifier(columnObject.getColumnNameOnly()); 163 } 164 else { 165 this.name = DlineageUtil.propertyColumnPath(columnObject); 166 } 167 } 168 else if(columnObject.getColumnSource()!=null && columnObject.getColumnSource().isStructFieldAccess()) { 169 if(supportProperty() && columnObject.getSourceTable() == table.getTableObject()) { 170 // Use resolver info to build struct name without table alias prefix 171 String structName = null; 172 gudusoft.gsqlparser.resolver2.model.ColumnSource cs = columnObject.getColumnSource(); 173 if (cs != null && cs.hasFieldPath()) { 174 structName = cs.getFieldPath().toFullReference(cs.getExposedName()); 175 } 176 if (structName != null) { 177 this.name = structName; 178 } else { 179 this.name = columnObject.toString(); 180 } 181 } else { 182 this.name = DlineageUtil.decodeColumnIdentifier(columnObject.getColumnNameOnly()); 183 } 184 } 185 else if(columnObject.getStructFieldHint() != null && supportProperty() 186 && !isQualifiedColumnReference(columnObject)) { 187 this.name = columnObject.getStructFieldHint().toFullReference(); 188 } 189 else { 190 this.name = DlineageUtil.decodeColumnIdentifier(columnObject.getColumnNameOnly()); 191 } 192 } 193 else if (columnObject.toString().indexOf(".") != -1 && "".equals(columnObject.getColumnNameOnly()) && 194 supportProperty()) { 195 this.name = columnObject.toString(); 196 } 197 else if (columnObject.toString().indexOf(".") != -1) { 198 List<String> splits = SQLUtil.parseNames(columnObject.toString()); 199 this.name = DlineageUtil.decodeColumnIdentifier(splits.get(splits.size() - 1)); 200 } 201 else { 202 // A SELECT alias reaches here with a null part token 203 // (dbObjectType=column_alias, so getColumnNameOnly() is empty), which 204 // is why a CREATE VIEW published '[Volgorde]' / 'Volgorde' verbatim 205 // as the view's column name (GitHub issue #704). The argument is a 206 // TObjectName — an identifier by construction, never a value 207 // literal — so decoding its delimiters here is sound. 208 this.name = DlineageUtil.decodeAliasIdentifier(columnObject.toString()); 209 } 210 211 this.table = table; 212 table.addColumn(this); 213 214 if(isStarColumn() && !table.isDetermined()){ 215 showStar = true; 216 } 217 } 218 219 /** @deprecated read {@code NestedAccessProfile.publishesPropertyNames()} of the analysis's vendor. */ 220 @Deprecated 221 protected boolean supportProperty() { 222 return gudusoft.gsqlparser.nodes.structured.NestedAccessProfile.forVendor(ModelBindingManager.getGlobalVendor()) 223 .publishesPropertyNames(); 224 } 225 226 227 /** 228 * Check if a column reference is a standard qualified table.column reference 229 * (not a struct field access). Detects when tableToken matches the source table's name, 230 * e.g., "catalog.Hospital.table_update_01.Position" where table_update_01 is the table. 231 */ 232 private boolean isQualifiedColumnReference(TObjectName columnObject) { 233 if (columnObject.getSourceTable() == null || columnObject.getTableToken() == null) { 234 return false; 235 } 236 String tableTokenStr = columnObject.getTableToken().getAstext(); 237 gudusoft.gsqlparser.nodes.TTable src = columnObject.getSourceTable(); 238 // Compare tableToken with the source table's simple name (last segment) 239 String tableName = src.getTableName() != null ? src.getTableName().getTableString() : ""; 240 List<String> parts = SQLUtil.parseNames(tableName); 241 String simpleTableName = parts.isEmpty() ? tableName : parts.get(parts.size() - 1); 242 if (sameTableName(tableTokenStr, simpleTableName)) { 243 return true; 244 } 245 // Also check alias 246 String aliasName = src.getAliasName(); 247 if (aliasName != null && sameTableName(tableTokenStr, aliasName)) { 248 return true; 249 } 250 return false; 251 } 252 253 /** 254 * Table-name/alias equality routed through the unified façade; falls back to 255 * case-insensitive comparison when no analysis vendor is bound. 256 */ 257 private static boolean sameTableName(String a, String b) { 258 if (a == null || b == null) { 259 return false; 260 } 261 if (DlineageUtil.configuresIdentifierRules()) { 262 return DlineageUtil.sameName(ESQLDataObjectType.dotTable, a, b); 263 } 264 EDbVendor vendor = ModelBindingManager.getGlobalVendor(); 265 return vendor == null ? a.equalsIgnoreCase(b) 266 : SQLUtil.sameName(vendor, ESQLDataObjectType.dotTable, a, b); 267 } 268 269 /** 270 * Constructor for MDX and other non-SQL columns where only a name is available. 271 */ 272 public TableColumn(Table table, String columnName) { 273 if (table == null || columnName == null) 274 throw new IllegalArgumentException("TableColumn arguments can't be null."); 275 276 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 277 278 this.startPosition = new Pair3<Long, Long, String>(-1L, -1L, ModelBindingManager.getGlobalHash()); 279 this.endPosition = new Pair3<Long, Long, String>(-1L, -1L, ModelBindingManager.getGlobalHash()); 280 281 this.name = columnName; 282 this.table = table; 283 table.addColumn(this); 284 } 285 286 public TableColumn(Table table, TConstant columnObject, int columnIndex) { 287 if (table == null || columnObject == null) 288 throw new IllegalArgumentException("TableColumn arguments can't be null."); 289 290 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 291 292 TSourceToken startToken = columnObject.getStartToken(); 293 TSourceToken endToken = columnObject.getEndToken(); 294 this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo, 295 startToken.columnNo, ModelBindingManager.getGlobalHash()); 296 this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo, 297 endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash()); 298 299 this.name = columnObject.toString(); 300 301 this.table = table; 302 table.addColumn(this); 303 } 304 305 public TableColumn(Table table, TConstant columnObject) { 306 if (table == null || columnObject == null) 307 throw new IllegalArgumentException("TableColumn arguments can't be null."); 308 309 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 310 311 TSourceToken startToken = columnObject.getStartToken(); 312 TSourceToken endToken = columnObject.getEndToken(); 313 this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo, 314 startToken.columnNo, ModelBindingManager.getGlobalHash()); 315 this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo, 316 endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash()); 317 318 this.name = columnObject.toString(); 319 this.table = table; 320 table.addColumn(this); 321 } 322 323 public TableColumn(Table table, TFunctionCall columnObject) { 324 if (table == null || columnObject == null) 325 throw new IllegalArgumentException("TableColumn arguments can't be null."); 326 327 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 328 329 TSourceToken startToken = columnObject.getStartToken(); 330 TSourceToken endToken = columnObject.getEndToken(); 331 this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo, 332 startToken.columnNo, ModelBindingManager.getGlobalHash()); 333 this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo, 334 endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash()); 335 336 this.name = columnObject.toString(); 337 this.table = table; 338 table.addColumn(this); 339 } 340 341 342 public TableColumn(Table table, TExpression columnObject, int columnIndex) { 343 if (table == null || columnObject == null) 344 throw new IllegalArgumentException("TableColumn arguments can't be null."); 345 346 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 347 348 TSourceToken startToken = columnObject.getStartToken(); 349 TSourceToken endToken = columnObject.getEndToken(); 350 this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo, 351 startToken.columnNo, ModelBindingManager.getGlobalHash()); 352 this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo, 353 endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash()); 354 355 this.name = getColumnName(columnObject); 356 357 this.table = table; 358 table.addColumn(this); 359 } 360 361 protected String getColumnName(TExpression expr) { 362 if (expr.getExpressionType() == EExpressionType.simple_constant_t) { 363 return expr.toString(); 364 } else if (expr.getExpressionType() == EExpressionType.sqlserver_proprietary_column_alias_t) { 365 // "alias = expression": no TAliasClause exists, but the left operand 366 // is an alias by grammar, so it decodes like one (issue #704). 367 return DlineageUtil.decodeAliasIdentifier(expr.getLeftOperand().toString()); 368 } else if (expr.getExpressionType() == EExpressionType.function_t) { 369 return expr.getFunctionCall().toString(); 370 } else if (expr.getExpressionType() == EExpressionType.typecast_t) { 371 return expr.getLeftOperand().toString(); 372 } else if (expr.getExpressionType() == EExpressionType.parenthesis_t) { 373 TExpression inner = expr.getLeftOperand(); 374 while (inner != null && inner.getExpressionType() == EExpressionType.parenthesis_t) { 375 inner = inner.getLeftOperand(); 376 } 377 return getColumnName(inner); 378 } else if (expr.getExpressionType() == EExpressionType.simple_object_name_t) { 379 TObjectName columnName = expr.getObjectOperand(); 380 if (columnName.getPropertyToken() != null) { 381 return DlineageUtil.decodeColumnIdentifier(DlineageUtil.fieldLeafText(columnName)); 382 } else { 383 return DlineageUtil.decodeColumnIdentifier(columnName.getColumnNameOnly()); 384 } 385 } else if (expr.getExpressionType() == EExpressionType.subquery_t) { 386 TResultColumnList resultset = expr.getSubQuery().getResultColumnList(); 387 if (resultset != null && resultset.size() == 1) { 388 TResultColumn resultColumn = resultset.getResultColumn(0); 389 if (!SQLUtil.isEmpty(resultColumn.getColumnAlias())) { 390 return resultColumn.getColumnAlias(); 391 } else if (resultColumn.getExpr() != null) { 392 return getColumnName(resultColumn.getExpr()); 393 } else if (resultColumn.getColumnNameOnly() != null 394 && !"".equals(resultColumn.getColumnNameOnly())) { 395 if (resultColumn.getExpr().getObjectOperand() != null 396 && resultColumn.getExpr().getObjectOperand().getPropertyToken() != null) { 397 this.name = DlineageUtil.decodeColumnIdentifier(DlineageUtil.fieldLeafText(resultColumn.getExpr().getObjectOperand())); 398 } else { 399 this.name = DlineageUtil.decodeColumnIdentifier(resultColumn.getColumnNameOnly()); 400 } 401 } else { 402 this.name = resultColumn.toString(); 403 } 404 } 405 } 406 return quote(expr.toString()); 407 } 408 409 410 private String quote(String column) { 411 EDbVendor vendor = ModelBindingManager.getGlobalVendor(); 412 if (!DlineageUtil.isQuote(column)){ 413 return SQLUtil.quoteDottedName(vendor, ESQLDataObjectType.dotColumn, column); 414 } 415 else return column; 416 } 417 418 public Table getTable() { 419 return table; 420 } 421 422 public long getId() { 423 return id; 424 } 425 426 public String getName() { 427 return name; 428 } 429 430 public Pair3<Long, Long, String> getStartPosition() { 431 return startPosition; 432 } 433 434 public Pair3<Long, Long, String> getEndPosition() { 435 return endPosition; 436 } 437 438 public void setStartPosition(Pair3<Long, Long, String> startPosition) { 439 this.startPosition = startPosition; 440 } 441 442 public void setEndPosition(Pair3<Long, Long, String> endPosition) { 443 this.endPosition = endPosition; 444 } 445 446 public TObjectName getColumnObject() { 447 return columnObject; 448 } 449 450 /** 451 * Adds the given star links as a map's {@code putAll} does: a link of a published name this column already has is 452 * replaced, a new one is appended. The map of another column ({@link #getStarLinkColumns()}) shares that column's 453 * links, as {@link #shareStarLinksFrom(Column)} does. 454 * 455 * @deprecated use {@link #shareStarLinksFrom(Column)} 456 */ 457 @Deprecated 458 public void bindStarLinkColumns(Map<String, Set<TObjectName>> starLinkColumns) { 459 if (starLinkColumns != null && !starLinkColumns.isEmpty()) { 460 getStarLinkColumns().putAll(starLinkColumns); 461 } 462 } 463 464 @Override 465 void propagateStarLink(TObjectName objectName) { 466 if(this.getStarSourceColumns()!=null) { 467 StarLinkKey nameKey = StarLinkStore.IDENTITY && !this.getStarSourceColumns().isEmpty() 468 ? StarLinkStore.keyOf(objectName) : null; 469 for(Column column: this.getStarSourceColumns()) { 470 boolean bind = true; 471 if(column instanceof ResultColumn) { 472 ResultSet parent = ((ResultColumn) column).getResultSet(); 473 for(ResultColumn item: parent.getColumns()) { 474 Boolean same = StarLinkStore.identityAnswer(item, nameKey); 475 if(same != null ? same.booleanValue() : DlineageUtil.sameColumnDisplay(item.getName(), objectName)) { 476 bind = false; 477 break; 478 } 479 } 480 } 481 if(column instanceof TableColumn) { 482 Table parent = ((TableColumn) column).getTable(); 483 for(TableColumn item: parent.getColumns()) { 484 Boolean same = StarLinkStore.identityAnswer(item, nameKey); 485 if(same != null ? same.booleanValue() : DlineageUtil.sameColumnDisplay(item.getName(), objectName)) { 486 bind = false; 487 break; 488 } 489 } 490 } 491 if (bind) { 492 column.bindStarLinkColumn(objectName); 493 } 494 } 495 } 496 } 497 498 public boolean isShowStar() { 499 return showStar; 500 } 501 502 public void setShowStar(boolean showStar) { 503 this.showStar = showStar; 504 } 505 506 public Table getView() { 507 return getTable(); 508 } 509 510 public Integer getColumnIndex() { 511 return columnIndex; 512 } 513 514 public boolean isVariant() { 515 return isVariant; 516 } 517 518 public void setVariant(boolean isVariant) { 519 this.isVariant = isVariant; 520 } 521 522 public String getDisplayName() { 523 return displayName; 524 } 525 526 public void setDisplayName(String displayName) { 527 this.displayName = displayName; 528 } 529 530 public void setExpandStar(boolean expandStar) { 531 this.expandStar = expandStar; 532 } 533 534 public boolean isExpandStar() { 535 return expandStar; 536 } 537 538 public boolean isPseduo() { 539 return isPseduo; 540 } 541 542 public void setPseduo(boolean isPseduo) { 543 this.isPseduo = isPseduo; 544 } 545 546 public void notBindStarLinkColumn(boolean notBindStarColumn) { 547 this.notBindStarColumn = notBindStarColumn; 548 } 549 550 public boolean isNotBindStarLinkColumn() { 551 return notBindStarColumn; 552 } 553 554 public Boolean getPrimaryKey() { 555 return primaryKey; 556 } 557 558 public void setPrimaryKey(Boolean primaryKey) { 559 this.primaryKey = primaryKey; 560 } 561 562 public Boolean getUnqiueKey() { 563 return unqiueKey; 564 } 565 566 public void setUnqiueKey(Boolean unqiueKey) { 567 this.unqiueKey = unqiueKey; 568 } 569 570 public Boolean getIndexKey() { 571 return indexKey; 572 } 573 574 public void setIndexKey(Boolean indexKey) { 575 this.indexKey = indexKey; 576 } 577 578 public Boolean getForeignKey() { 579 return foreignKey; 580 } 581 582 public void setForeignKey(Boolean foreignKey) { 583 this.foreignKey = foreignKey; 584 } 585 586 public String getDataType() { 587 return dataType; 588 } 589 590 public void setDataType(String dataType) { 591 this.dataType = dataType; 592 } 593 594 @Override 595 public String toString() { 596 return this.name; 597 } 598 599 public void setColumnIndex(int columnIndex) { 600 this.columnIndex = columnIndex; 601 } 602 603 public boolean isStruct() { 604 return isStruct; 605 } 606 607 public void setStruct(boolean isStruct) { 608 this.isStruct = isStruct; 609 } 610 611 public List<Transform> getTransforms() { 612 return transforms; 613 } 614 615 public void setTransform(Transform transform) { 616 if(transform==null) { 617 return; 618 } 619 if(transforms == null) { 620 transforms = new ArrayList<Transform>(); 621 } 622 if(!transforms.contains(transform)) { 623 transforms.add(transform); 624 } 625 } 626 627 public List<Object> getCandidateParents() { 628 return candidateParents; 629 } 630 631 public void setCandidateParents(List<Object> candidateParents) { 632 if (this.candidateParents == null) { 633 this.candidateParents = candidateParents; 634 } 635 else { 636 this.candidateParents.addAll(candidateParents); 637 } 638 } 639 640 public TableColumn getSourceColumn() { 641 return sourceColumn; 642 } 643 644 /** Whether this is a field path node (a field or an array element of a column), not a column of its own. */ 645 public boolean isFieldPathNode() { 646 return fieldPath != null; 647 } 648 649 /** For a field path node, the node it is a field or an array element of; null for a column. */ 650 public TableColumn getParentColumn() { 651 return parentColumn; 652 } 653 654 /** For a field path node, its path under the declared column; null for a column. */ 655 public gudusoft.gsqlparser.common.structured.StructuredColumnPath getFieldPath() { 656 return fieldPath; 657 } 658 659 /** 660 * The fields of this column or field as its declared STRUCT type lists them, each a field path node, in order; 661 * empty when no declared type lists them, so the fields are not known. 662 */ 663 public List<TableColumn> getDeclaredFields() { 664 List<TableColumn> fields = new ArrayList<TableColumn>(); 665 FieldDeclaration declaration = getTable().getActiveFieldDeclaration(); 666 if (declaration == null || !declaration.declaresFieldsOf(this)) { 667 return fields; 668 } 669 for (TableColumn node : children) { 670 if (declaration.declares(node) && node.fieldPath.getSegments() 671 .get(node.fieldPath.getSegments().size() - 1) 672 .getKind() == gudusoft.gsqlparser.common.structured.StructuredPathSegmentKind.FIELD) { 673 fields.add(node); 674 } 675 } 676 return fields; 677 } 678 679 /** The node of the element of this column or field when a declared ARRAY type made one, {@code items[*]}; else null. */ 680 public TableColumn getDeclaredElement() { 681 FieldDeclaration declaration = getTable().getActiveFieldDeclaration(); 682 if (declaration == null) { 683 return null; 684 } 685 for (TableColumn node : children) { 686 if (declaration.declares(node) && node.fieldPath.getSegments() 687 .get(node.fieldPath.getSegments().size() - 1) 688 .getKind() == gudusoft.gsqlparser.common.structured.StructuredPathSegmentKind.ARRAY_ELEMENT) { 689 return node; 690 } 691 } 692 return null; 693 } 694 695 /** 696 * Whether the STRUCT type this column or field is declared with lists its fields, each a field path node, under 697 * the declaration in force at the statement being analyzed ({@link Table#getActiveFieldDeclaration()}). 698 */ 699 public boolean isFieldsDeclared() { 700 FieldDeclaration declaration = getTable().getActiveFieldDeclaration(); 701 return declaration != null && declaration.declaresFieldsOf(this); 702 } 703 704 /** Records, in the declaration being made, that this column's or field's declared STRUCT type lists its fields. */ 705 public void setFieldsDeclared(boolean fieldsDeclared) { 706 if (fieldsDeclared) { 707 getTable().fieldDeclarationForWrite().addFieldsDeclared(this); 708 } 709 } 710 711 /** Records, in the declaration being made, that a declared type made this field path node. */ 712 public void setDeclaredField(boolean declaredField) { 713 if (declaredField) { 714 getTable().fieldDeclarationForWrite().addDeclaredNode(this); 715 } 716 } 717 718 /** Whether the declaration in force made this field path node, rather than a read. */ 719 public boolean isDeclaredField() { 720 FieldDeclaration declaration = getTable().getActiveFieldDeclaration(); 721 return declaration != null && declaration.declares(this); 722 } 723 724 /** The kind of type the DDL declares this column or field with; null when no DDL declares it. */ 725 public gudusoft.gsqlparser.common.structured.DeclaredTypeNode.Kind getDeclaredKind() { 726 return declaredKind; 727 } 728 729 public void setDeclaredKind(gudusoft.gsqlparser.common.structured.DeclaredTypeNode.Kind declaredKind) { 730 this.declaredKind = declaredKind; 731 } 732 733 /** The declared column a field path node belongs to, the column itself for a column. */ 734 public TableColumn getDeclaredColumn() { 735 TableColumn column = this; 736 while (column.parentColumn != null) { 737 column = column.parentColumn; 738 } 739 return column; 740 } 741 742 public void setSourceColumn(TableColumn sourceColumn) { 743 this.sourceColumn = sourceColumn; 744 } 745}