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}