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}