001package gudusoft.gsqlparser.nodes; 002 003 004import gudusoft.gsqlparser.EDataType; 005import gudusoft.gsqlparser.ESqlClause; 006import gudusoft.gsqlparser.ESqlStatementType; 007import gudusoft.gsqlparser.TCustomSqlStatement; 008import gudusoft.gsqlparser.nodes.TExpression; 009import gudusoft.gsqlparser.nodes.TParseTreeNode; 010import gudusoft.gsqlparser.nodes.TParseTreeVisitor; 011import gudusoft.gsqlparser.sqlenv.TSQLColumn; 012import gudusoft.gsqlparser.stmt.TSelectSqlStatement; 013 014 015public class TUnnestClause extends TParseTreeNode { 016 017 private TObjectNameList derivedColumnList = null; 018 private TAliasClause withOffsetAlais; 019 020 public TAliasClause getWithOffsetAlais() { 021 return withOffsetAlais; 022 } 023 024 public TDummy getWithOffset() { 025 return withOffset; 026 } 027 028 private TDummy withOffset; 029 private TObjectNameList columns; 030 031 public TObjectNameList getColumns() { 032 return columns; 033 } 034 035 private TExpression arrayExpr; 036 037 public TExpression getArrayExpr() { 038 return arrayExpr; 039 } 040 041 private TExpressionList exprList; 042 043 /** 044 * All UNNEST arguments as expressions (presto/trino), e.g. 045 * <code>UNNEST(array[1,2], array[3,4])</code> or 046 * <code>UNNEST(SPLIT(c, ','))</code>. Plain column arguments are also 047 * mirrored into {@link #getColumns()} for backward compatibility. 048 * 049 * @return the argument expression list, or null for the legacy forms 050 */ 051 public TExpressionList getExprList() { 052 return exprList; 053 } 054 055 public void init(Object arg1){ 056 if (arg1 instanceof TExpression){ 057 //bigquery, athena 058 arrayExpr = (TExpression)arg1; 059 }else if (arg1 instanceof TObjectNameList){ 060 //presto,bigquery 061 columns = (TObjectNameList)arg1; 062 }else if (arg1 instanceof TExpressionList){ 063 //presto/trino: UNNEST takes arbitrary array-typed expressions 064 exprList = (TExpressionList)arg1; 065 for(int i=0;i<exprList.size();i++){ 066 TExpression expr = exprList.getExpression(i); 067 if ((expr.getExpressionType() == gudusoft.gsqlparser.EExpressionType.simple_object_name_t) 068 && (expr.getObjectOperand() != null) 069 // placeholders (? / :b) are simple_object_name_t too but 070 // are not columns and must not be linked to a source table 071 && !isPlaceholderName(expr.getObjectOperand())){ 072 if (columns == null){ 073 columns = new TObjectNameList(); 074 } 075 columns.addObjectName(expr.getObjectOperand()); 076 } 077 } 078 } 079 080 } 081 082 public void init(Object arg1, Object arg2){ 083 init(arg1); 084 this.withOffset = (TDummy)arg2; 085 if (this.withOffset != null){ 086 if (this.withOffset.int1 == 2){ 087 this.withOffsetAlais = (TAliasClause)this.withOffset.node1; 088 } 089 } 090 } 091 092 private static boolean isPlaceholderName(TObjectName name){ 093 if (name.getObjectType() == TObjectName.ttobjPositionalParameters) return true; 094 String text = name.toString(); 095 // non-identifier-compare: placeholder markers, not database object names 096 return text.equals("?") || text.startsWith(":"); 097 } 098 099 public TObjectNameList getDerivedColumnList() { 100 return derivedColumnList; 101 } 102 103 public void doParse(TCustomSqlStatement psql, ESqlClause plocation) 104 { 105 if (exprList != null){ 106 // presto/trino: non-column arguments (function calls, array 107 // constructors, ...) resolve through the generic expression path; 108 // plain column arguments are mirrored in columns and linked below 109 for(int i=0;i<exprList.size();i++){ 110 TExpression expr = exprList.getExpression(i); 111 if (expr.getExpressionType() != gudusoft.gsqlparser.EExpressionType.simple_object_name_t){ 112 expr.doParse(psql,plocation); 113 } 114 } 115 } 116 if (arrayExpr != null){ 117 arrayExpr.doParse(psql,plocation); 118 }else if (columns != null){ 119 // link to the first table of the from clause 120 TTable lcFirstTable = null; 121 for(TObjectName pColumn:columns){ 122 if (psql.getTables().size() > 0){ 123 lcFirstTable = psql.getTables().getTable(0); 124 lcFirstTable.getLinkedColumns().addObjectName(pColumn); 125 pColumn.setSourceTable(lcFirstTable); 126 }else if ((psql.getParentStmt() != null) && (psql.getParentStmt().sqlstatementtype == ESqlStatementType.sstselect)){ 127 // search up level 128 TSelectSqlStatement selectSqlStatement = (TSelectSqlStatement)psql.getParentStmt(); 129 if (selectSqlStatement.getTables().size()>0){ 130 lcFirstTable = selectSqlStatement.getTables().getTable(0); 131 lcFirstTable.getLinkedColumns().addObjectName(pColumn); 132 pColumn.setSourceTable(lcFirstTable); 133 } 134 } 135 136 // get derived columns from this column if it's type of struct<> 137 // create table absolute-runner-302907.gudu_sqlflow.ADDRESS_NESTED ( 138 // Emp_id INT64,Name STRING 139 // ,Address ARRAY<STRUCT<State STRING, City STRING, Zipcode INT64>> ); 140 141 if ((psql.getSqlEnv() != null)&&(lcFirstTable != null)&&(lcFirstTable.getFullName()!=null)) { 142 // System.out.println("env\n"+psql.getSqlEnv().toString()); 143 144 TSQLColumn tsqlColumn = psql.getSqlEnv().getColumnInTable(lcFirstTable.getFullName(), pColumn.toString()); 145 if (tsqlColumn != null){ 146 TTypeName columnDataType = tsqlColumn.getColumnDataType(); 147 if (columnDataType != null){ 148 if (columnDataType.getDataType() == EDataType.array_t){ 149 TTypeName subDataType = columnDataType.getTypeOfList(); 150 if (subDataType.getDataType() == EDataType.struct_t){ 151 if (derivedColumnList == null){ 152 derivedColumnList = new TObjectNameList(); 153 } 154 //System.out.println("element name in struct: "+subDataType.getDataTypeName()); 155 for(int i=0;i<subDataType.getColumnDefList().size();i++){ 156 //System.out.println(subDataType.getColumnDefList().getColumn(i).getColumnName().toString()); 157 TObjectName attributeName = subDataType.getColumnDefList().getColumn(i).getColumnName(); 158 attributeName.setParentObjectName(pColumn); 159 derivedColumnList.addObjectName(attributeName); 160 } 161 } 162 } 163 else if (columnDataType.getDataType() == EDataType.struct_t){ 164 if (derivedColumnList == null){ 165 derivedColumnList = new TObjectNameList(); 166 } 167 //System.out.println("element name in struct: "+subDataType.getDataTypeName()); 168 for(int i=0;i<columnDataType.getColumnDefList().size();i++){ 169 //System.out.println(subDataType.getColumnDefList().getColumn(i).getColumnName().toString()); 170 TObjectName attributeName = columnDataType.getColumnDefList().getColumn(i).getColumnName(); 171 attributeName.setParentObjectName(pColumn); 172 derivedColumnList.addObjectName(attributeName); 173 } 174 } 175 } 176 //System.out.println("Find column: "+pColumn.toString()+" in table: "+ lcFirstTable.getFullName().toString()+" with datatype:"+tsqlColumn.getColumnDataType()); 177 } 178 } 179 180 } 181 } 182 } 183 184 public void accept(TParseTreeVisitor v){ 185 v.preVisit(this); 186 v.postVisit(this); 187 } 188 189 public void acceptChildren(TParseTreeVisitor v){ 190 v.preVisit(this); 191 if (this.getExprList() != null){ 192 // exprList contains every argument (columns included); visiting 193 // both it and getColumns() would visit the same nodes twice 194 this.getExprList().acceptChildren(v); 195 }else if (this.getColumns() != null){ 196 this.getColumns().acceptChildren(v); 197 } 198 if (this.getArrayExpr() != null){ 199 this.getArrayExpr().acceptChildren(v); 200 } 201 v.postVisit(this); 202 } 203}