001
002package gudusoft.gsqlparser.dlineage.dataflow.model;
003
004import gudusoft.gsqlparser.EDbVendor;
005import gudusoft.gsqlparser.EExpressionType;
006import gudusoft.gsqlparser.ESetOperatorType;
007import gudusoft.gsqlparser.TCustomSqlStatement;
008import gudusoft.gsqlparser.dlineage.util.DlineageUtil;
009import gudusoft.gsqlparser.dlineage.util.Pair;
010import gudusoft.gsqlparser.dlineage.util.Pair3;
011import gudusoft.gsqlparser.dlineage.util.SHA256;
012import gudusoft.gsqlparser.nodes.*;
013import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType;
014import gudusoft.gsqlparser.sqlenv.TSQLEnv;
015import gudusoft.gsqlparser.stmt.*;
016import gudusoft.gsqlparser.stmt.mssql.TMssqlDeclare;
017import gudusoft.gsqlparser.util.IndexedLinkedHashMap;
018import gudusoft.gsqlparser.util.Logger;
019import gudusoft.gsqlparser.util.LoggerFactory;
020import gudusoft.gsqlparser.util.SQLUtil;
021
022import java.util.*;
023
024@SuppressWarnings({ "unchecked", "rawtypes" })
025public class ModelBindingManager {
026
027        private static final Logger logger = LoggerFactory.getLogger(ModelBindingManager.class);
028
029        private final Map modelBindingMap = new LinkedHashMap();
030        private final Map processModelBindingMap = new LinkedHashMap();
031        private final Map viewModelBindingMap = new LinkedHashMap();
032        private final Map insertModelBindingMap = new LinkedHashMap();
033        private final Map createModelBindingMap = new LinkedHashMap();
034        private final Map createModelQuickBindingMap = new LinkedHashMap();
035        private final Map mergeModelBindingMap = new LinkedHashMap();
036        private final Map updateModelBindingMap = new LinkedHashMap();
037        private final Map cursorModelBindingMap = new LinkedHashMap();
038        private final Map functionTableMap = new LinkedHashMap<>();
039        private final Map tableNamesMap = new LinkedHashMap();
040        private final Set<Table> dropTables = new HashSet<Table>();
041        private final Map procedureNamesMap = new LinkedHashMap();
042        private final Map oraclePackageNamesMap = new LinkedHashMap();
043        private final Set<Relationship> relationHolder = Collections.synchronizedSet(new LinkedHashSet<Relationship>());
044        private final Map<String, TTable> tableAliasMap = new LinkedHashMap();
045        private final Map<TCustomSqlStatement, String> tableHashMap = new LinkedHashMap();
046        private final Map<TCustomSqlStatement, String> topStmtHashMap = new HashMap();
047        private final Map hashSQLMap = new HashMap();
048        private final Map dynamicSQLMap = new HashMap();
049        private final Set tableSet = new LinkedHashSet();
050        private final Set resultSetSet = new LinkedHashSet();
051        private final Set<String> dblinkTableSet = new LinkedHashSet();
052
053        // Pipelined function stitching state
054        private final Map<String, List<String>> objectTypeIndex = new LinkedHashMap<>();
055        private final Map<String, String> collectionToElementTypeIndex = new LinkedHashMap<>();
056        private final Map<String, List<PipelinedFunctionSignature>> pipelinedSignatures = new LinkedHashMap<>();
057        private final List<PendingPipelinedCallSite> pendingPipelinedCallSites = new ArrayList<>();
058        private final Set<String> resolvingPipelinedFunctions = new HashSet<>();
059
060        public Map<String, Long> DISPLAY_ID = new LinkedHashMap<String, Long>();
061        public Map<Long, String> DISPLAY_NAME = new LinkedHashMap<Long, String>();
062        public Map<String, String> virtualTableNames = new LinkedHashMap<String, String>();
063
064        public long TABLE_COLUMN_ID = 0;
065        public long RELATION_ID = 0;
066        public int virtualTableIndex = -1;
067
068        private final static ThreadLocal localManager = new ThreadLocal();
069        private final static ThreadLocal globalDatabase = new ThreadLocal();
070        private final static ThreadLocal globalSchema = new ThreadLocal();
071        private final static ThreadLocal schemaUnsetStatements = new ThreadLocal();
072        private final static ThreadLocal globalVendor = new ThreadLocal();
073        private final static ThreadLocal globalSQLEnv = new ThreadLocal();
074        private final static ThreadLocal globalHash = new ThreadLocal();
075        private final static ThreadLocal globalStmtStack = new ThreadLocal();
076        private final static ThreadLocal globalSqlInfo = new ThreadLocal();
077        private final static ThreadLocal globalOption = new ThreadLocal();
078        private final static ThreadLocal globalOraclePackage = new ThreadLocal();
079        private final static ThreadLocal globalProcedure = new ThreadLocal();
080
081        public static void set(ModelBindingManager modelManager) {
082                if (modelManager != null) {
083                        localManager.set(modelManager);
084                }
085        }
086
087        public static ModelBindingManager get() {
088                return (ModelBindingManager) localManager.get();
089        }
090
091        public static void setGlobalDatabase(String database) {
092                if (database != null) {
093                        globalDatabase.set(database);
094                }
095        }
096
097        public static String getGlobalDatabase() {
098                return (String) globalDatabase.get();
099        }
100
101        public static void removeGlobalDatabase() {
102                globalDatabase.remove();
103        }
104
105        public static void setGlobalSchema(String schema) {
106                if (schema != null) {
107                        globalSchema.set(schema);
108                }
109        }
110
111        public static void removeGlobalSchema() {
112                globalSchema.remove();
113        }
114
115        public static String getGlobalSchema() {
116                return (String) globalSchema.get();
117        }
118
119        /**
120         * The statements that a Snowflake {@code USE <database>} left with NO current
121         * schema, i.e. everything after such a switch up to the next schema switch.
122         *
123         * <p>Recorded as a set of statements rather than as a running flag on
124         * purpose. The analyzer walks the statement list several times - one pass for
125         * CREATE TABLE, another for views, another for procedures - replaying the
126         * {@code USE} statements in each. A running flag set during one pass is still
127         * set when a LATER pass reaches a statement that precedes the switch, so it
128         * would apply the clearing backwards and strip an already-exact qualification
129         * off a statement that ran while the old schema was still current. Keyed by
130         * statement, the answer is the same no matter which pass asks.
131         *
132         * <p>See GitHub #715 / MantisBT 4677.
133         */
134        public static void setSchemaUnsetStatements(Set<TCustomSqlStatement> statements) {
135                if (statements == null || statements.isEmpty()) {
136                        schemaUnsetStatements.remove();
137                } else {
138                        schemaUnsetStatements.set(statements);
139                }
140        }
141
142        public static void removeSchemaUnsetStatements() {
143                schemaUnsetStatements.remove();
144        }
145
146        /**
147         * Whether the statement being analyzed right now has no current schema
148         * because a Snowflake {@code USE <database>} unset it.
149         *
150         * @see #setSchemaUnsetStatements(Set)
151         */
152        @SuppressWarnings("unchecked")
153        public static boolean isGlobalSchemaUnset() {
154                Set<TCustomSqlStatement> unset = (Set<TCustomSqlStatement>) schemaUnsetStatements.get();
155                if (unset == null || unset.isEmpty()) {
156                        return false;
157                }
158                Stack<TCustomSqlStatement> stack = getGlobalStmtStack();
159                if (stack == null || stack.isEmpty()) {
160                        return false;
161                }
162                return unset.contains(DlineageUtil.getTopStmt(stack.peek()));
163        }
164
165        public static void setGlobalHash(String hash) {
166                if (hash != null) {
167                        globalHash.set(hash);
168                }
169        }
170
171        public static void removeGlobalHash() {
172                globalHash.remove();
173        }
174
175        public static String getGlobalHash() {
176                return (String) globalHash.get();
177        }
178
179        public static void setGlobalOraclePackage(OraclePackage currentOraclePackage) {
180                if (currentOraclePackage != null) {
181                        globalOraclePackage.set(currentOraclePackage);
182                }
183        }
184
185        public static void removeGlobalOraclePackage() {
186                globalOraclePackage.remove();
187        }
188
189        public static OraclePackage getGlobalOraclePackage() {
190                return (OraclePackage) globalOraclePackage.get();
191        }
192        
193        public static void setGlobalProcedure(TStoredProcedureSqlStatement procedureSqlStatement) {
194                if (procedureSqlStatement != null) {
195                        globalProcedure.set(procedureSqlStatement);
196                }
197        }
198
199        public static void removeGlobalProcedure() {
200                globalProcedure.remove();
201                ModelBindingManager.get().cleanTempTable();
202        }
203
204        public static TStoredProcedureSqlStatement getGlobalProcedure() {
205                return (TStoredProcedureSqlStatement) globalProcedure.get();
206        }
207        
208        public static void setGlobalVendor(EDbVendor vendor) {
209                if (vendor != null) {
210                        globalVendor.set(vendor);
211                }
212        }
213
214        public static void removeGlobalVendor() {
215                globalVendor.remove();
216        }
217
218        public static EDbVendor getGlobalVendor() {
219                return (EDbVendor) globalVendor.get();
220        }
221
222        public static void setGlobalSQLEnv(TSQLEnv sqlenv) {
223                if (sqlenv != null) {
224                        globalSQLEnv.set(sqlenv);
225                }
226        }
227
228        public static void removeGlobalSQLEnv() {
229                globalSQLEnv.remove();
230        }
231
232        public static TSQLEnv getGlobalSQLEnv() {
233                return (TSQLEnv) globalSQLEnv.get();
234        }
235
236        public static String getGlobalServer() {
237                TSQLEnv sqlEnv = getGlobalSQLEnv();
238                if (sqlEnv != null)
239                        return TSQLEnv.DEFAULT_SERVER_NAME.equals(sqlEnv.getDefaultServerName()) ? null : sqlEnv.getDefaultServerName();
240                return null;
241        }
242
243        /**
244         * Resolve the effective default database (catalog) used to qualify
245         * unqualified / partial object names, with a three-tier precedence:
246         *
247         * <ol>
248         * <li>the active global database — an in-script {@code USE database}, or the
249         * per-group database set by {@code DataFlowAnalyzer};</li>
250         * <li>the per-analysis {@code Option.getDefaultDatabase()};</li>
251         * <li>the attached {@code TSQLEnv} default catalog;</li>
252         * <li>{@code TSQLEnv.DEFAULT_DB_NAME}.</li>
253         * </ol>
254         *
255         * Each lower tier is consulted only when the higher tier is null/empty. This
256         * lets a shared multi-catalog {@code TSQLEnv} be reused across analyses with
257         * different per-analysis default databases (tier 2) without mutating the env,
258         * while still honoring an in-script {@code USE database} (tier 1).
259         */
260        public static String getEffectiveDefaultDatabase() {
261                // tier 1: active global database (USE database / per-group database)
262                String database = getGlobalDatabase();
263                if (!SQLUtil.isEmpty(database)) {
264                        return database;
265                }
266                // tier 2: per-analysis Option default database
267                Option option = getGlobalOption();
268                if (option != null) {
269                        String optionDatabase = option.getDefaultDatabase();
270                        if (!SQLUtil.isEmpty(optionDatabase) && !TSQLEnv.DEFAULT_DB_NAME.equals(optionDatabase)) {
271                                return optionDatabase;
272                        }
273                }
274                // tier 3: attached TSQLEnv default catalog
275                TSQLEnv sqlenv = getGlobalSQLEnv();
276                if (sqlenv != null && !SQLUtil.isEmpty(sqlenv.getDefaultCatalogName())) {
277                        return sqlenv.getDefaultCatalogName();
278                }
279                // tier 4: fallback
280                return TSQLEnv.DEFAULT_DB_NAME;
281        }
282
283        /**
284         * The schema an unqualified name defaults to right now.
285         *
286         * <p>Six model classes used to carry an identical copy of this fallback; it
287         * lives here so the {@link #isGlobalSchemaUnset() unset} rule cannot be
288         * applied to some of them and forgotten on the others. Returns
289         * {@link TSQLEnv#DEFAULT_SCHEMA_NAME} when there is no schema to default to.
290         *
291         * @see #getEffectiveDefaultDatabase()
292         */
293        public static String getEffectiveDefaultSchema() {
294                // A Snowflake USE <database> unset the current schema for this statement.
295                // The attached env's default schema is batch-global -- TUseSchema writes
296                // it while PARSING, so it holds the LAST USE SCHEMA of the whole script --
297                // and falling back to it here would re-attach the previous database's
298                // schema to names that follow the switch. See GitHub #715.
299                if (isGlobalSchemaUnset()) {
300                        // Unset, unless the attached catalog PROVES what the switch landed
301                        // in. Snowflake makes PUBLIC current when the new database has one;
302                        // when the catalog shows it does, that is a fact rather than an
303                        // assumption, and discarding it would lose an exact resolution.
304                        String afterSwitch = null;
305                        if (getGlobalSQLEnv() != null) {
306                                afterSwitch = getGlobalSQLEnv().findSchemaAfterDatabaseSwitch(getEffectiveDefaultDatabase());
307                        }
308                        return SQLUtil.isEmpty(afterSwitch) ? TSQLEnv.DEFAULT_SCHEMA_NAME : afterSwitch;
309                }
310                String defaultSchema = null;
311                if (getGlobalSQLEnv() != null) {
312                        defaultSchema = getGlobalSQLEnv().getDefaultSchemaName();
313                }
314                if (!SQLUtil.isEmpty(defaultSchema)) {
315                        return defaultSchema;
316                }
317                return TSQLEnv.DEFAULT_SCHEMA_NAME;
318        }
319
320        public static void setGlobalStmtStack(Stack<TCustomSqlStatement> stack) {
321                if (stack != null) {
322                        globalStmtStack.set(stack);
323                }
324        }
325
326        public static void removeGlobalStmtStack() {
327                globalStmtStack.remove();
328        }
329        
330        public static void setGlobalSqlInfo(IndexedLinkedHashMap<String, List<SqlInfo>> sqlInfoMap) {
331                if (sqlInfoMap != null) {
332                        globalSqlInfo.set(sqlInfoMap);
333                }
334        }
335
336        public static void removeGlobalSqlInfo() {
337                globalSqlInfo.remove();
338        }
339
340        public static IndexedLinkedHashMap<String, List<SqlInfo>> getGlobalSqlInfo() {
341                return (IndexedLinkedHashMap<String, List<SqlInfo>>) globalSqlInfo.get();
342        }
343
344        public static void setGlobalOption(Option option) {
345                if (option != null) {
346                        globalOption.set(option);
347                }
348        }
349        
350        public static Option getGlobalOption() {
351                return (Option) globalOption.get();
352        }
353
354        public static void removeGlobalOption() {
355                globalOption.remove();
356        }
357
358        public static Stack<TCustomSqlStatement> getGlobalStmtStack() {
359                return (Stack<TCustomSqlStatement>) globalStmtStack.get();
360        }
361
362        public static void remove() {
363                localManager.remove();
364                globalDatabase.remove();
365                globalSchema.remove();
366                schemaUnsetStatements.remove();
367                globalVendor.remove();
368                globalSQLEnv.remove();
369                globalHash.remove();
370                globalStmtStack.remove();
371                globalOption.remove();
372                globalSqlInfo.remove();
373        }
374
375        public void bindModel(Object gspModel, Object relationModel) {
376                if(gspModel == null) {
377                        return;
378                }
379                modelBindingMap.put(gspModel, relationModel);
380
381                TTable table = null;
382                if (gspModel instanceof TTable && !(gspModel instanceof TCTE)) {
383                        table = ((TTable) gspModel);
384                } else if (gspModel instanceof Table) {
385                        table = ((Table) gspModel).getTableObject();
386                } else if (gspModel instanceof QueryTable) {
387                        table = ((QueryTable) gspModel).getTableObject();
388                } else if (gspModel instanceof TCTE) {
389                        TTableList tables = ((TCTE) gspModel).getPreparableStmt().tables;
390                        for (int j = 0; j < tables.size(); j++) {
391                                TTable item = tables.getTable(j);
392                                if (item != null && !SQLUtil.isEmpty(item.getAliasName())) {
393                                        TCustomSqlStatement stmt = ((TCTE) gspModel).getPreparableStmt();
394                                        if (tableHashMap.containsKey(stmt)) {
395                                                tableAliasMap.put(
396                                                                tableHashMap.get(stmt) + ":" + DlineageUtil.getIdentifierNormalTableName(item.getAliasName()),
397                                                                item);
398                                        } else {
399                                                String sql = stmt.toString();
400                                                if(sql == null){
401                                                        System.err.println("Current statement toString() returns null.");
402                                                        continue;
403                                                }
404                                                String hash = SHA256.getMd5(sql);
405                                                tableHashMap.put(stmt, hash);
406                                                tableAliasMap.put(hash + ":" + DlineageUtil.getIdentifierNormalTableName(item.getAliasName()), item);
407                                        }
408                                }
409
410                                if (item != null) {
411                                        tableSet.add(item);
412                                }
413                        }
414                } else if (gspModel instanceof Pair && ((Pair) gspModel).first instanceof Table) {
415                        table = ((Table) ((Pair) gspModel).first).getTableObject();
416                }
417
418                if (table == null && relationModel instanceof QueryTable) {
419                        table = ((QueryTable) relationModel).getTableObject();
420                }
421
422                updateTableAliasMap(table);
423
424                if (table != null) {
425                        tableSet.add(table);
426                }
427                
428                if(relationModel instanceof ResultSet) {
429                        resultSetSet.add(relationModel);
430                }
431        }
432
433        protected void updateTableAliasMap(TTable table) {
434                if (table != null && !SQLUtil.isEmpty(table.getAliasName())) {
435                        TCustomSqlStatement stmt = ModelBindingManager.getGlobalStmtStack().peek();
436                        if (tableHashMap.containsKey(stmt)) {
437                                tableAliasMap.put(tableHashMap.get(stmt) + ":" + DlineageUtil.getIdentifierNormalTableName(table.getAliasName()),
438                                                table);
439                        } else {
440                                String sql = stmt.toString();
441                                if(sql == null){
442                                        System.err.println("Current statement toString() returns null.");
443                                }
444                                else {
445                                        String hash = SHA256.getMd5(sql);
446                                        tableHashMap.put(stmt, hash);
447                                        tableAliasMap.put(hash + ":" + DlineageUtil.getIdentifierNormalTableName(table.getAliasName()),
448                                                        table);
449                                }
450                        }
451                }
452        }
453
454        public Object getModel(Object gspModel) {
455                if (gspModel == null) {
456                        return null;
457                }
458
459                if (gspModel instanceof TTable && !(gspModel instanceof TCTE)) {
460                        TTable table = (TTable) gspModel;
461                        if (table.getCTE() != null) {
462                                Object result =  modelBindingMap.get(table.getCTE());
463                                if (result != null) {
464                                        return result;
465                                }
466                        }
467                        if (table.getAliasClause() != null && table.getAliasClause().getColumns() != null) {
468                                Object result = modelBindingMap.get(table.getAliasClause().getColumns());
469                                if (result != null) {
470                                        return result;
471                                }
472                        }
473                        if (table.getSubquery() != null && !table.getSubquery().isCombinedQuery()) {
474                                Object result =  modelBindingMap.get(table.getSubquery().getResultColumnList());
475                                if (result != null) {
476                                        return result;
477                                }
478                        }
479                }
480                if (gspModel instanceof TSelectSqlStatement) {
481                        TSelectSqlStatement select = (TSelectSqlStatement) gspModel;
482                        if (!select.isCombinedQuery()) {
483                                if (select.getResultColumnList() != null) {
484                                        Object result = modelBindingMap.get(select.getResultColumnList());
485                                        if (result != null) {
486                                                return result;
487                                        }
488                                }
489                                else if (select.getTransformClause() != null) {
490                                        Object result = modelBindingMap.get(select.getTransformClause());
491                                        if (result != null) {
492                                                return result;
493                                        }
494                                }
495                        }
496                }
497                Object result = modelBindingMap.get(gspModel);
498                if (result == null) {
499                        result = createModelBindingMap.get(gspModel);
500                }
501                if (result == null) {
502                        result = insertModelBindingMap.get(gspModel);
503                }
504                if (result == null) {
505                        result = updateModelBindingMap.get(gspModel);
506                }
507                if (result == null) {
508                        result = mergeModelBindingMap.get(gspModel);
509                }
510                if (result == null) {
511                        result = viewModelBindingMap.get(gspModel);
512                }
513                if (result == null) {
514                        result = cursorModelBindingMap.get(gspModel);
515                }
516
517                if (result == null && gspModel instanceof TTable) {
518                        result = getCreateTable((TTable) gspModel);
519                }
520
521                if (result == null && gspModel instanceof TTable) {
522                        result = (Table) getCreateModel((TTable) gspModel);
523                }
524
525                if (result == null && gspModel instanceof TTable) {
526                        TTable table = (TTable) gspModel;
527                        if (table.getLinkTable() != null) {
528                                result = (Table) getTableByName(
529                                                DlineageUtil.getTableFullName(table.getLinkTable().getTableName().toString()));
530                                if (result == null) {
531                                        result = (Table) getTableByName(
532                                                        DlineageUtil.getTableFullNameWithDefaultSchema(table.getLinkTable().getTableName().toString()));
533                                }
534                        } else {
535                                result = (Table) getTableByName(
536                                                DlineageUtil.getTableFullName(((TTable) gspModel).getTableName().toString()));
537                                if (result == null) {
538                                        result = (Table) getTableByName(
539                                                        DlineageUtil.getTableFullNameWithDefaultSchema(((TTable) gspModel).getTableName().toString()));
540                                }
541                        }
542                }
543                
544                if (result == null && gspModel instanceof TTable) {
545                        TTable table = (TTable) gspModel;
546                        if(table.getFuncCall()!=null) {
547                                result = getModel(table.getFuncCall());
548                        }
549                }
550                
551                if (result == null && gspModel instanceof TTable) {
552                        TTable table = (TTable) gspModel;
553                        Table tableModel = new Table(table);
554                        result = (Table) getTableByName(DlineageUtil.getTableFullName(tableModel.getName()));
555                }
556
557                return result;
558        }
559
560        public List<Procedure> getProcedureModels(){
561                List<Procedure> procedures = new ArrayList<Procedure>();
562                for (Object obj : modelBindingMap.values()) {
563                        if(obj instanceof Procedure && !procedures.contains(obj)){
564                                procedures.add((Procedure)obj);
565                        }
566                }
567                return procedures;
568        }
569        
570        public List<OraclePackage> getOraclePackageModels(){
571                List<OraclePackage> oraclePackages = new ArrayList<OraclePackage>();
572                for (Object obj : modelBindingMap.values()) {
573                        if(obj instanceof OraclePackage && !oraclePackages.contains(obj)){
574                                oraclePackages.add((OraclePackage)obj);
575                        }
576                }
577                return oraclePackages;
578        }
579        
580        public List<Process> getProcessModels(){
581                List<Process> processes = new ArrayList<Process>();
582                for (Object obj : processModelBindingMap.values()) {
583                        if(obj instanceof Process && !processes.contains(obj)){
584                                processes.add((Process)obj);
585                        }
586                }
587                return processes;
588        }
589        
590        public void bindProcessModel(TParseTreeNode gspModel, Object relationModel) {
591                processModelBindingMap.put(gspModel, relationModel);
592        }
593
594        public void unbindProcessModel(TParseTreeNode gspModel) {
595                processModelBindingMap.remove(gspModel);
596        }
597
598        public Process getProcessModel(TParseTreeNode gspModel) {
599                return (Process)processModelBindingMap.get(gspModel);
600        }
601        
602        public void bindViewModel(Object gspModel, Object relationModel) {
603                viewModelBindingMap.put(gspModel, relationModel);
604        }
605        
606        public Object getViewModel(Object gspModel) {
607                return viewModelBindingMap.get(gspModel);
608        }
609
610        public void bindUpdateModel(Object gspModel, Object relationModel) {
611                updateModelBindingMap.put(gspModel, relationModel);
612        }
613
614        public Object getUpdateModel(Object gspModel) {
615                return updateModelBindingMap.get(gspModel);
616        }
617
618        public void bindMergeModel(Object gspModel, Object relationModel) {
619                mergeModelBindingMap.put(gspModel, relationModel);
620        }
621
622        public Object getMergeModel(Object gspModel) {
623                return mergeModelBindingMap.get(gspModel);
624        }
625
626        public void bindInsertModel(Object gspModel, Object relationModel) {
627                insertModelBindingMap.put(gspModel, relationModel);
628        }
629
630        public Object getInsertModel(Object gspModel) {
631                return insertModelBindingMap.get(gspModel);
632        }
633
634        public void bindTableFunction(Object gspModel, Object functionTable) {
635                if (functionTableMap.containsKey(gspModel)) {
636                        ((Set) functionTableMap.get(gspModel)).add(functionTable);
637                } else {
638                        Set tableSet = new LinkedHashSet();
639                        tableSet.add(functionTable);
640                        functionTableMap.put(gspModel, tableSet);
641                }
642        }
643
644        public Set<Object> getFunctionTable(Object gspModel) {
645                Set<Object> result = (Set<Object>)functionTableMap.get(gspModel);
646                if (result == null && gspModel instanceof String) {
647                        for (Object key : functionTableMap.keySet()) {
648                                if(!(key instanceof String)) {
649                                        continue;
650                                }
651                                String keyString = (String) key;
652                                String modelString = (String) gspModel;
653                                
654                                if (keyString.indexOf(".") != -1 && modelString.indexOf(".") == -1) {
655                                        keyString = keyString.substring(keyString.lastIndexOf(".") + 1);
656                                        if (DlineageUtil.sameName(ESQLDataObjectType.dotTable, keyString, modelString)) {
657                                                return (Set<Object>)functionTableMap.get(key);
658                                        }
659                                }
660                                if (keyString.indexOf(".") == -1 && modelString.indexOf(".") != -1) {
661                                        modelString = modelString.substring(modelString.lastIndexOf(".") + 1);
662                                        if (DlineageUtil.sameName(ESQLDataObjectType.dotTable, keyString, modelString)) {
663                                                return (Set<Object>)functionTableMap.get(key);
664                                        }
665                                }
666                                
667                        }
668                }
669                return result;
670        }
671
672        public Table getCreateTable(TTable table) {
673                if (table != null && table.getFullName() != null) {
674                        return (Table) createModelQuickBindingMap.get(DlineageUtil.getTableFullName(table.getTableName().toString()));
675                }
676                if(modelBindingMap.get(table) instanceof Table) {
677                        return (Table)modelBindingMap.get(table);
678                }
679                return null;
680        }
681
682        public Table getCreateModel(TTable table) {
683                return (Table) createModelBindingMap.get(table);
684        }
685
686        public void bindCreateModel(TTable table, Table tableModel) {
687                createModelBindingMap.put(table, tableModel);
688                if (!createModelQuickBindingMap.containsKey(DlineageUtil.getTableFullName(table.getTableName().toString()))) {
689                        createModelQuickBindingMap.put(DlineageUtil.getTableFullName(table.getTableName().toString()), tableModel);
690                }
691        }
692
693        public void bindCreateModel(TObjectName tableName, Table tableModel) {
694                if (!createModelQuickBindingMap.containsKey(DlineageUtil.getTableFullName(tableName.toString()))) {
695                        createModelQuickBindingMap.put(DlineageUtil.getTableFullName(tableName.toString()), tableModel);
696                }
697        }
698        
699        public void bindCreateModel(String tableName, Table tableModel) {
700                if (!createModelQuickBindingMap.containsKey(DlineageUtil.getTableFullName(tableName))) {
701                        createModelQuickBindingMap.put(DlineageUtil.getTableFullName(tableName), tableModel);
702                }
703        }
704
705        public TObjectName[] getTableColumns(TTable table) {
706                Table createTable = getCreateTable(table);
707                if (createTable != null) {
708                        List<TableColumn> columnList = createTable.getColumns();
709                        TObjectName[] columns = new TObjectName[columnList.size()];
710                        for (int i = 0; i < columns.length; i++) {
711                                columns[i] = columnList.get(i).getColumnObject();
712                        }
713                        Arrays.sort(columns, new Comparator<TObjectName>() {
714
715                                @Override
716                                public int compare(TObjectName o1, TObjectName o2) {
717                                        if (o1 == null) {
718                                                return -1;
719                                        }
720                                        if (o2 == null) {
721                                                return 1;
722                                        }
723                                        return o1.getStartToken().posinlist - o2.getStartToken().posinlist;
724                                }
725                        });
726                        return columns;
727                }
728                else if(modelBindingMap.get(table) instanceof QueryTable) {
729                        QueryTable queryTable = (QueryTable)modelBindingMap.get(table);
730                        List<TObjectName> columns = new ArrayList<TObjectName>();
731                        for (ResultColumn resultColumn : queryTable.getColumns()) {
732                                if(resultColumn.hasStarLinkColumn()) {
733                                        for(TObjectName column: resultColumn.getStarLinkColumnList()) {
734                                                columns.add(column);
735                                        }
736                                }
737                                else if (resultColumn.getColumnObject() instanceof TResultColumn) {
738                                        TResultColumn columnObject = ((TResultColumn) resultColumn.getColumnObject());
739                                        TAliasClause alias = columnObject.getAliasClause();
740                                        if (alias != null && alias.getAliasName() != null) {
741                                                if(resultColumn.isStruct() && resultColumn.getName().endsWith("*")) {
742                                                        TObjectName structColumn = new TObjectName();
743                                                        structColumn.setString("*");
744                                                        columns.add(structColumn);
745                                                }
746                                                else {
747                                                        columns.add(alias.getAliasName());
748                                                }
749                                        } else {
750                                                if (columnObject.getFieldAttr() != null) {
751                                                        columns.add(columnObject.getFieldAttr());
752                                                } else {
753                                                        TObjectName column = new TObjectName();
754                                                        if (columnObject.getExpr().getExpressionType() == EExpressionType.typecast_t) {
755                                                                column.setString(columnObject.getExpr().getLeftOperand().toString());
756                                                        } else {
757                                                                column.setString(columnObject.toString());
758                                                        }
759                                                        columns.add(column);
760                                                }
761                                        }
762                                } else if (resultColumn.getColumnObject() instanceof TObjectName) {
763                                        columns.add((TObjectName) resultColumn.getColumnObject());
764                                }
765                        }
766                        return columns.toArray(new TObjectName[0]);
767                }
768
769                TObjectNameList list = table.getLinkedColumns();
770                List<TObjectName> columns = new ArrayList<TObjectName>();
771
772                if (table.getCTE() != null) {
773                        ResultSet resultSet = (ResultSet) getModel(table.getCTE());
774                        if (resultSet != null) {
775                                List<ResultColumn> columnList = resultSet.getColumns();
776                                for (int i = 0; i < columnList.size(); i++) {
777                                        ResultColumn resultColumn = columnList.get(i);
778                                        if (resultColumn.getColumnObject() instanceof TResultColumn) {
779                                                TResultColumn columnObject = ((TResultColumn) resultColumn.getColumnObject());
780                                                TAliasClause alias = columnObject.getAliasClause();
781                                                if (alias != null && alias.getAliasName() != null) {
782                                                        columns.add(alias.getAliasName());
783                                                } else {
784                                                        if (columnObject.getFieldAttr() != null) {
785                                                                columns.add(columnObject.getFieldAttr());
786                                                        } else {
787                                                                continue;
788                                                        }
789                                                }
790                                        } else if (resultColumn.getColumnObject() instanceof TObjectName) {
791                                                columns.add((TObjectName) resultColumn.getColumnObject());
792                                        }
793                                }
794                        }
795                } else if (list.size() == 1 && list.toString().indexOf("*") != -1 && table.getSubquery() != null) {
796                        addSubqueryColumns(table, columns);
797                } else {
798                        for (int i = 0; i < list.size(); i++) {
799                                TObjectName object = list.getObjectName(i);
800                                if (object.toString().indexOf("*") != -1 && table.getSubquery() != null) {
801                                        addSubqueryColumns(table, columns);
802                                }
803                                else 
804                                        columns.add(object);
805                        }
806                }
807                Collections.sort(columns, new Comparator<TObjectName>() {
808
809                        @Override
810                        public int compare(TObjectName o1, TObjectName o2) {
811                                return o1.getStartToken().posinlist - o2.getStartToken().posinlist;
812                        }
813                });
814                return columns.toArray(new TObjectName[0]);
815        }
816
817        private void addSubqueryColumns(TTable table, List<TObjectName> columns) {
818                ResultSet resultSet = (ResultSet) getModel(table.getSubquery());
819                if (resultSet != null) {
820                        List<ResultColumn> columnList = resultSet.getColumns();
821                        for (int i = 0; i < columnList.size(); i++) {
822                                ResultColumn resultColumn = columnList.get(i);
823                                if (resultColumn.getColumnObject() instanceof TResultColumn) {
824                                        TResultColumn columnObject = ((TResultColumn) resultColumn.getColumnObject());
825                                        TAliasClause alias = columnObject.getAliasClause();
826                                        if (alias != null && alias.getAliasName() != null) {
827                                                columns.add(alias.getAliasName());
828                                        } else {
829                                                if (columnObject.getFieldAttr() != null) {
830                                                        columns.add(columnObject.getFieldAttr());
831                                                } else {
832                                                        TObjectName column = new TObjectName();
833                                                        if (columnObject.getExpr().getExpressionType() == EExpressionType.typecast_t) {
834                                                                column.setString(columnObject.getExpr().getLeftOperand().toString());
835                                                        } else {
836                                                                column.setString(columnObject.toString());
837                                                        }
838                                                        columns.add(column);
839                                                }
840                                        }
841                                } else if (resultColumn.getColumnObject() instanceof TObjectName) {
842                                        columns.add((TObjectName) resultColumn.getColumnObject());
843                                }
844                        }
845                }
846        }
847
848        public TTable getTableFromColumn(TObjectName column) {
849                if (column.getSourceTable() != null) {
850                        return column.getSourceTable();
851                }
852
853                if (column.getTableString() != null && column.getTableString().trim().length() > 0) {
854                        TCustomSqlStatement peekStmt = ModelBindingManager.getGlobalStmtStack().peek();
855                        while (peekStmt != null) {
856                                TTable table = null;
857                                if (tableHashMap.containsKey(peekStmt)) {
858                                        table = tableAliasMap.get(tableHashMap.get(peekStmt) + ":"
859                                                        + DlineageUtil.getIdentifierNormalTableName(column.getTableString()));
860                                }
861                                if (table != null) {
862                                        return table;
863                                } else {
864                                        peekStmt = peekStmt.getParentStmt();
865                                }
866                        }
867                }
868                return null;
869        }
870        
871        public TTable getTable(TCustomSqlStatement stmt, TObjectName column) {
872                if (column.getSourceTable() != null) {
873                        return column.getSourceTable();
874                }
875
876                if (column.getTableString() != null && column.getTableString().trim().length() > 0) {
877                        TCustomSqlStatement peekStmt = ModelBindingManager.getGlobalStmtStack().peek();
878                        while (peekStmt != null) {
879                                TTable table = null;
880                                if (tableHashMap.containsKey(peekStmt)) {
881                                        table = tableAliasMap.get(tableHashMap.get(peekStmt) + ":"
882                                                        + DlineageUtil.getIdentifierNormalTableName(column.getTableString()));
883                                }
884                                if (table != null && table.getSubquery() != stmt)
885                                        return table;
886                                
887                                if (peekStmt.getTables() != null) {
888                                        for (TTable tableItem : peekStmt.getTables()) {
889                                                if (DlineageUtil.getIdentifierNormalTableName(tableItem.getTableName().toString())
890                                                                .equals(DlineageUtil.getIdentifierNormalTableName(column.getTableString()))) {
891                                                        return tableItem;
892                                                }
893                                        }
894                                }
895                                
896                                peekStmt = peekStmt.getParentStmt();
897                        }
898                }
899
900                if (stmt.getTables() != null) {
901                        for (int j = 0; j < stmt.getTables().size(); j++) {
902                                TTable table = (TTable) stmt.getTables().getTable(j);
903                                if (table.getSubquery() == stmt)
904                                        continue;
905
906                                TObjectName[] columns = getTableColumns(table);
907                                for (int i = 0; i < columns.length; i++) {
908                                        TObjectName columnName = columns[i];
909                                        if (columnName == null || "*".equals(columnName.getColumnNameOnly()))
910                                                continue;
911                                        if (DlineageUtil.getIdentifierNormalColumnName(columnName.toString())
912                                                        .equals(DlineageUtil.getIdentifierNormalColumnName(column.toString()))) {
913                                                if (columnName.getSourceTable() == null || columnName.getSourceTable() == table) {
914                                                        return table;
915                                                }
916                                        }
917                                        if (!SQLUtil.isEmpty(columnName.getColumnNameOnly())
918                                                        && DlineageUtil.getIdentifierNormalColumnName(columnName.getColumnNameOnly())
919                                                                        .equals(DlineageUtil.getIdentifierNormalColumnName(column.getColumnNameOnly()))) {
920                                                if (columnName.getSourceTable() == null || columnName.getSourceTable() == table) {
921                                                        return table;
922                                                }
923                                        }
924                                }
925                        }
926                }
927
928                // Iterator iter = tableSet.iterator();
929                // while (iter.hasNext()) {
930                // TTable table = (TTable) iter.next();
931                //
932                // if (table.getSubquery() == stmt)
933                // continue;
934                //
935                // if (table.getSubquery() != null) {
936                // int start = table.getSubquery().getStartToken().posinlist;
937                // int end = table.getSubquery().getEndToken().posinlist;
938                // if (start <= stmt.getStartToken().posinlist && end >=
939                // stmt.getEndToken().posinlist) {
940                // continue;
941                // }
942                // }
943                //
944                // TObjectName[] columns = getTableColumns(table);
945                // for (int i = 0; i < columns.length; i++) {
946                // TObjectName columnName = columns[i];
947                // if (columnName == null || "*".equals(columnName.getColumnNameOnly()))
948                // continue;
949                // if
950                // (DlineageUtil.getIdentifierNormalName(columnName.toString()).equals(DlineageUtil.getIdentifierNormalName(column.toString())))
951                // {
952                // if (columnName.getSourceTable() == null
953                // || columnName.getSourceTable() == table) {
954                // return table;
955                // }
956                // }
957                // if (columnName.getColumnNameOnly()!=null &&
958                // DlineageUtil.getIdentifierNormalName(columnName.getColumnNameOnly()).equals(DlineageUtil.getIdentifierNormalName(column.getColumnNameOnly())))
959                // {
960                // if (columnName.getSourceTable() == null
961                // || columnName.getSourceTable() == table) {
962                // return table;
963                // }
964                // }
965                // }
966                // }
967                return null;
968        }
969
970        public TTable guessTable(TCustomSqlStatement stmt, TObjectName column) {
971                if (column.getTableString() != null && column.getTableString().trim().length() > 0) {
972                        TCustomSqlStatement peekStmt = ModelBindingManager.getGlobalStmtStack().peek();
973                        TTable table = null;
974                        if (tableHashMap.containsKey(peekStmt)) {
975                                table = tableAliasMap.get(
976                                                tableHashMap.get(peekStmt) + ":" + DlineageUtil.getIdentifierNormalTableName(column.getTableString()));
977                        }
978                        if (table != null && table.getSubquery() != stmt)
979                                return table;
980                }
981
982                Iterator iter = tableSet.iterator();
983                while (iter.hasNext()) {
984                        TTable table = (TTable) iter.next();
985
986                        if (table.getSubquery() == stmt)
987                                continue;
988
989                        TObjectName[] columns = getTableColumns(table);
990                        for (int i = 0; i < columns.length; i++) {
991                                TObjectName columnName = columns[i];
992                                if ("*".equals(columnName.getColumnNameOnly()))
993                                        continue;
994                                if (DlineageUtil.sameQualifiedName(ESQLDataObjectType.dotColumn, columnName.toString(), column.toString())) {
995                                        if (columnName.getSourceTable() == null || columnName.getSourceTable() == table) {
996                                                return table;
997                                        }
998                                }
999                        }
1000                }
1001                return null;
1002        }
1003
1004        public List<Table> getTablesByName() {
1005                List<Table> tables = new ArrayList<Table>();
1006                Iterator iter = tableNamesMap.values().iterator();
1007                while (iter.hasNext()) {
1008                        tables.add((Table) iter.next());
1009                }
1010                return tables;
1011        }
1012
1013        public List<TTable> getBaseTables() {
1014                List<TTable> tables = new ArrayList<TTable>();
1015
1016                Iterator iter = modelBindingMap.keySet().iterator();
1017                while (iter.hasNext()) {
1018                        Object key = iter.next();
1019                        if (!(key instanceof TTable)) {
1020                                continue;
1021                        }
1022                        TTable table = (TTable) key;
1023                        if (table.getSubquery() == null) {
1024                                tables.add(table);
1025                        }
1026                }
1027
1028                iter = createModelBindingMap.keySet().iterator();
1029                while (iter.hasNext()) {
1030                        Object key = iter.next();
1031                        if (!(key instanceof TTable)) {
1032                                continue;
1033                        }
1034                        TTable table = (TTable) key;
1035                        tables.add(table);
1036                }
1037
1038                iter = insertModelBindingMap.keySet().iterator();
1039                while (iter.hasNext()) {
1040                        Object key = iter.next();
1041                        if (!(key instanceof TTable)) {
1042                                continue;
1043                        }
1044                        TTable table = (TTable) key;
1045                        tables.add(table);
1046                }
1047
1048                iter = mergeModelBindingMap.keySet().iterator();
1049                while (iter.hasNext()) {
1050                        Object key = iter.next();
1051                        if (!(key instanceof TTable)) {
1052                                continue;
1053                        }
1054                        TTable table = (TTable) key;
1055                        tables.add(table);
1056                }
1057
1058                iter = updateModelBindingMap.keySet().iterator();
1059                while (iter.hasNext()) {
1060                        Object key = iter.next();
1061                        if (!(key instanceof TTable)) {
1062                                continue;
1063                        }
1064                        TTable table = (TTable) key;
1065                        if (!tables.contains(table)) {
1066                                tables.add(table);
1067                        }
1068                }
1069
1070                return tables;
1071        }
1072
1073        public List<TCustomSqlStatement> getViews() {
1074                List<TCustomSqlStatement> views = new ArrayList<TCustomSqlStatement>();
1075
1076                Iterator iter = viewModelBindingMap.keySet().iterator();
1077                while (iter.hasNext()) {
1078                        Object key = iter.next();
1079                        if (!(key instanceof TCreateViewSqlStatement) && !(key instanceof TCreateMaterializedSqlStatement)) {
1080                                continue;
1081                        }
1082                        TCustomSqlStatement view = (TCustomSqlStatement) key;
1083                        if (!views.contains(view)) {
1084                                views.add(view);
1085                        }
1086                }
1087                return views;
1088        }
1089
1090        public List<TResultColumnList> getSelectResultSets() {
1091                List<TResultColumnList> resultSets = new ArrayList<TResultColumnList>();
1092
1093                Iterator iter = modelBindingMap.keySet().iterator();
1094                while (iter.hasNext()) {
1095                        Object key = iter.next();
1096                        if (!(key instanceof TResultColumnList)) {
1097                                continue;
1098                        }
1099                        TResultColumnList resultset = (TResultColumnList) key;
1100                        resultSets.add(resultset);
1101                }
1102                return resultSets;
1103        }
1104
1105        public List<TObjectNameList> getQueryAliasTables() {
1106                List<TObjectNameList> resultSets = new ArrayList<TObjectNameList>();
1107
1108                Iterator iter = modelBindingMap.keySet().iterator();
1109                while (iter.hasNext()) {
1110                        Object key = iter.next();
1111                        if (!(key instanceof TObjectNameList)) {
1112                                continue;
1113                        }
1114                        TObjectNameList resultset = (TObjectNameList) key;
1115                        resultSets.add(resultset);
1116                }
1117                return resultSets;
1118        }
1119
1120        public List<TSelectSqlStatement> getSelectSetResultSets() {
1121                List<TSelectSqlStatement> resultSets = new ArrayList<TSelectSqlStatement>();
1122
1123                Iterator iter = modelBindingMap.keySet().iterator();
1124                while (iter.hasNext()) {
1125                        Object key = iter.next();
1126                        if (!(key instanceof TSelectSqlStatement)) {
1127                                continue;
1128                        }
1129
1130                        TSelectSqlStatement stmt = (TSelectSqlStatement) key;
1131                        if (stmt.getSetOperatorType() == ESetOperatorType.none)
1132                                continue;
1133
1134                        resultSets.add(stmt);
1135                }
1136                return resultSets;
1137        }
1138
1139        public List<TCTE> getCTEs() {
1140                List<TCTE> resultSets = new ArrayList<TCTE>();
1141
1142                Iterator iter = modelBindingMap.keySet().iterator();
1143                while (iter.hasNext()) {
1144                        Object key = iter.next();
1145                        if (!(key instanceof TCTE)) {
1146                                continue;
1147                        }
1148
1149                        TCTE cte = (TCTE) key;
1150                        resultSets.add(cte);
1151                }
1152                return resultSets;
1153        }
1154
1155        public List<TTable> getTableWithSelectSetResultSets() {
1156                List<TTable> resultSets = new ArrayList<TTable>();
1157
1158                Iterator iter = modelBindingMap.keySet().iterator();
1159                while (iter.hasNext()) {
1160                        Object key = iter.next();
1161                        if (!(key instanceof TTable)) {
1162                                continue;
1163                        }
1164
1165                        if (((TTable) key).getSubquery() == null)
1166                                continue;
1167                        TSelectSqlStatement stmt = ((TTable) key).getSubquery();
1168                        if (stmt.getSetOperatorType() == ESetOperatorType.none)
1169                                continue;
1170
1171                        resultSets.add((TTable) key);
1172                }
1173                return resultSets;
1174        }
1175
1176        public List<TParseTreeNode> getMergeResultSets() {
1177                List<TParseTreeNode> resultSets = new ArrayList<TParseTreeNode>();
1178
1179                Iterator iter = modelBindingMap.keySet().iterator();
1180                while (iter.hasNext()) {
1181                        Object key = iter.next();
1182                        if (!(key instanceof TMergeUpdateClause) && !(key instanceof TMergeInsertClause)) {
1183                                continue;
1184                        }
1185                        TParseTreeNode resultset = (TParseTreeNode) key;
1186                        resultSets.add(resultset);
1187                }
1188                return resultSets;
1189        }
1190
1191        public List<TParseTreeNode> getOutputResultSets() {
1192                List<TParseTreeNode> resultSets = new ArrayList<TParseTreeNode>();
1193
1194                Iterator iter = modelBindingMap.keySet().iterator();
1195                while (iter.hasNext()) {
1196                        Object key = iter.next();
1197                        if (!(key instanceof TOutputClause)) {
1198                                continue;
1199                        }
1200                        TParseTreeNode resultset = (TParseTreeNode) key;
1201                        resultSets.add(resultset);
1202                }
1203                return resultSets;
1204        }
1205
1206        public List<TParseTreeNode> getUpdateResultSets() {
1207                List<TParseTreeNode> resultSets = new ArrayList<TParseTreeNode>();
1208
1209                Iterator iter = modelBindingMap.keySet().iterator();
1210                while (iter.hasNext()) {
1211                        Object key = iter.next();
1212                        if (!(key instanceof TUpdateSqlStatement)) {
1213                                continue;
1214                        }
1215                        TParseTreeNode resultset = (TParseTreeNode) key;
1216                        resultSets.add(resultset);
1217                }
1218                return resultSets;
1219        }
1220
1221        public List<TParseTreeNode> getFunctoinCalls() {
1222                List<TParseTreeNode> resultSets = new ArrayList<TParseTreeNode>();
1223
1224                Iterator iter = modelBindingMap.keySet().iterator();
1225                while (iter.hasNext()) {
1226                        Object key = iter.next();
1227                        if (!(key instanceof TFunctionCall) && !(key instanceof TCaseExpression)) {
1228                                continue;
1229                        }
1230                        TParseTreeNode resultset = (TParseTreeNode) key;
1231                        resultSets.add(resultset);
1232                }
1233                return resultSets;
1234        }
1235
1236        public List<TParseTreeNode> getAliases() {
1237                List<TParseTreeNode> resultSets = new ArrayList<TParseTreeNode>();
1238
1239                Iterator iter = modelBindingMap.keySet().iterator();
1240                while (iter.hasNext()) {
1241                        Object key = iter.next();
1242                        if (!(key instanceof TAliasClause)) {
1243                                continue;
1244                        }
1245                        TParseTreeNode resultset = (TParseTreeNode) key;
1246                        resultSets.add(resultset);
1247                }
1248                return resultSets;
1249        }
1250        
1251        public List<TParseTreeNode> getCursors() {
1252                List<TParseTreeNode> resultSets = new ArrayList<TParseTreeNode>();
1253
1254                Iterator iter = modelBindingMap.keySet().iterator();
1255                while (iter.hasNext()) {
1256                        Object key = iter.next();
1257                        if (!(key instanceof TCursorDeclStmt) && !(key instanceof TOpenforStmt)) {
1258                                continue;
1259                        }
1260                        TParseTreeNode resultset = (TParseTreeNode) key;
1261                        resultSets.add(resultset);
1262                }
1263                return resultSets;
1264        }
1265        
1266        public List<TParseTreeNode> getPivotdTables() {
1267                List<TParseTreeNode> resultSets = new ArrayList<TParseTreeNode>();
1268
1269                Iterator iter = modelBindingMap.keySet().iterator();
1270                while (iter.hasNext()) {
1271                        Object key = iter.next();
1272                        if (!(key instanceof TPivotClause)) {
1273                                continue;
1274                        }
1275                        TParseTreeNode resultset = (TParseTreeNode) key;
1276                        resultSets.add(resultset);
1277                }
1278                return resultSets;
1279        }
1280
1281        public void addRelation(Relationship relation) {
1282                if (relation != null && !relationHolder.contains(relation)) {
1283                        relationHolder.add(relation);
1284                }
1285        }
1286        
1287        public void removeRelation(ImpactRelationship relation) {
1288                if (relation != null) {
1289                        relationHolder.remove(relation);
1290                }
1291        }
1292
1293        public Relationship[] getRelations() {
1294                return relationHolder.toArray(new Relationship[0]);
1295        }
1296
1297        public void reset() {
1298                modelBindingMap.clear();
1299                processModelBindingMap.clear();
1300                viewModelBindingMap.clear();
1301                insertModelBindingMap.clear();
1302                createModelBindingMap.clear();
1303                createModelQuickBindingMap.clear();
1304                mergeModelBindingMap.clear();
1305                updateModelBindingMap.clear();
1306                cursorModelBindingMap.clear();
1307                functionTableMap.clear();
1308                tableNamesMap.clear();
1309                dropTables.clear();
1310                procedureNamesMap.clear();
1311                oraclePackageNamesMap.clear();
1312                relationHolder.clear();
1313                tableAliasMap.clear();
1314                tableHashMap.clear();
1315                topStmtHashMap.clear();
1316                hashSQLMap.clear();
1317                dynamicSQLMap.clear();
1318                tableSet.clear();
1319                resultSetSet.clear();
1320                dblinkTableSet.clear();
1321                objectTypeIndex.clear();
1322                collectionToElementTypeIndex.clear();
1323                pipelinedSignatures.clear();
1324                pendingPipelinedCallSites.clear();
1325                resolvingPipelinedFunctions.clear();
1326                DISPLAY_ID.clear();
1327                DISPLAY_NAME.clear();
1328                virtualTableNames.clear();
1329        }
1330
1331        public void bindCursorModel(TCursorDeclStmt stmt, Variable resultSet) {
1332                String procedureName = DlineageUtil.getProcedureParentName(stmt);
1333                String variableString = stmt.getCursorName().toString();
1334                if (variableString.startsWith(":")) {
1335                        variableString = variableString.substring(variableString.indexOf(":") + 1);
1336                }
1337                if (!SQLUtil.isEmpty(procedureName)) {
1338                        variableString = DlineageUtil.getTableFullName(procedureName) + "." + SQLUtil.getIdentifierNormalTableName(variableString);
1339                }
1340                createModelBindingMap.put(DlineageUtil.getTableFullName(variableString), resultSet);
1341        }
1342        
1343        public void bindCursorModel(TMssqlDeclare stmt, Variable resultSet) {
1344                String procedureName = DlineageUtil.getProcedureParentName(stmt);
1345                String variableString = stmt.getCursorName().toString();
1346                if (variableString.startsWith(":")) {
1347                        variableString = variableString.substring(variableString.indexOf(":") + 1);
1348                }
1349                if (!SQLUtil.isEmpty(procedureName)) {
1350                        variableString = DlineageUtil.getTableFullName(procedureName) + "." + SQLUtil.getIdentifierNormalTableName(variableString);
1351                }
1352                createModelBindingMap.put(DlineageUtil.getTableFullName(variableString), resultSet);
1353        }
1354        
1355        public void bindCursorModel(TOpenforStmt stmt, Variable resultSet) {
1356                String procedureName = DlineageUtil.getProcedureParentName(stmt);
1357                String variableString = stmt.getCursorVariableName().toString();
1358                if (variableString.startsWith(":")) {
1359                        variableString = variableString.substring(variableString.indexOf(":") + 1);
1360                }
1361                if (!SQLUtil.isEmpty(procedureName)) {
1362                        variableString = DlineageUtil.getTableFullName(procedureName) + "." + SQLUtil.getIdentifierNormalTableName(variableString);
1363                }
1364                createModelBindingMap.put(DlineageUtil.getTableFullName(variableString), resultSet);
1365        }
1366        
1367        public void bindCursorModel(TForStmt stmt, Variable resultSet) {
1368                String procedureName = DlineageUtil.getProcedureParentName(stmt);
1369                String variableString = stmt.getCursorName().toString();
1370                if (variableString.startsWith(":")) {
1371                        variableString = variableString.substring(variableString.indexOf(":") + 1);
1372                }
1373                if (!SQLUtil.isEmpty(procedureName)) {
1374                        variableString = DlineageUtil.getTableFullName(procedureName) + "." + SQLUtil.getIdentifierNormalTableName(variableString);
1375                }
1376                createModelBindingMap.put(DlineageUtil.getTableFullName(variableString), resultSet);
1377        }
1378        
1379        public void bindCursorModel(TLoopStmt stmt, Variable resultSet) {
1380                String procedureName = DlineageUtil.getProcedureParentName(stmt);
1381                String variableString = stmt.getRecordName().toString();
1382                if (variableString.startsWith(":")) {
1383                        variableString = variableString.substring(variableString.indexOf(":") + 1);
1384                }
1385                if (!SQLUtil.isEmpty(procedureName)) {
1386                        variableString = DlineageUtil.getTableFullName(procedureName) + "." + SQLUtil.getIdentifierNormalTableName(variableString);
1387                }
1388                createModelBindingMap.put(DlineageUtil.getTableFullName(variableString), resultSet);
1389        }
1390
1391        public void bindCursorIndex(TObjectName indexName, TObjectName cursorName) {
1392                TCustomSqlStatement stmt = getGlobalStmtStack().peek();
1393                String procedureName = DlineageUtil.getProcedureParentName(stmt);
1394                String variableString = cursorName.toString();
1395                if (variableString.startsWith(":")) {
1396                        variableString = variableString.substring(variableString.indexOf(":") + 1);
1397                }
1398                if (!SQLUtil.isEmpty(procedureName)) {
1399                        variableString = DlineageUtil.getTableFullName(procedureName) + "." + SQLUtil.getIdentifierNormalTableName(variableString);
1400                }
1401                
1402                Variable cursor = ((Variable) createModelBindingMap
1403                                .get(DlineageUtil.getTableFullName(variableString)));
1404                if (cursor != null) {
1405                        String indexNameString = indexName.toString();
1406                        if (!SQLUtil.isEmpty(procedureName)) {
1407                                indexNameString = DlineageUtil.getTableFullName(procedureName) + "." + SQLUtil.getIdentifierNormalTableName(indexNameString);
1408                        }
1409                        if (cursor instanceof Cursor) {
1410                                bindModel(DlineageUtil.getTableFullName(indexNameString),
1411                                                modelBindingMap.get(((Cursor)cursor).getResultColumnObject()));
1412                        }
1413                        bindTableByName(DlineageUtil.getTableFullName(indexNameString),
1414                                        cursor);
1415                }
1416        }
1417
1418        public List<TStoredProcedureSqlStatement> getProcedures() {
1419                List<TStoredProcedureSqlStatement> procedures = new ArrayList();
1420                Iterator iter = this.modelBindingMap.keySet().iterator();
1421
1422                while (iter.hasNext()) {
1423                        Object key = iter.next();
1424                        if (key instanceof TStoredProcedureSqlStatement) {
1425                                TStoredProcedureSqlStatement procedure = (TStoredProcedureSqlStatement) key;
1426                                procedures.add(procedure);
1427                        }
1428                }
1429
1430                return procedures;
1431        }
1432
1433        // public void bindTableByName(TObjectName tableName, Table tableModel) {
1434        // tableNamesMap.put(tableName, tableModel);
1435        //
1436        // }
1437        //
1438        public void bindTableByName(String tableName, Table tableModel) {
1439                tableNamesMap.put(tableName, tableModel);
1440
1441        }
1442
1443        // public Table getTableByName(TObjectName tableName) {
1444        // return (Table)tableNamesMap.get(tableName);
1445        // }
1446
1447        /**
1448         * B3 (routine-summary-scc-design §3): the overload-discriminating scope
1449         * of the routine whose body is being analyzed, active ONLY under
1450         * {@code Option.identityFirstVariablePools}. Held here (not just in the
1451         * factory) because DIRECT name lookups all funnel through
1452         * {@link #getTableByName(String)} — without this, a same-named
1453         * overload's reads fall back to the SHARED procedure-composed key and
1454         * cross-contaminate the pools (codex-B3-r2 finding 7). Empty in LEGACY
1455         * by construction.
1456         */
1457        private final java.util.ArrayDeque<String> identityScopeStack =
1458                        new java.util.ArrayDeque<String>();
1459        public void pushIdentityScope(String identityScopeKey) {
1460                if (identityScopeKey != null) {
1461                        identityScopeStack.push(identityScopeKey);
1462                }
1463        }
1464
1465        public void popIdentityScope() {
1466                if (!identityScopeStack.isEmpty()) {
1467                        identityScopeStack.pop();
1468                }
1469        }
1470
1471        public boolean hasIdentityScope() {
1472                return !identityScopeStack.isEmpty();
1473        }
1474
1475        public String peekIdentityScope() {
1476                return identityScopeStack.peek();
1477        }
1478
1479        public Table getTableByName(String tableName) {
1480                if (tableName == null) {
1481                        return null;
1482                }
1483                Table table = (Table) tableNamesMap.get(tableName);
1484                // A direct hit that is a VARIABLE may be the legacy shared-key
1485                // binding of a same-named overload (cursor-fetch readers pass the
1486                // shared qualified key) — under an active identity scope the pool
1487                // binding wins (codex-B3-r3 finding 2a). Non-variable hits (real
1488                // tables) keep winning unchanged; stack is empty in LEGACY.
1489                // An identity-scoped request (\u0002-prefixed) is already
1490                // pool-precise: redirecting it to the ACTIVE scope would hand an
1491                // explicit callee-formal lookup the CALLER's same-named variable
1492                // (codex-B3-r4 finding 1).
1493                if (table instanceof Variable && !identityScopeStack.isEmpty()
1494                                && !tableName.startsWith("\u0002")) {
1495                        java.util.List<String> segments = SQLUtil.parseNames(tableName);
1496                        String simple = segments.isEmpty()
1497                                        ? tableName : segments.get(segments.size() - 1);
1498                        Object pooled = tableNamesMap.get(identityScopeStack.peek() + "."
1499                                        + SQLUtil.getIdentifierNormalTableName(simple));
1500                        if (pooled instanceof Variable) {
1501                                return (Table) pooled;
1502                        }
1503                }
1504                if (table == null) {
1505                        Stack<TCustomSqlStatement> stmtStack = getGlobalStmtStack();
1506                        if (stmtStack.isEmpty()) {
1507                                return (Table) tableNamesMap.get(DlineageUtil.getTableFullName(tableName.toString()));
1508                        }
1509                        TCustomSqlStatement stmt = stmtStack.peek();
1510                        String procedureName = DlineageUtil.getProcedureParentName(stmt);
1511                        String variableString = tableName.toString();
1512                        // Identity-first pool resolution (mode-gated: stack empty in
1513                        // LEGACY). Only VARIABLE hits short-circuit — a real table that
1514                        // resolved above already won, exactly as before.
1515                        if (!identityScopeStack.isEmpty()
1516                                        && !tableName.startsWith("\u0002")) { // never re-scope an identity key
1517                                Object pooled = tableNamesMap.get(identityScopeStack.peek() + "."
1518                                                + SQLUtil.getIdentifierNormalTableName(variableString));
1519                                if (pooled instanceof Variable) {
1520                                        return (Table) pooled;
1521                                }
1522                        }
1523                        if (!SQLUtil.isEmpty(procedureName) && !tableName.startsWith("\u0002")) {
1524                                java.util.List<String> segments = SQLUtil.parseNames(variableString);
1525                                String simpleName = segments.isEmpty() ? variableString : segments.get(segments.size() - 1);
1526                                String variableWithProcedure = DlineageUtil.getTableFullName(procedureName) + "." + SQLUtil.getIdentifierNormalTableName(simpleName);
1527                                if(tableNamesMap.containsKey(DlineageUtil.getTableFullName(variableWithProcedure))) {
1528                                        return  (Table) tableNamesMap.get(DlineageUtil.getTableFullName(variableWithProcedure));
1529                                }
1530                        }
1531                        return (Table) tableNamesMap.get(DlineageUtil.getTableFullName(variableString));
1532                } else {
1533                        return table;
1534                }
1535        }
1536
1537
1538        public void bindProcedureByName(String procedureName, Procedure procedureModel) {
1539                procedureNamesMap.put(procedureName, procedureModel);
1540
1541        }
1542        
1543        public Procedure getProcedureByName(String procedureName) {
1544                if(procedureName == null){
1545                        return null;
1546                }
1547
1548                if (ModelBindingManager.getGlobalOraclePackage() != null
1549                                && !procedureName.startsWith(ModelBindingManager.getGlobalOraclePackage().getName())) {
1550                        procedureName = ModelBindingManager.getGlobalOraclePackage().getName() + "." + procedureName;
1551                }
1552                Procedure procedure = (Procedure) procedureNamesMap.get(procedureName);
1553                if (procedure == null) {
1554                        if (procedureName.indexOf("(") == -1) {
1555                                for (Object name : procedureNamesMap.keySet()) {
1556                                        String fuctionName = name.toString();
1557                                        if (fuctionName.startsWith(procedureName + "(")) {
1558                                                return (Procedure) procedureNamesMap.get(name);
1559                                        }
1560                                        if (fuctionName.indexOf(".") != -1 && procedureName.indexOf(".") == -1) {
1561                                                fuctionName = fuctionName.substring(fuctionName.lastIndexOf(".") + 1);
1562                                                if (fuctionName.startsWith(procedureName + "(")) {
1563                                                        return (Procedure) procedureNamesMap.get(name);
1564                                                }
1565                                        }
1566                                        if (fuctionName.indexOf(".") == -1 && procedureName.indexOf(".") != -1) {
1567                                                String procedureNameSubstring = procedureName.substring(procedureName.lastIndexOf(".") + 1);
1568                                                if (fuctionName.startsWith(procedureNameSubstring + "(")) {
1569                                                        return (Procedure) procedureNamesMap.get(name);
1570                                                }
1571                                        }
1572                                }
1573                        }
1574                        else {
1575                                for (Object name : procedureNamesMap.keySet()) {
1576                                        String fuctionName = name.toString();
1577                                        if (fuctionName.startsWith(procedureName)) {
1578                                                return (Procedure) procedureNamesMap.get(name);
1579                                        }
1580                                        if (fuctionName.indexOf(".") != -1 && procedureName.indexOf(".") == -1) {
1581                                                fuctionName = fuctionName.substring(fuctionName.lastIndexOf(".") + 1);
1582                                                if (DlineageUtil.sameName(ESQLDataObjectType.dotProcedure, fuctionName, procedureName)) {
1583                                                        return (Procedure) procedureNamesMap.get(name);
1584                                                }
1585                                        }
1586                                        if (fuctionName.indexOf(".") == -1 && procedureName.indexOf(".") != -1) {
1587                                                String procedureNameSubstring = procedureName.substring(procedureName.lastIndexOf(".") + 1);
1588                                                if (DlineageUtil.sameName(ESQLDataObjectType.dotProcedure, fuctionName, procedureNameSubstring)) {
1589                                                        return (Procedure) procedureNamesMap.get(name);
1590                                                }
1591                                        }
1592                                }
1593                        }
1594                }
1595                return procedure;
1596        }
1597        
1598        public void bindOraclePackageByName(String oraclePackageName, OraclePackage oraclePackageModel) {
1599                oraclePackageNamesMap.put(oraclePackageName, oraclePackageModel);
1600
1601        }
1602        
1603        public OraclePackage getOraclePackageByName(String oraclePackageName) {
1604                return (OraclePackage) oraclePackageNamesMap.get(oraclePackageName);
1605        }
1606
1607
1608        public String getSqlHash(TCustomSqlStatement stmt) {
1609                return topStmtHashMap.get(stmt);
1610        }
1611
1612        public Map getHashSQLMap() {
1613                return hashSQLMap;
1614        }
1615
1616        public Map getDynamicSQLMap() {
1617                return dynamicSQLMap;
1618        }
1619
1620        public void collectSqlHash(TCustomSqlStatement stmt) {
1621                if (getGlobalOption().getAnalyzeMode() == AnalyzeMode.crud) {
1622                        String sql = stmt.toString();
1623                        if (sql != null) {
1624                                sql = stmt.getStartToken().lineNo + ":" + sql;
1625                                String hash = SQLUtil.stringToMD5(sql);
1626                                topStmtHashMap.put(stmt, hash);
1627                                hashSQLMap.put(hash, new Pair3<Long, Long, String>(stmt.getStartToken().lineNo, stmt.getEndToken().lineNo, ModelBindingManager.getGlobalHash()));
1628                        }
1629                } else {
1630                        try {
1631                                String sql = stmt.asCanonical();
1632                                if (sql != null) {
1633                                        String hash = SQLUtil.stringToMD5(sql);
1634                                        topStmtHashMap.put(stmt, hash);
1635                                        hashSQLMap.put(hash, sql);
1636                                }
1637                        } catch (Exception e) {
1638                                logger.error("collectSqlHash error: " + e.getMessage(), e);
1639                        }
1640                }
1641        }
1642        
1643        public void collectDynamicSqlHash(TCustomSqlStatement stmt) {
1644                if (getGlobalOption().getAnalyzeMode() == AnalyzeMode.dynamic) {
1645                        String sql = stmt.toString();
1646                        if (sql != null) {
1647                                sql = stmt.getStartToken().lineNo + ":" + sql;
1648                                String hash = SQLUtil.stringToMD5(sql);
1649                                dynamicSQLMap.put(hash, new Pair3<Long, Long, String>(stmt.getStartToken().lineNo, stmt.getEndToken().lineNo, ModelBindingManager.getGlobalHash()));
1650                        }
1651                }
1652        }
1653
1654        public void appendDblinkTable(String name) {
1655                if (!SQLUtil.isEmpty(name)) {
1656                        dblinkTableSet.add(name);
1657                }
1658        }
1659        
1660        public boolean isDblinkTable(String name) {
1661                return dblinkTableSet.contains(name);
1662        }
1663
1664        public Set<ResultSet> getResultSets() {
1665                return resultSetSet;
1666        }
1667        
1668        public void dropTable(Table table) {
1669                List tableNames = new ArrayList(tableNamesMap.keySet());
1670                for (Object tableName : tableNames) {
1671                        if(tableNamesMap.get(tableName) == table) {
1672                                tableNamesMap.remove(tableName);
1673                                dropTables.add(table);
1674                        }
1675                }
1676        }
1677        
1678        public void cleanTempTable() {
1679                Iterator<String> iter = tableNamesMap.keySet().iterator();
1680                while(iter.hasNext()) {
1681                        String tableName = iter.next();
1682                        if(DlineageUtil.isTempTable((Table)tableNamesMap.get(tableName), getGlobalVendor())) {
1683                                dropTables.add((Table)tableNamesMap.get(tableName));
1684                                iter.remove();
1685                        }
1686                }
1687        }
1688
1689        public Set<Table> getDropTables() {
1690                return dropTables;
1691        }
1692
1693        // --- Pipelined function stitching methods ---
1694
1695        public void indexObjectType(String normalizedName, List<String> fieldNames) {
1696                objectTypeIndex.put(normalizedName, fieldNames);
1697        }
1698
1699        public List<String> getObjectTypeFields(String normalizedName) {
1700                return objectTypeIndex.get(normalizedName);
1701        }
1702
1703        public void indexCollectionType(String collectionName, String elementTypeName) {
1704                collectionToElementTypeIndex.put(collectionName, elementTypeName);
1705        }
1706
1707        public String getElementTypeName(String collectionName) {
1708                return collectionToElementTypeIndex.get(collectionName);
1709        }
1710
1711        public void addPipelinedSignature(PipelinedFunctionSignature sig) {
1712                pipelinedSignatures.computeIfAbsent(sig.getFunctionKey(), k -> new ArrayList<>()).add(sig);
1713                if (sig.getNakedKey() != null && !sig.getNakedKey().equals(sig.getFunctionKey())) {
1714                        pipelinedSignatures.computeIfAbsent(sig.getNakedKey(), k -> new ArrayList<>()).add(sig);
1715                }
1716        }
1717
1718        public PipelinedFunctionSignature findPipelinedSignature(List<String> candidates, int argCount) {
1719                for (String key : candidates) {
1720                        List<PipelinedFunctionSignature> sigs = pipelinedSignatures.get(key);
1721                        if (sigs != null) {
1722                                PipelinedFunctionSignature match = null;
1723                                for (PipelinedFunctionSignature sig : sigs) {
1724                                        if (sig.getArgCount() == argCount || argCount < 0) {
1725                                                if (match != null) {
1726                                                        return null; // ambiguous
1727                                                }
1728                                                match = sig;
1729                                        }
1730                                }
1731                                if (match != null) {
1732                                        return match;
1733                                }
1734                        }
1735                }
1736                return null;
1737        }
1738
1739        public PipelinedFunctionSignature findPipelinedSignatureAny(List<String> candidates) {
1740                for (String key : candidates) {
1741                        List<PipelinedFunctionSignature> sigs = pipelinedSignatures.get(key);
1742                        if (sigs != null && !sigs.isEmpty()) {
1743                                if (sigs.size() == 1) {
1744                                        return sigs.get(0);
1745                                }
1746                        }
1747                }
1748                return null;
1749        }
1750
1751        public void addPendingPipelinedCallSite(PendingPipelinedCallSite callSite) {
1752                pendingPipelinedCallSites.add(callSite);
1753        }
1754
1755        public List<PendingPipelinedCallSite> getPendingPipelinedCallSites() {
1756                return pendingPipelinedCallSites;
1757        }
1758
1759        public Set<String> getResolvingPipelinedFunctions() {
1760                return resolvingPipelinedFunctions;
1761        }
1762
1763        public Map<String, List<PipelinedFunctionSignature>> getPipelinedSignatures() {
1764                return pipelinedSignatures;
1765        }
1766
1767}