001package gudusoft.gsqlparser.nodes;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.compiler.TSymbolTableManager;
005import gudusoft.gsqlparser.compiler.TVariable;
006import gudusoft.gsqlparser.sqlenv.TSQLTable;
007import gudusoft.gsqlparser.stmt.TSelectSqlStatement;
008
009import java.util.ArrayList;
010
011
012public class TIntoClause extends TParseTreeNode {
013
014    public void setBulkCollect(boolean bulkCollect) {
015        this.bulkCollect = bulkCollect;
016    }
017
018    public boolean isBulkCollect() {
019
020        return bulkCollect;
021    }
022
023
024    public TObjectName getTableName() {
025        return tableName;
026    }
027
028    private TObjectName tableName;
029
030    private boolean bulkCollect = false;
031    private TSourceToken filename;
032    private TExpressionList exprList = null;
033    private TObjectName intoName = null;
034    private TObjectNameList variableList = null;
035    private ETableKind tableKind = ETableKind.etkBase;
036    private boolean tableKindDetected = false;
037
038    /**
039     * Returns the table kind for SELECT INTO targets (TEMPORARY, TEMP, LOCAL TEMPORARY, etc.).
040     * <p>
041     * This is consistent with {@link gudusoft.gsqlparser.stmt.TCreateTableSqlStatement#getTableKinds()}
042     * but returns a single ETableKind value since SELECT INTO can only specify one kind.
043     * <p>
044     * For SQL Server, table kind is detected from the # or ## prefix on the table name.
045     * For PostgreSQL, Redshift, Greenplum, and Netezza, table kind is detected from
046     * TEMPORARY/TEMP/LOCAL/GLOBAL/UNLOGGED keywords in the INTO clause.
047     *
048     * @return the table kind, or {@link ETableKind#etkBase} if no special kind is specified
049     */
050    public ETableKind getTableKind() {
051        if (!tableKindDetected) {
052            detectTableKindFromTokens();
053        }
054        return tableKind;
055    }
056
057    public void setTableKind(ETableKind tableKind) {
058        this.tableKind = tableKind;
059        this.tableKindDetected = true;
060    }
061
062    public TObjectNameList getVariableList() {
063        return variableList;
064    }
065
066    public void setIntoName(TObjectName intoName) {
067        this.intoName = intoName;
068    }
069
070    public TObjectName getIntoName() {
071        if (intoName != null){
072            return intoName;
073        }else{
074           return exprList.getExpression(0).getObjectOperand();
075        }
076    }
077
078    public TExpressionList getExprList() {
079        return exprList;
080    }
081
082    public TSourceToken getFilename() {
083        return filename;
084    }
085
086    public void init(Object arg1)
087    {
088        if (arg1 instanceof TExpressionList){
089            exprList = (TExpressionList)arg1;
090        }else if (arg1 instanceof TSourceToken){
091            filename = (TSourceToken)arg1;
092        }else if (arg1 instanceof TObjectNameList){
093            variableList = (TObjectNameList)arg1;
094            for(int i=0;i<variableList.size();i++){
095                if (variableList.getObjectName(i).toString().startsWith("#")){
096                    // #temp_table, not a variable
097                    variableList.getObjectName(i).setDbObjectType(EDbObjectType.TEMP_TABLE);
098                }else{
099                    variableList.getObjectName(i).setDbObjectType(EDbObjectType.variable);
100                }
101
102            }
103            intoName = variableList.getObjectName(0);
104        }else if (arg1 instanceof TObjectName){
105            tableName = (TObjectName)arg1;
106        }
107    }
108
109   /**
110    * Detects the table kind (TEMPORARY, TEMP, LOCAL, GLOBAL, UNLOGGED, VOLATILE)
111    * by scanning the tokens between the INTO keyword (startToken) and the table name.
112    * This handles SELECT INTO TEMPORARY TABLE, SELECT INTO LOCAL TEMP TABLE, etc.
113    */
114   private void detectTableKindFromTokens() {
115       tableKindDetected = true;
116       TSourceToken st = getStartToken();
117       if (st == null) return;
118
119       boolean hasLocal = false;
120       boolean hasGlobal = false;
121
122       // Scan tokens after startToken (INTO) looking for table-kind keywords
123       st = st.getNextTokenInChain();
124       TSourceToken endSt = getEndToken();
125       while (st != null && st != endSt) {
126           if (st.tokencode == TBaseType.lexspace || st.tokencode == TBaseType.lexnewline
127                   || st.tokencode == TBaseType.cmtslashstar || st.tokencode == TBaseType.cmtdoublehyphen) {
128               st = st.getNextTokenInChain();
129               continue;
130           }
131           String text = st.toString().toUpperCase();
132           if (text.equals("LOCAL")) {
133               hasLocal = true;
134           } else if (text.equals("GLOBAL")) {
135               hasGlobal = true;
136           } else if (text.equals("TEMPORARY")) {
137               if (hasLocal) tableKind = ETableKind.etkLocalTemporary;
138               else if (hasGlobal) tableKind = ETableKind.etkGlobalTemporary;
139               else tableKind = ETableKind.etkTemporary;
140               return;
141           } else if (text.equals("TEMP")) {
142               if (hasLocal) tableKind = ETableKind.etkLocalTemp;
143               else if (hasGlobal) tableKind = ETableKind.etkGlobalTemp;
144               else tableKind = ETableKind.etkTemp;
145               return;
146           } else if (text.equals("UNLOGGED")) {
147               tableKind = ETableKind.etkUnlogged;
148               return;
149           } else if (text.equals("VOLATILE")) {
150               if (hasLocal) tableKind = ETableKind.etkLocalVolatile;
151               else if (hasGlobal) tableKind = ETableKind.etkGlobalVolatile;
152               else tableKind = ETableKind.etkVolatile;
153               return;
154           } else if (text.equals("STRICT") || text.equals("TABLE")) {
155               // Skip known non-kind keywords in INTO clause
156           } else {
157               // Reached the table name or unknown token, stop scanning
158               break;
159           }
160           st = st.getNextTokenInChain();
161       }
162   }
163
164   public void doParse(TCustomSqlStatement psql, ESqlClause plocation){
165       // Detect table kind from tokens (TEMPORARY, TEMP, LOCAL, GLOBAL, etc.)
166       if (!tableKindDetected) {
167           detectTableKindFromTokens();
168       }
169
170       if (exprList != null){
171           for(int i=0;i<exprList.size();i++){
172               TExpression expr = exprList.getExpression(i);
173               if (expr.getExpressionType() == EExpressionType.simple_object_name_t){
174                   TObjectName dbObject = expr.getObjectOperand();
175                   if ((psql.dbvendor == EDbVendor.dbvmssql)
176                           ||(psql.dbvendor == EDbVendor.dbvgreenplum)
177                           ||(psql.dbvendor == EDbVendor.dbvpostgresql)
178                           ||(psql.dbvendor == EDbVendor.dbvsybase)
179                           ||(psql.dbvendor == EDbVendor.dbvodbc)
180                           ||(psql.dbvendor == EDbVendor.dbvredshift)
181                           ||(psql.dbvendor == EDbVendor.dbvnetezza)
182                           ){  // database support: select .. into table
183                       if (dbObject.toString().startsWith("@")){
184                           // table variable
185                           dbObject.setObjectType(TObjectName.ttobjVariable);
186                       }else {
187                           TVariable symbolVariable =  TSymbolTableManager.searchSymbolVariable(psql.getFrameStack(),dbObject.toString()); //psql.searchSymbolVariable(dbObject.toString());
188                           if (symbolVariable != null){
189                               dbObject.setDbObjectType(EDbObjectType.variable);
190                           }else{
191                               TTable table = new TTable();
192                               dbObject.setObjectType(TObjectName.ttobjTable);
193                               if (tableKind != ETableKind.etkBase) {
194                                   // Table kind detected from TEMPORARY/TEMP/etc. keywords
195                                   dbObject.setTableKind(tableKind);
196                               } else if (dbObject.toString().startsWith("##")){
197                                   // SQL Server global temporary table (##tablename)
198                                   dbObject.setTableKind(ETableKind.etkGlobalTemporary);
199                               } else if (dbObject.toString().startsWith("#")){
200                                   // SQL Server local temporary table (#tablename)
201                                   dbObject.setTableKind(ETableKind.etkTemporary);
202                               }
203                               table.setTableName(dbObject);
204                               table.setTableType(ETableSource.objectname);
205                               table.setEffectType(ETableEffectType.tetSelectInto);
206                               psql.addToTables(table);
207                               if (psql instanceof TSelectSqlStatement){
208                                   TSelectSqlStatement subQuery = ((TSelectSqlStatement)psql);
209                                   table.setColumnListInTempTable(subQuery.getResultColumnList());
210
211                                   TSQLTable sqlTmpTable = null;
212                                   if (psql.getSqlEnv() != null)  {
213                                       sqlTmpTable = psql.getSqlEnv().addTable(dbObject.toString(),true);
214
215                                       if (subQuery.getResultColumnList().getResultColumn(0).toString().endsWith("*")){
216                                           // select * into #tmp, we need to find real columns in from clause
217                                           // if (subQuery.getTables().getTable())
218                                           ArrayList<String> columns = subQuery.getColumnsInFromClause();
219                                           if (columns.size() > 0){
220                                               for(String c:columns){
221                                                   sqlTmpTable.addColumn(c);
222                                               }
223                                           }
224                                       }else{
225                                           subQuery.addColumnInSelectListToSQLEnv(sqlTmpTable);
226                                       }
227                                   }
228                               }
229                           }
230                       }
231                   }else { // database support: select .. into variable
232                       if (dbObject.toString().startsWith(":")){
233                           // oracle, INTO :NEW.DI_RESID
234                           expr.doParse(psql, ESqlClause.selectInto);
235                       }else{
236                           if (dbObject.toString().startsWith("#")){
237                                // #temp_table, not a variable
238                               dbObject.setDbObjectType(EDbObjectType.TEMP_TABLE);
239                           }else{
240                               dbObject.setObjectType(TObjectName.ttobjVariable);
241                               dbObject.setDbObjectType(EDbObjectType.variable);
242                           }
243                       }
244                   }
245               }else{
246                   expr.doParse(psql, ESqlClause.selectInto);
247               }
248           }
249       }
250    }
251
252    //public void addTableToList()
253
254    public void accept(TParseTreeVisitor v){
255        v.preVisit(this);
256        v.postVisit(this);
257
258    }
259
260    public void acceptChildren(TParseTreeVisitor v){
261        v.preVisit(this);
262        v.postVisit(this);
263    }
264
265    public void setExprList(TExpressionList exprList) {
266        this.exprList = exprList;
267    }
268}