001package gudusoft.gsqlparser.nodes; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.ESqlClause; 005import gudusoft.gsqlparser.TBaseType; 006import gudusoft.gsqlparser.TCustomSqlStatement; 007import gudusoft.gsqlparser.TSourceToken; 008import gudusoft.gsqlparser.sqlenv.TSQLEnv; 009 010import java.util.ArrayList; 011import java.util.Collections; 012 013/* 014* Date: 2010-2-3 015* Time: 11:13:19 016*/ 017public class TResultColumnList extends TParseTreeNodeList <TResultColumn>{ 018 019 private ArrayList<TResultColumn> sortedResultColumns = null; 020 021 private boolean isSorted = false; 022 023 public ArrayList<TResultColumn> getSortedColumns(){ 024 if (isSorted) return sortedResultColumns; 025 if (sortedResultColumns == null){ 026 sortedResultColumns = new ArrayList<>(); 027 } 028 for(int i=0;i<size();i++){ 029 TResultColumn rc = getResultColumn(i); 030 sortedResultColumns.add(rc); 031 } 032 Collections.sort(sortedResultColumns); 033 isSorted = true; 034 return sortedResultColumns; 035 } 036 037 public TResultColumnList() 038 { 039 } 040 041 042 public void removeResultColumn(int index){ 043 this.removeItem(index); 044 } 045 046 public TParseTreeNode removeItem(int index){ 047 TSourceToken st=null ; 048 if ((index<0)||(index>size())) return null; 049 050 if (TParseTreeNode.doubleLinkedTokenListToString){ 051 // getResultColumn(index).removeTokens(); 052 053 }else { 054 if (size() > 1){ 055 if (index != size() - 1){ 056 st = getResultColumn(index).getEndToken().searchToken(",",1); 057 }else{ 058 st = getResultColumn(index).getStartToken().searchToken(",",-1); 059 } 060 } 061 getResultColumn(index).removeAllMyTokensFromTokenList(st); 062 } 063 064 065 return super.removeItem(index); 066 } 067 068 /** 069 * Used to add a result column manually when re-construct a select list 070 * There must exist at least one column in column list in order to this function. 071 * @param ptext 072 */ 073 public void addResultColumn(String ptext){ 074 if (size() == 0) return; 075 076 TResultColumn column = new TResultColumn(); 077 column.setGsqlparser(this.getGsqlparser()); 078 column.setString(","+ptext); 079 080 TResultColumn last_column = getResultColumn(size()-1); 081 TSourceToken last_token = last_column.getEndToken(); 082 083 column.addAllMyTokensToTokenList(last_token.container, last_token.posinlist + 1); 084 085 for(int i=0;i<last_token.getNodesEndWithThisToken().size();i++){ 086 TParseTreeNode node = last_token.getNodesEndWithThisToken().get(i); 087 if (!(node instanceof TResultColumn)){ 088 // change all end token of parse tree node except the last result column 089 node.setEndToken(column.getEndToken()); 090 } 091 } 092 093 } 094 095 /** 096 * Add a ResultColumn to the list 097 * 098 * @param resultColumn The ResultColumn to add to the list 099 */ 100 101 public void addResultColumn(TResultColumn resultColumn) 102 { 103 /* Vectors are 0-based, ResultColumns are 1-based */ 104 // resultColumn.setVirtualColumnId(size() + 1); 105 addElement(resultColumn); 106 } 107 108 /** 109 * Get a ResultColumn from a column position (0-based) in the list 110 * 111 * @param position The ResultColumn to get from the list (1-based) 112 * 113 * @return the column at that position. 114 */ 115 116 public TResultColumn getResultColumn(int position) 117 { 118 if (position < size()) 119 { 120 return (TResultColumn)elementAt(position); 121 }else{ 122 return null; 123 } 124 } 125 126 void addParseTreeNode(Object arg1){ 127 addResultColumn((TResultColumn)arg1); 128 } 129 130 131 /** 132 * In teradata, the whole result column can be null, so this result column 133 * should be ingored when get start token in result column list. 134 * @return 135 */ 136 public TSourceToken getStartToken() { 137 TSourceToken ret = null; 138 for(int i=0;i<this.size();i++){ 139 ret = this.getResultColumn(i).getStartToken(); 140 if (ret != null){ 141 break; 142 }; 143 } 144 return ret; 145 } 146 147 public TSourceToken getEndToken() { 148 TSourceToken ret = null; 149 for(int i = this.size() -1; i >= 0; i--){ 150 ret = this.getResultColumn(i).getEndToken(); 151 if (ret != null){ 152 break; 153 }; 154 } 155 return ret; 156 } 157 158 public void doParse(TCustomSqlStatement psql, ESqlClause plocation){ 159 for(int i=0;i<size();i++){ 160 getResultColumn(i).doParse(psql,plocation); 161 } 162 } 163 164 /** 165 * Find the SELECT-list column alias that an unqualified reference elsewhere in the 166 * same SELECT block binds to — a "lateral column alias" reference such as 167 * Snowflake's {@code SELECT val*2 AS derived_col FROM t WHERE derived_col > 1}. 168 * 169 * <p>This is the SINGLE definition of that binding rule. Both consumers delegate 170 * here so the two paths can never drift apart:</p> 171 * <ul> 172 * <li><b>publish</b> — Phase 1 {@code TCustomSqlStatement.linkColumnToTable()}, 173 * which sets {@code sourceColumn} and deliberately leaves 174 * {@code sourceTable} null;</li> 175 * <li><b>lookup</b> — resolver2 176 * {@code TSQLResolver2.handleSelectListAliasResolution()}.</li> 177 * </ul> 178 * <p>Fixing one and not the other yields a reference whose {@code sourceColumn} 179 * is the alias while its {@code sourceTable} is a FROM-clause table that has no 180 * such column — a wrong answer with nothing to flag it (MantisBT 4659).</p> 181 * 182 * <p>The rule is vendor-gated twice: the dialect must support lateral column 183 * aliases at all ({@link TBaseType#isSupportLateralColumn}), and it must not 184 * forbid referencing a SELECT-list alias outside ORDER BY 185 * ({@link TSQLEnv#isAliasReferenceForbidden}). Oracle, MySQL, PostgreSQL, 186 * BigQuery and friends forbid it, so for them such a name is NOT an alias 187 * reference and this returns null.</p> 188 * 189 * <p>The reference must also appear textually AFTER the alias it binds to. That 190 * ordering rule is what keeps a column inside the alias's own defining 191 * expression ({@code CAST(id AS DECIMAL) AS id}) bound to the real table column 192 * rather than to the alias being defined.</p> 193 * 194 * @param vendor the dialect of the enclosing statement 195 * @param reference an unqualified column reference from the same SELECT block 196 * @return the defining {@link TResultColumn}, or {@code null} when this is not a 197 * lateral column alias reference 198 */ 199 public TResultColumn findLateralAliasDefinition(EDbVendor vendor, TObjectName reference){ 200 if ((vendor == null) || (reference == null)) return null; 201 if (!TBaseType.isSupportLateralColumn(vendor)) return null; 202 Boolean forbidden = TSQLEnv.isAliasReferenceForbidden.get(vendor); 203 if ((forbidden == null) || forbidden.booleanValue()) return null; 204 if (reference.isQualified()) return null; 205 if (reference.getStartToken() == null) return null; 206 207 for(int i=0;i<size();i++){ 208 TResultColumn resultColumn = getResultColumn(i); 209 if (resultColumn == null) continue; 210 if (!resultColumn.isMatchedUsingAlias(reference)) continue; 211 TAliasClause alias = resultColumn.getAliasClause(); 212 if ((alias == null) || (alias.getStartToken() == null)) continue; 213 // only a BACKWARD reference binds to the alias 214 if (reference.getStartToken().posinlist > alias.getStartToken().posinlist){ 215 return resultColumn; 216 } 217 } 218 return null; 219 } 220 221 public void accept(TParseTreeVisitor v){ 222 v.preVisit(this); 223 v.postVisit(this); 224 } 225// 226 public void acceptChildren(TParseTreeVisitor v){ 227 v.preVisit(this); 228 for(int i=0;i<this.size();i++){ 229 this.getResultColumn(i).acceptChildren(v); 230 } 231 v.postVisit(this); 232 } 233 234}