001
002package gudusoft.gsqlparser.dlineage.dataflow.model;
003
004import java.util.LinkedHashSet;
005import java.util.Set;
006
007import gudusoft.gsqlparser.EDbVendor;
008import gudusoft.gsqlparser.EExpressionType;
009import gudusoft.gsqlparser.ESetOperatorType;
010import gudusoft.gsqlparser.TSourceToken;
011import gudusoft.gsqlparser.dlineage.util.DlineageUtil;
012import gudusoft.gsqlparser.dlineage.util.Pair3;
013import gudusoft.gsqlparser.nodes.TObjectName;
014import gudusoft.gsqlparser.nodes.TResultColumn;
015import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType;
016import gudusoft.gsqlparser.sqlenv.TSQLEnv;
017import gudusoft.gsqlparser.stmt.TSelectSqlStatement;
018import gudusoft.gsqlparser.util.SQLUtil;
019
020public class SelectSetResultColumn extends ResultColumn {
021        
022        private LinkedHashSet<String> aliasSet = new LinkedHashSet<String>();
023
024    public SelectSetResultColumn(ResultSet resultSet,
025                                 TResultColumn resultColumnObject, int index) {
026        super(resultSet, resultColumnObject);
027        aliasSet.clear();
028        if (isNotSameAlias(resultSet, index)) {
029            this.name = aliasSet.iterator().next();
030            // Named by a branch's alias: a column, not the first branch's star.
031            setStarColumn(false);
032                } else {
033                        if (resultColumnObject.getAliasClause() != null) {
034                                this.name = resultColumnObject.getAliasClause().toString();
035                        } else if (resultColumnObject.getColumnNameOnly() != null
036                                        && !"".equals(resultColumnObject.getColumnNameOnly())) {
037                                this.name = resultColumnObject.getColumnNameOnly();
038                        } else if (resultColumnObject.getExpr().getExpressionType() == EExpressionType.simple_constant_t) {
039                                this.name = resultColumnObject.getExpr().toString();
040                        } 
041                        else {
042                                this.name = quote(resultColumnObject.toString());
043                        }
044                }
045
046        this.fullName = this.name;
047    }
048
049    private boolean isNotSameAlias(ResultSet resultSet, int index) {
050        if (resultSet instanceof SelectSetResultSet) {
051            SelectSetResultSet selectSetResultSet = (SelectSetResultSet) resultSet;
052            if (selectSetResultSet
053                    .getSetOperatorType() == ESetOperatorType.none) {
054                return false;
055            }
056            TSelectSqlStatement select = selectSetResultSet.getSelectObject();
057            return isNotSameAlias(select, index);
058        } else if (resultSet instanceof SelectResultSet) {
059            SelectResultSet selectResultSet = (SelectResultSet) resultSet;
060            if (selectResultSet.getSelectStmt()
061                    .getSetOperatorType() == ESetOperatorType.none) {
062                return false;
063            }
064            TSelectSqlStatement select = selectResultSet.getSelectStmt();
065            return isNotSameAlias(select, index);
066        }
067        return false;
068    }
069
070    private boolean isNotSameAlias(TSelectSqlStatement select, int index) {
071        if (starBefore(select, index)) {
072            return false;
073        }
074        collectSelectAlias(select, index, aliasSet);
075        aliasSet.remove("*");
076        return aliasSet.size() > 1;
077    }
078
079    /**
080     * Whether a select of the set operation {@code select} has a star before its item at {@code index}: the star's width
081     * leaves the item's position unknown, so the items at that index of the selects are no one column (Mantis 4914).
082     */
083    private static boolean starBefore(TSelectSqlStatement select, int index) {
084        java.util.Deque<TSelectSqlStatement> stack = new java.util.ArrayDeque<>();
085        stack.push(select);
086        while (!stack.isEmpty()) {
087            TSelectSqlStatement current = stack.pop();
088            if (current.getSetOperatorType() != ESetOperatorType.none) {
089                if (current.getRightStmt() != null) stack.push(current.getRightStmt());
090                if (current.getLeftStmt() != null) stack.push(current.getLeftStmt());
091                continue;
092            }
093            if (current.getResultColumnList() == null) {
094                continue;
095            }
096            for (int i = 0; i < index && i < current.getResultColumnList().size(); i++) {
097                TResultColumn item = current.getResultColumnList().getResultColumn(i);
098                if (item.getExpr() != null && item.getExpr().getExpressionType() == EExpressionType.simple_object_name_t
099                        && DlineageUtil.isStarName(item.getExpr().getObjectOperand())) {
100                    return true;
101                }
102            }
103        }
104        return false;
105    }
106
107    private void collectSelectAlias(TSelectSqlStatement select, int index,
108                                    Set<String> aliasSet) {
109        // Iterative traversal using explicit stack to avoid StackOverflow
110        java.util.Deque<TSelectSqlStatement> stack = new java.util.ArrayDeque<>();
111        stack.push(select);
112
113        while (!stack.isEmpty()) {
114            TSelectSqlStatement current = stack.pop();
115            if (current.getSetOperatorType() == ESetOperatorType.none) {
116                if (current.getResultColumnList() == null) {
117                    continue;
118                }
119                TResultColumn column = current.getResultColumnList()
120                        .getResultColumn(index);
121
122                String alias = null;
123                if (column == null) {
124                    continue;
125                }
126                if (column.getAliasClause() != null) {
127                    alias = column.getAliasClause().toString();
128                } else if (column.getColumnNameOnly() != null && !"".equals(column.getColumnNameOnly())) {
129                    alias = column.getColumnNameOnly();
130                } else if (column.getExpr().getExpressionType() == EExpressionType.simple_constant_t) {
131                    alias = column.getExpr().toString();
132                } else {
133                    alias = quote(column.toString());
134                }
135                aliasSet.add(alias);
136            } else {
137                // Push right first so left is processed first
138                if (current.getRightStmt() != null) stack.push(current.getRightStmt());
139                if (current.getLeftStmt() != null) stack.push(current.getLeftStmt());
140            }
141        }
142    }
143
144    private String quote(String column) {
145        EDbVendor vendor = ModelBindingManager.getGlobalVendor();
146        if (!DlineageUtil.isQuote(column)){
147                return SQLUtil.quoteDottedName(vendor, ESQLDataObjectType.dotColumn, column);
148        }
149        else return column;
150    }
151
152    public SelectSetResultColumn(ResultSet resultSet,
153                                 ResultColumn resultColumn) {
154        if (resultColumn == null || resultSet == null)
155            throw new IllegalArgumentException(
156                    "ResultColumn arguments can't be null.");
157
158        id = ++ModelBindingManager.get().TABLE_COLUMN_ID;
159
160        this.resultSet = resultSet;
161        resultSet.addColumn(this);
162        this.setStruct(resultColumn.isStruct());
163        inheritIdentifier(resultColumn);
164        inheritStarColumn(resultColumn);
165
166        if(resultColumn.getRefColumnName()!=null) {
167                TResultColumn resultColumnObject = (TResultColumn) resultColumn
168                    .getColumnObject();
169                TSourceToken startToken = resultColumnObject.getStartToken();
170            TSourceToken endToken = resultColumnObject.getEndToken();
171            this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo,
172                    startToken.columnNo, ModelBindingManager.getGlobalHash());
173            this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo,
174                    endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash());
175            this.columnObject = resultColumnObject;
176            this.refColumnName = resultColumn.getRefColumnName();
177            this.name = resultColumn.getName();
178        }
179        else if (resultColumn.getColumnObject() instanceof TResultColumn) {
180            TResultColumn resultColumnObject = (TResultColumn) resultColumn
181                    .getColumnObject();
182            if (resultColumnObject.getAliasClause() != null) {
183                this.alias = resultColumnObject.getAliasClause().toString();
184                TSourceToken startToken = resultColumnObject.getAliasClause()
185                        .getStartToken();
186                TSourceToken endToken = resultColumnObject.getAliasClause()
187                        .getEndToken();
188                this.aliasStartPosition = new Pair3<Long, Long, String>(
189                        startToken.lineNo, startToken.columnNo, ModelBindingManager.getGlobalHash());
190                this.aliasEndPosition = new Pair3<Long, Long, String>(endToken.lineNo,
191                        endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash());
192
193                this.name = this.alias;
194                // Named by its alias: a column, even over a whole-row struct.
195                setStarColumn(false);
196            } else if (resultColumnObject.getExpr()
197                    .getExpressionType() == EExpressionType.simple_object_name_t) {
198                this.name = resultColumnObject.getColumnNameOnly();
199            } else if (resultColumnObject.getFieldAttr() != null) {
200                this.name = resultColumnObject.getColumnNameOnly();
201            } else {
202                if (resultColumnObject.getExpr()
203                        .getExpressionType() == EExpressionType.simple_constant_t) {
204                    this.name = resultColumnObject.toString();
205                } else if (resultColumnObject.getExpr()
206                        .getExpressionType() == EExpressionType.function_t) {
207                    this.name = resultColumnObject.getCompactString();
208                } else if (resultColumnObject.getColumnNameOnly() != null
209                        && !"".equals(
210                        resultColumnObject.getColumnNameOnly())) {
211                    this.name = resultColumnObject.getColumnNameOnly();
212                } else {
213                    this.name = resultColumnObject.toString();
214                }
215            }
216
217            if (resultColumnObject.getExpr()
218                    .getExpressionType() == EExpressionType.function_t) {
219                this.fullName = resultColumnObject.getExpr()
220                        .getFunctionCall()
221                        .getFunctionName()
222                        .toString();
223            } else {
224                this.fullName = resultColumnObject.toString();
225            }
226
227            TSourceToken startToken = resultColumnObject.getStartToken();
228            TSourceToken endToken = resultColumnObject.getEndToken();
229            this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo,
230                    startToken.columnNo, ModelBindingManager.getGlobalHash());
231            this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo,
232                    endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash());
233            this.columnObject = resultColumnObject;
234        } else if (resultColumn.getColumnObject() instanceof TObjectName) {
235            TObjectName resultColumnObject = (TObjectName) resultColumn
236                    .getColumnObject();
237
238            if (resultColumnObject.getColumnNameOnly() != null
239                    && !"".equals(resultColumnObject.getColumnNameOnly())) {
240                this.name = resultColumnObject.getColumnNameOnly();
241            } else {
242                this.name = resultColumnObject.toString();
243            }
244
245            this.fullName = this.name;
246
247            TSourceToken startToken = resultColumnObject.getStartToken();
248            TSourceToken endToken = resultColumnObject.getEndToken();
249            this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo,
250                    startToken.columnNo, ModelBindingManager.getGlobalHash());
251            this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo,
252                    endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash());
253            this.columnObject = resultColumnObject;
254        } else {
255            this.name = resultColumn.getName();
256            this.fullName = this.name;
257
258            TSourceToken startToken = resultColumn.getColumnObject()
259                    .getStartToken();
260            TSourceToken endToken = resultColumn.getColumnObject()
261                    .getEndToken();
262            this.startPosition = new Pair3<Long, Long, String>(startToken.lineNo,
263                    startToken.columnNo, ModelBindingManager.getGlobalHash());
264            this.endPosition = new Pair3<Long, Long, String>(endToken.lineNo,
265                    endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash());
266            this.columnObject = resultColumn.getColumnObject();
267        }
268    }
269
270        public Set<String> getAliasSet() {
271                return aliasSet;
272        }
273}