001 002package gudusoft.gsqlparser.dlineage.dataflow.model; 003 004import java.util.*; 005import java.util.concurrent.atomic.AtomicInteger; 006 007import gudusoft.gsqlparser.EDbVendor; 008import gudusoft.gsqlparser.EExpressionType; 009import gudusoft.gsqlparser.EFunctionType; 010import gudusoft.gsqlparser.TSourceToken; 011import gudusoft.gsqlparser.dlineage.util.DlineageUtil; 012import gudusoft.gsqlparser.dlineage.util.Pair; 013import gudusoft.gsqlparser.dlineage.util.Pair3; 014import gudusoft.gsqlparser.nodes.TExpression; 015import gudusoft.gsqlparser.nodes.TFunctionCall; 016import gudusoft.gsqlparser.nodes.TObjectName; 017import gudusoft.gsqlparser.nodes.TParseTreeNode; 018import gudusoft.gsqlparser.nodes.TResultColumn; 019import gudusoft.gsqlparser.nodes.TResultColumnList; 020import gudusoft.gsqlparser.nodes.TTable; 021import gudusoft.gsqlparser.util.SQLUtil; 022 023public class ResultColumn extends Column{ 024 025 protected ResultSet resultSet; 026 027 protected long id; 028 029 protected String alias; 030 protected Pair3<Long, Long, String> aliasStartPosition; 031 protected Pair3<Long, Long, String> aliasEndPosition; 032 033 protected String fullName; 034 protected String name; 035 036 protected Pair3<Long, Long, String> startPosition; 037 protected Pair3<Long, Long, String> endPosition; 038 039 protected TParseTreeNode columnObject; 040 041 protected String refColumnName; 042 043 protected Map<String, Set<TObjectName>> starLinkColumns = new LinkedHashMap<String, Set<TObjectName>>(); 044 045 private boolean showStar = true; 046 047 protected boolean isFunction = false; 048 049 private boolean isPseduo = false; 050 051 private boolean isStruct = false; 052 053 private List<Transform> transforms; 054 055 public ResultColumn() { 056 057 } 058 059 public ResultColumn(ResultSet resultSet, TParseTreeNode columnObject) { 060 if (columnObject == null || resultSet == null) 061 throw new IllegalArgumentException("ResultColumn arguments can't be null."); 062 063 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 064 065 this.resultSet = resultSet; 066 resultSet.addColumn(this); 067 068 this.columnObject = columnObject; 069 070 TSourceToken startToken = columnObject.getStartToken(); 071 TSourceToken endToken = columnObject.getEndToken(); 072 073 if (columnObject instanceof TObjectName) { 074 if (((TObjectName) columnObject).getColumnNameOnly() != null 075 && !"".equals(((TObjectName) columnObject).getColumnNameOnly())) { 076 this.name = DlineageUtil.decodeColumnIdentifier(((TObjectName) columnObject).getColumnNameOnly()); 077 } else { 078 // Empty columnNameOnly means a null part token, which is what an 079 // ALIAS TObjectName looks like (dbObjectType=column_alias). This is 080 // the path MSSQL's "alias = expression" form takes, and leaving it 081 // raw published RS-1."[Alias]" beside a view column "Alias" (#704). 082 this.name = DlineageUtil.decodeAliasIdentifier(((TObjectName) columnObject).toString()); 083 } 084 } else 085 this.name = columnObject.toString(); 086 if (this.name == null) { 087 this.name = ""; 088 } 089 090 this.fullName = columnObject.toString(); 091 if (this.fullName == null) { 092 this.fullName = this.name; 093 } 094 095 if (startToken != null) { 096 this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo, startToken.columnNo, 097 ModelBindingManager.getGlobalHash()); 098 } 099 if (endToken != null) { 100 this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo, 101 endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), 102 ModelBindingManager.getGlobalHash()); 103 } 104 } 105 106 public ResultColumn(ResultSet resultSet, TResultColumn resultColumnObject) { 107 if (resultColumnObject == null || resultSet == null) 108 throw new IllegalArgumentException("ResultColumn arguments can't be null."); 109 110 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 111 112 this.resultSet = resultSet; 113 resultSet.addColumn(this); 114 115 this.columnObject = resultColumnObject; 116 117 if (resultColumnObject.getAliasClause() != null) { 118 this.alias = resultColumnObject.getAliasClause().toString(); 119 TSourceToken startToken = resultColumnObject.getAliasClause().getStartToken(); 120 TSourceToken endToken = resultColumnObject.getAliasClause().getEndToken(); 121 this.aliasStartPosition = new Pair3<Long, Long, String>(startToken.lineNo, startToken.columnNo, 122 ModelBindingManager.getGlobalHash()); 123 this.aliasEndPosition = new Pair3<Long, Long, String>(endToken.lineNo, 124 endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash()); 125 126 // The alias is an identifier: publish the name a catalog stores, not 127 // the syntactic spelling. Replaces a vendor-blind apostrophe strip 128 // that left [x] / "x" / `x` delimited (GitHub issue #704). 129 // Collision detection must key on the name we PUBLISH, not on the 130 // spelling. `SELECT a AS [x], b AS x` decodes both to x, and keying 131 // on the raw text would emit two indistinguishable RS-1.x targets — 132 // the canonical lineage walker keys nodes by (parent, column), so a 133 // trace of x would conflate two different sources. 134 String decodedAlias = DlineageUtil.decodeAliasIdentifier(this.alias); 135 String aliasKey = decodedAlias.toLowerCase(); 136 if(resultSet.getAliasMap().containsKey(aliasKey)){ 137 int index = resultSet.getAliasMap().get(aliasKey).incrementAndGet(); 138 this.name = decodedAlias + "(" + index + ")"; 139 } 140 else { 141 resultSet.getAliasMap().put(aliasKey, new AtomicInteger(0)); 142 this.name = decodedAlias; 143 } 144 145 if(resultColumnObject.getExpr().getFunctionCall()!=null) { 146 TFunctionCall functionObj = resultColumnObject.getExpr().getFunctionCall(); 147 if (functionObj.getFunctionType() == EFunctionType.array_agg_t 148 && ModelBindingManager.getGlobalVendor() == EDbVendor.dbvbigquery 149 && functionObj.getArgs() != null 150 && functionObj.getArgs().size() == 1) { 151 152 TExpression arg0 = functionObj.getArgs().getExpression(0); 153 if (arg0 != null 154 && arg0.getExpressionType() == EExpressionType.simple_object_name_t) { 155 156 TObjectName on = arg0.getObjectOperand(); 157 if (on.getSourceTable()!=null) { 158 this.setStruct(true); 159 this.name = "*"; 160 } 161 } 162 } 163 } 164 } else if (resultColumnObject.getExpr() != null) { 165 this.name = getColumnName(resultColumnObject.getExpr()); 166 } 167 168 if(this.name == null){ 169 if (resultColumnObject.getColumnNameOnly() != null 170 && !"".equals(resultColumnObject.getColumnNameOnly())) { 171 if (resultColumnObject.getExpr().getObjectOperand() != null 172 && resultColumnObject.getExpr().getObjectOperand().getPropertyToken() != null) { 173 this.name = DlineageUtil.decodeColumnIdentifier(resultColumnObject.getExpr().getObjectOperand().getPropertyToken().getAstext()); 174 } else { 175 this.name = DlineageUtil.decodeColumnIdentifier(resultColumnObject.getColumnNameOnly()); 176 } 177 } else { 178 this.name = resultColumnObject.toString(); 179 } 180 } 181 182 if (resultColumnObject.getExpr().getExpressionType() == EExpressionType.function_t) { 183 this.fullName = resultColumnObject.getExpr().getFunctionCall().toString(); 184 } else if (resultColumnObject.getExpr().getExpressionType() == EExpressionType.typecast_t) { 185 this.fullName = resultColumnObject.getExpr().getLeftOperand().toString(); 186 } else { 187 this.fullName = resultColumnObject.toString(); 188 } 189 190 TSourceToken startToken = resultColumnObject.getStartToken(); 191 TSourceToken endToken = resultColumnObject.getEndToken(); 192 this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo, startToken.columnNo, 193 ModelBindingManager.getGlobalHash()); 194 this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo, endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), 195 ModelBindingManager.getGlobalHash()); 196 } 197 198 public ResultColumn(ResultSet resultSet, TResultColumn resultColumnObject, String refColumnName) { 199 if (resultColumnObject == null || resultSet == null) 200 throw new IllegalArgumentException("ResultColumn arguments can't be null."); 201 202 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 203 204 this.resultSet = resultSet; 205 resultSet.addColumn(this); 206 207 this.columnObject = resultColumnObject; 208 this.refColumnName = refColumnName; 209 210 this.name = refColumnName; 211 212 TSourceToken startToken = resultColumnObject.getStartToken(); 213 TSourceToken endToken = resultColumnObject.getEndToken(); 214 this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo, startToken.columnNo, 215 ModelBindingManager.getGlobalHash()); 216 this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo, endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), 217 ModelBindingManager.getGlobalHash()); 218 } 219 220 protected String getColumnName(TExpression expr) { 221 if (expr.getExpressionType() == EExpressionType.simple_constant_t) { 222 return expr.toString(); 223 } else if (expr.getExpressionType() == EExpressionType.sqlserver_proprietary_column_alias_t) { 224 this.alias = expr.getLeftOperand().toString(); 225 TSourceToken startToken = expr.getLeftOperand().getStartToken(); 226 TSourceToken endToken = expr.getLeftOperand().getEndToken(); 227 this.aliasStartPosition = new Pair3<Long, Long, String>(startToken.lineNo, startToken.columnNo, 228 ModelBindingManager.getGlobalHash()); 229 this.aliasEndPosition = new Pair3<Long, Long, String>(endToken.lineNo, 230 endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash()); 231 232 return DlineageUtil.decodeAliasIdentifier(this.alias); 233 } else if (expr.getExpressionType() == EExpressionType.function_t) { 234 return expr.getFunctionCall().toString(); 235 } else if (expr.getExpressionType() == EExpressionType.typecast_t) { 236 return expr.getLeftOperand().toString(); 237 } else if (expr.getExpressionType() == EExpressionType.parenthesis_t) { 238 TExpression inner = expr.getLeftOperand(); 239 while (inner != null && inner.getExpressionType() == EExpressionType.parenthesis_t) { 240 inner = inner.getLeftOperand(); 241 } 242 return getColumnName(inner); 243 } else if (expr.getExpressionType() == EExpressionType.simple_object_name_t) { 244 TObjectName columnName = expr.getObjectOperand(); 245 if (columnName.getPropertyToken() != null) { 246 return DlineageUtil.decodeColumnIdentifier(columnName.getPropertyToken().getAstext()); 247 } else { 248 if (SQLUtil.isEmpty(columnName.getColumnNameOnly())) { 249 return columnName.toString(); 250 } else { 251 return DlineageUtil.decodeColumnIdentifier(columnName.getColumnNameOnly()); 252 } 253 } 254 } else if (expr.getExpressionType() == EExpressionType.subquery_t) { 255 TResultColumnList resultset = expr.getSubQuery().getResultColumnList(); 256 if (resultset != null && resultset.size() == 1) { 257 TResultColumn resultColumn = resultset.getResultColumn(0); 258 if (!SQLUtil.isEmpty(resultColumn.getColumnAlias())) { 259 return resultColumn.getColumnAlias(); 260 } else if (resultColumn.getExpr() != null) { 261 return getColumnName(resultColumn.getExpr()); 262 } else if (resultColumn.getColumnNameOnly() != null 263 && !"".equals(resultColumn.getColumnNameOnly())) { 264 if (resultColumn.getExpr().getObjectOperand() != null 265 && resultColumn.getExpr().getObjectOperand().getPropertyToken() != null) { 266 this.name = DlineageUtil.decodeColumnIdentifier(resultColumn.getExpr().getObjectOperand().getPropertyToken().getAstext()); 267 } else { 268 this.name = DlineageUtil.decodeColumnIdentifier(resultColumn.getColumnNameOnly()); 269 } 270 } else { 271 this.name = resultColumn.toString(); 272 } 273 } 274 } 275 return null; 276 } 277 278 public ResultColumn(SelectResultSet resultSet, Pair<TResultColumn, TObjectName> starColumnPair) { 279 if (starColumnPair == null || resultSet == null) 280 throw new IllegalArgumentException("ResultColumn arguments can't be null."); 281 282 id = ++ModelBindingManager.get().TABLE_COLUMN_ID; 283 284 this.resultSet = resultSet; 285 resultSet.addColumn(this); 286 287 this.columnObject = starColumnPair.first; 288 289 TSourceToken startToken = columnObject.getStartToken(); 290 TSourceToken endToken = columnObject.getEndToken(); 291 292 this.name = DlineageUtil.decodeColumnIdentifier(((TObjectName) columnObject).getColumnNameOnly()); 293 if(name == null){ 294 name = DlineageUtil.decodeAliasIdentifier(((TObjectName) columnObject).toString()); 295 } 296 this.fullName = columnObject.toString(); 297 298 this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo, startToken.columnNo, 299 ModelBindingManager.getGlobalHash()); 300 this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo, endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), 301 ModelBindingManager.getGlobalHash()); 302 } 303 304 // private int getIndexOf(TResultColumnList resultColumnList, 305 // TResultColumn resultColumnObject) { 306 // for (int i = 0; i < resultColumnList.size(); i++) { 307 // if (resultColumnList.getResultColumn(i) == resultColumnObject) 308 // return i; 309 // } 310 // return -1; 311 // } 312 313 public ResultSet getResultSet() { 314 return resultSet; 315 } 316 317 public long getId() { 318 return id; 319 } 320 321 public String getAlias() { 322 return alias; 323 } 324 325 public Pair3<Long, Long, String> getAliasStartPosition() { 326 return aliasStartPosition; 327 } 328 329 public Pair3<Long, Long, String> getAliasEndPosition() { 330 return aliasEndPosition; 331 } 332 333 public String getFullName() { 334 return fullName; 335 } 336 337 public Pair3<Long, Long, String> getStartPosition() { 338 return startPosition; 339 } 340 341 public Pair3<Long, Long, String> getEndPosition() { 342 return endPosition; 343 } 344 345 public TParseTreeNode getColumnObject() { 346 return columnObject; 347 } 348 349 public String getName() { 350 return name; 351 } 352 353 public boolean bindStarLinkColumn(TObjectName objectName) { 354 if (objectName == null) { 355 return false; 356 } 357 358 String columnName = DlineageUtil.getColumnName(objectName); 359 360 if ("*".equals(columnName)) { 361 return false; 362 } 363 364 boolean flag = false; 365 if (!starLinkColumns.containsKey(columnName)) { 366 starLinkColumns.put(columnName, new LinkedHashSet<TObjectName>()); 367 flag = true; 368 } 369 370 starLinkColumns.get(columnName).add(objectName); 371 372 if (this.getStarSourceColumns() != null) { 373 for (Column column : this.getStarSourceColumns()) { 374 boolean find = false; 375 if (column instanceof ResultColumn) { 376 ResultSet resultset = ((ResultColumn) column).getResultSet(); 377 for (ResultColumn resultColumn : resultset.getColumns()) { 378 if (DlineageUtil.compareColumnIdentifier(getColumnName(resultColumn.name), 379 getColumnName(objectName.toString()))) { 380 find = true; 381 break; 382 } 383 } 384 } 385 if (!find) { 386 column.bindStarLinkColumn(objectName); 387 } 388 } 389 } 390 391 return flag; 392 } 393 394 private String getColumnName(String column) { 395 if (column == null) { 396 return null; 397 } 398 String name = column.substring(column.lastIndexOf(".") + 1); 399 if (name == null || "".equals(name.trim())) { 400 return DlineageUtil.getIdentifierNormalColumnName(column.toString().trim()); 401 } else 402 return DlineageUtil.getIdentifierNormalColumnName(name.trim()); 403 } 404 405 406 public int indexOfStarLinkColumn(TObjectName objectName) { 407 if (objectName == null) { 408 return -1; 409 } 410 411 String columnName = DlineageUtil.getColumnName(objectName); 412 413 if ("*".equals(columnName)) { 414 return -1; 415 } 416 417 int index = -1; 418 for (String item : starLinkColumns.keySet()) { 419 index++; 420 if (item.equals(columnName)) { 421 return index; 422 } 423 } 424 return index; 425 } 426 427 public void unbindStarLinkColumn(TObjectName objectName) { 428 if (objectName == null) { 429 return; 430 } 431 432 String columnName = DlineageUtil.getColumnName(objectName); 433 starLinkColumns.remove(columnName); 434 } 435 436 public void bindStarLinkColumn(TObjectName objectName, int index) { 437 if (objectName == null) { 438 return; 439 } 440 441 String columnName = DlineageUtil.getColumnName(objectName); 442 443 if ("*".equals(columnName)) { 444 return; 445 } 446 if (!starLinkColumns.containsKey(columnName)) { 447 Map<String, Set<TObjectName>> copyStarLinkColumns = new LinkedHashMap<String, Set<TObjectName>>(); 448 int i = 0; 449 for(String item: starLinkColumns.keySet()){ 450 if(index == i){ 451 copyStarLinkColumns.put(columnName, new LinkedHashSet<TObjectName>()); 452 } 453 copyStarLinkColumns.put(item, starLinkColumns.get(item)); 454 i++; 455 } 456 starLinkColumns = copyStarLinkColumns; 457 } 458 459 if (starLinkColumns.get(columnName) != null) { 460 starLinkColumns.get(columnName).add(objectName); 461 } 462 else { 463 starLinkColumns.put(columnName, new LinkedHashSet<TObjectName>()); 464 starLinkColumns.get(columnName).add(objectName); 465 } 466 } 467 468 public boolean hasStarLinkColumn(){ 469 return starLinkColumns!=null && !starLinkColumns.isEmpty(); 470 } 471 472 public Map<String, Set<TObjectName>> getStarLinkColumns() { 473 return starLinkColumns; 474 } 475 476 public List<TObjectName> getStarLinkColumnList() { 477 List<TObjectName> columns = new ArrayList<TObjectName>(); 478 if (starLinkColumns != null) { 479 for (Set<TObjectName> columnSet : starLinkColumns.values()) { 480 columns.addAll(columnSet); 481 } 482 } 483 return columns; 484 } 485 486 public List<String> getStarLinkColumnNames() { 487 List<String> columns = new ArrayList<String>(); 488 if (starLinkColumns != null) { 489 columns.addAll(starLinkColumns.keySet()); 490 } 491 return columns; 492 } 493 494 public TObjectName getStarLinkColumnName(int index) { 495 List<String> columns = new ArrayList<String>(); 496 if (starLinkColumns != null) { 497 columns.addAll(starLinkColumns.keySet()); 498 } 499 return starLinkColumns.get(getStarLinkColumnNames().get(index)).iterator().next(); 500 } 501 502 public boolean isShowStar() { 503 return showStar; 504 } 505 506 public void setShowStar(boolean showStar) { 507 this.showStar = showStar; 508 } 509 510 public boolean isFunction() { 511 return isFunction; 512 } 513 514 public void setFunction(boolean isFunction) { 515 this.isFunction = isFunction; 516 } 517 518 public boolean isPseduo() { 519 return isPseduo; 520 } 521 522 public void setPseduo(boolean isPseduo) { 523 this.isPseduo = isPseduo; 524 } 525 526 public String getRefColumnName() { 527 return refColumnName; 528 } 529 530 @Override 531 public String toString() { 532 return this.name; 533 } 534 535 public boolean isStruct() { 536 return isStruct; 537 } 538 539 public void setStruct(boolean isStruct) { 540 this.isStruct = isStruct; 541 } 542 543 public List<Transform> getTransforms() { 544 return transforms; 545 } 546 547 public void setTransform(Transform transform) { 548 if(transform==null) { 549 return; 550 } 551 if(transforms == null) { 552 transforms = new ArrayList<Transform>(); 553 } 554 if(!transforms.contains(transform)) { 555 transforms.add(transform); 556 } 557 } 558}