001package gudusoft.gsqlparser.sqlenv;
002
003import gudusoft.gsqlparser.EDbVendor;
004import gudusoft.gsqlparser.EExpressionType;
005import gudusoft.gsqlparser.TCustomSqlStatement;
006import gudusoft.gsqlparser.TGSqlParser;
007import gudusoft.gsqlparser.dlineage.util.DlineageUtil;
008import gudusoft.gsqlparser.dlineage.util.Pair3;
009import gudusoft.gsqlparser.nodes.TColumnDefinition;
010import gudusoft.gsqlparser.nodes.TDeclareVariable;
011import gudusoft.gsqlparser.nodes.TDeclareVariableList;
012import gudusoft.gsqlparser.nodes.TFunctionCall;
013import gudusoft.gsqlparser.nodes.TObjectName;
014import gudusoft.gsqlparser.nodes.TParseTreeNode;
015import gudusoft.gsqlparser.nodes.TResultColumn;
016import gudusoft.gsqlparser.nodes.TTableElement;
017import gudusoft.gsqlparser.nodes.TTableElementList;
018import gudusoft.gsqlparser.nodes.TTypeName;
019import gudusoft.gsqlparser.nodes.TViewAliasItem;
020import gudusoft.gsqlparser.stmt.TCreateFunctionStmt;
021import gudusoft.gsqlparser.stmt.TCreateProcedureStmt;
022import gudusoft.gsqlparser.stmt.TCreateSchemaSqlStatement;
023import gudusoft.gsqlparser.stmt.TCreateSynonymStmt;
024import gudusoft.gsqlparser.stmt.TCreateTableSqlStatement;
025import gudusoft.gsqlparser.stmt.TCreateTriggerStmt;
026import gudusoft.gsqlparser.stmt.TCreateViewSqlStatement;
027import gudusoft.gsqlparser.stmt.TStoredProcedureSqlStatement;
028import gudusoft.gsqlparser.stmt.TUseDatabase;
029import gudusoft.gsqlparser.stmt.mssql.TMssqlCreateFunction;
030import gudusoft.gsqlparser.stmt.mssql.TMssqlDeclare;
031import gudusoft.gsqlparser.stmt.oracle.TPlsqlCreateFunction;
032import gudusoft.gsqlparser.stmt.oracle.TPlsqlCreatePackage;
033import gudusoft.gsqlparser.stmt.oracle.TPlsqlCreateProcedure;
034import gudusoft.gsqlparser.stmt.oracle.TPlsqlCreateTrigger;
035import gudusoft.gsqlparser.stmt.teradata.TTeradataCreateProcedure;
036import gudusoft.gsqlparser.stmt.snowflake.TUseSchema;
037import gudusoft.gsqlparser.util.SQLUtil;
038
039import java.io.ByteArrayInputStream;
040import java.io.IOException;
041import java.util.ArrayList;
042import java.util.List;
043import java.util.Properties;
044import java.util.Stack;
045
046public class TDDLSQLEnv extends TSQLEnv {
047
048    private String sql;
049
050    private TSQLEnv metadataSQLEnv;
051
052    private String globalDatabase = null;
053
054    private String globalSchema = null;
055    /**
056     * A Snowflake {@code USE <database>} unset the current schema. Distinct from
057     * {@code globalSchema == null} ("never set"), which is allowed to fall back
058     * to the batch-global {@link #getDefaultSchemaName()}. See GitHub #715.
059     */
060    private boolean schemaUnsetByDatabaseSwitch = false;
061
062    private boolean init = false;
063
064    private boolean includeLocalTemporaryTables = true;
065
066    /** Disable for whole-script catalog preloading; call-frame temporary DDL needs positional scope. */
067    public void setIncludeLocalTemporaryTables(boolean include) {
068        this.includeLocalTemporaryTables = include;
069    }
070    
071    private Stack<TCustomSqlStatement> stmtStack = new Stack<TCustomSqlStatement>();
072
073    public boolean isInit() {
074        return init;
075    }
076
077    public TDDLSQLEnv(String defaultServer, String defaultDatabase, String defaultSchema, EDbVendor dbVendor, String sql) {
078        this(defaultServer, defaultDatabase, defaultSchema, dbVendor, sql, null);
079    }
080
081    /**
082     * Same input, analyzed under an explicit identifier configuration.
083     *
084     * <p>DDL in the script registers objects in this environment, so the rules used to key them
085     * must be the run's rules — otherwise a later reference is compared by a relation the
086     * registration never used. A profile belonging to another vendor is not applied
087     * ({@link IdentifierProfile#forVendorOr}); null means the vendor default.
088     */
089    public TDDLSQLEnv(String defaultServer, String defaultDatabase, String defaultSchema, EDbVendor dbVendor,
090                      String sql, IdentifierProfile identifierProfile) {
091        super(dbVendor, IdentifierProfile.forVendorOr(identifierProfile, dbVendor));
092        if(!SQLUtil.isEmpty(defaultServer) && !defaultServer.equals(TSQLEnv.DEFAULT_SERVER_NAME)) {
093                        setDefaultServerName(defaultServer);
094                }
095                if(!SQLUtil.isEmpty(defaultDatabase) && !defaultDatabase.equals(TSQLEnv.DEFAULT_DB_NAME)) {
096                        setDefaultCatalogName(defaultDatabase);
097                }
098                if(!SQLUtil.isEmpty(defaultSchema) && !defaultSchema.equals(TSQLEnv.DEFAULT_SCHEMA_NAME)) {
099                        setDefaultSchemaName(defaultSchema);
100                }
101        this.sql = sql;
102    }
103
104    public TDDLSQLEnv(String defaultServer, String defaultDatabase, String defaultSchema, TSQLEnv metadataSQLEnv, EDbVendor dbVendor, String sql) {
105        this(defaultServer, defaultDatabase, defaultSchema, metadataSQLEnv, dbVendor, sql, null);
106    }
107
108    /**
109     * Same input, analyzed under an explicit identifier configuration.
110     *
111     * <p>DDL in the script registers objects in this environment, so the rules used to key them
112     * must be the run's rules — otherwise a later reference is compared by a relation the
113     * registration never used. A profile belonging to another vendor is not applied
114     * ({@link IdentifierProfile#forVendorOr}); null means the vendor default.
115     */
116    public TDDLSQLEnv(String defaultServer, String defaultDatabase, String defaultSchema, TSQLEnv metadataSQLEnv,
117                      EDbVendor dbVendor, String sql, IdentifierProfile identifierProfile) {
118        super(dbVendor, IdentifierProfile.forVendorOr(
119                identifierProfile != null || metadataSQLEnv == null
120                        ? identifierProfile
121                        // A metadata environment carries the configuration its own catalog was
122                        // indexed with; this DDL environment merges into it, so it must agree.
123                        : metadataSQLEnv.getIdentifierProfile(),
124                dbVendor));
125        if(!SQLUtil.isEmpty(defaultServer) && !defaultServer.equals(TSQLEnv.DEFAULT_SERVER_NAME)) {
126                        setDefaultServerName(defaultServer);
127                }
128                if(!SQLUtil.isEmpty(defaultDatabase) && !defaultDatabase.equals(TSQLEnv.DEFAULT_DB_NAME)) {
129                        setDefaultCatalogName(defaultDatabase);
130                }
131                if(!SQLUtil.isEmpty(defaultSchema) && !defaultSchema.equals(TSQLEnv.DEFAULT_SCHEMA_NAME)) {
132                        setDefaultSchemaName(defaultSchema);
133                }
134        this.sql = sql;
135        this.metadataSQLEnv = metadataSQLEnv;
136        if(metadataSQLEnv!=null) {
137                this.setDefaultServerName(metadataSQLEnv.getDefaultServerName());
138        }
139    }
140
141    @Override
142    public void initSQLEnv() {
143        synchronized (this) {
144            if (sql == null || init)
145                return;
146                        TGSqlParser sqlparser = new TGSqlParser(getDBVendor());
147                        try {
148                                // This parser reads THIS environment's own DDL, so it runs under THIS
149                                // environment's identifier configuration. Without this, a parser with neither an
150                                // environment nor a profile makes TGSqlParser.freezeIdentifierProfile build a
151                                // second environment on the vendor default, and the analysis briefly holds two
152                                // configurations at once — measured on the BigQuery I6D5KX fixture, whose
153                                // sidecar asks for case-insensitive table names while that second environment
154                                // answered a lookup for CUSTOMERS with a miss under the default sensitive rule.
155                                //
156                                // No known output changes: the objects this method registers land in THIS
157                                // environment, keyed by its own service, so the second environment is never read.
158                                // This restores the one-configuration-per-analysis invariant rather than fixing a
159                                // wrong answer, and the fixture's lineage is byte-identical with and without it.
160                                //
161                                // Only when there is no metadata environment. With one, the parser takes that
162                                // environment's profile, and freezeIdentifierProfile already raises a conflict
163                                // when the two disagree — setting ours here would turn that into a throw.
164                                if (metadataSQLEnv == null) {
165                                        sqlparser.setIdentifierProfile(getIdentifierProfile());
166                                }
167                                sqlparser.setSqlEnv(metadataSQLEnv);
168                                sqlparser.sqltext = sql;
169                                sqlparser.parse();
170                        } catch (Exception e) {
171                                // Parse failure - log and continue with whatever statements were parsed
172                        }
173            List<TCustomSqlStatement> stmts = new ArrayList<TCustomSqlStatement>();
174            if (sqlparser.sqlstatements != null) {
175                for (int j = 0; j < sqlparser.sqlstatements.size(); j++) {
176                    TCustomSqlStatement stmt = sqlparser.sqlstatements.get(j);
177                    extractDDLStmts(stmts, stmt);
178                }
179            }
180
181            if (!stmts.isEmpty()) {
182                for (int j = 0; j < stmts.size(); j++) {
183                    TCustomSqlStatement stmt = stmts.get(j);
184                    analyzeSQLEnv(stmt);
185                }
186                init = true;
187            }
188            stmts.clear();
189            sql = null;
190        }
191    }
192
193    private void extractDDLStmts(List<TCustomSqlStatement> stmts, TCustomSqlStatement stmt) {
194        if (stmt instanceof TUseDatabase || stmt instanceof TUseSchema
195                || stmt instanceof TCreateSchemaSqlStatement
196                || stmt instanceof TCreateTableSqlStatement || stmt instanceof TMssqlDeclare
197                || stmt instanceof TStoredProcedureSqlStatement
198                || (stmt instanceof TMssqlCreateFunction
199                && ((TMssqlCreateFunction) stmt).getReturnTableDefinitions() != null)) {
200            stmts.add(stmt);
201        } else if (stmt instanceof TCreateViewSqlStatement) {
202            stmts.add(stmt);
203        } else if (stmt instanceof TCreateSynonymStmt) {
204            stmts.add(stmt);
205        } else if (stmt.getStatements() != null && stmt.getStatements().size() > 0) {
206            for (int i = 0; i < stmt.getStatements().size(); i++) {
207                extractDDLStmts(stmts, stmt.getStatements().get(i));
208            }
209        }
210    }
211
212    private void analyzeSQLEnv(TCustomSqlStatement stmt) {
213        stmtStack.push(stmt);
214        if (stmt instanceof TUseDatabase) {
215            TUseDatabase useDatabase = (TUseDatabase) stmt;
216            analyzeUseDatabase(useDatabase);
217        } else if (stmt instanceof TUseSchema) {
218            // A Snowflake schema switch parses to its own statement class, not to
219            // TUseDatabase, so it has to be seen here or the "schema was unset by
220            // a database switch" flag would survive a subsequent USE SCHEMA and
221            // register later objects under DEFAULT instead of the schema the
222            // script just named. We only lift the flag: the schema name itself
223            // still comes from getDefaultSchemaName(), exactly as before, so this
224            // restores the previous behaviour rather than changing it.
225            schemaUnsetByDatabaseSwitch = false;
226        } else if (stmt instanceof TMssqlDeclare) {
227            TMssqlDeclare declare = (TMssqlDeclare) stmt;
228            analyzeMssqlDeclare(declare);
229        } else if (stmt instanceof TMssqlCreateFunction) {
230            TMssqlCreateFunction createFunction = (TMssqlCreateFunction) stmt;
231            analyzeMssqlCreateFunction(createFunction);
232        } else if (stmt instanceof TCreateSchemaSqlStatement) {
233            TCreateSchemaSqlStatement createSchema = (TCreateSchemaSqlStatement) stmt;
234            analyzeCreateSchema(createSchema);
235        } else if (stmt instanceof TCreateTableSqlStatement) {
236            TCreateTableSqlStatement createTable = (TCreateTableSqlStatement) stmt;
237            analyzeCreateTable(createTable);
238        } else if (stmt instanceof TCreateViewSqlStatement) {
239            TCreateViewSqlStatement createView = (TCreateViewSqlStatement) stmt;
240            analyzeCreateView(createView);
241        } else if (stmt instanceof TCreateSynonymStmt) {
242            TCreateSynonymStmt createSynonym = (TCreateSynonymStmt) stmt;
243            analyzeCreateSynonym(createSynonym);
244        } else if (stmt instanceof TStoredProcedureSqlStatement) {
245            TStoredProcedureSqlStatement procedure = (TStoredProcedureSqlStatement) stmt;
246            analyzeStoredProcedureStmt(procedure);
247        }
248        stmtStack.pop();
249    }
250
251    private void analyzeStoredProcedureStmt(TStoredProcedureSqlStatement procedure) {
252        if (procedure instanceof TPlsqlCreatePackage) {
253            analyzeProcedure(procedure, ESQLDataObjectType.dotOraclePackage);
254            return;
255        }
256        if (procedure instanceof TCreateTriggerStmt) {
257            analyzeProcedure(procedure, ESQLDataObjectType.dotTrigger);
258            return;
259        }
260        if (procedure instanceof TCreateFunctionStmt) {
261            analyzeProcedure(procedure, ESQLDataObjectType.dotFunction);
262            return;
263        }
264        if (procedure instanceof TCreateProcedureStmt) {
265            analyzeProcedure(procedure, ESQLDataObjectType.dotProcedure);
266            return;
267        }
268    }
269
270    private void analyzeProcedure(TStoredProcedureSqlStatement procedure, ESQLDataObjectType type) {
271        TObjectName procedureName = getProcedureName(procedure);
272        if(procedureName == null){
273            return;
274        }
275        TSQLRoutine sqlProcedure = addSQLRoutineToSQLEnv(procedureName, type);
276        sqlProcedure.setDefinition(procedure.toString());
277        
278        if(procedure instanceof TPlsqlCreatePackage) {
279                TPlsqlCreatePackage oraclePackage = (TPlsqlCreatePackage)procedure;
280                if (oraclePackage.getDeclareStatements() != null) {
281                                for (int i = 0; i < oraclePackage.getDeclareStatements().size(); ++i) {
282                                        TCustomSqlStatement stmt = oraclePackage.getDeclareStatements().get(i);
283                                        if (stmt instanceof TPlsqlCreateTrigger) {
284                                                analyzePackageProcedure((TSQLOraclePackage) sqlProcedure, (TStoredProcedureSqlStatement)stmt,
285                                                                ESQLDataObjectType.dotTrigger);
286                                        }
287                                        if (stmt instanceof TPlsqlCreateFunction) {
288                                                analyzePackageProcedure((TSQLOraclePackage) sqlProcedure, (TStoredProcedureSqlStatement)stmt,
289                                                                ESQLDataObjectType.dotFunction);
290                                        }
291                                        if (stmt instanceof TPlsqlCreateProcedure) {
292                                                analyzePackageProcedure((TSQLOraclePackage) sqlProcedure, (TStoredProcedureSqlStatement)stmt,
293                                                                ESQLDataObjectType.dotProcedure);
294                                        }
295                                }
296                        }
297        }
298    }
299    
300    private void analyzePackageProcedure(TSQLOraclePackage oraclePackage, TStoredProcedureSqlStatement procedure, ESQLDataObjectType type) {
301        TObjectName procedureName = getProcedureName(procedure);
302        if(procedureName == null){
303            return;
304        }
305                TSQLProcedure object = (TSQLProcedure) oraclePackage
306                                .createSchemaObject(getQualifiedTableName(procedureName), type);
307        object.setDefinition(procedure.toString());
308    }
309
310
311    private TObjectName getProcedureName(TParseTreeNode procedureObject) {
312        if(procedureObject instanceof TTeradataCreateProcedure)
313        {
314            return ((TTeradataCreateProcedure)procedureObject).getProcedureName();
315        }
316        if(procedureObject instanceof TStoredProcedureSqlStatement)
317        {
318            return ((TStoredProcedureSqlStatement)procedureObject).getStoredProcedureName();
319        }
320        if(procedureObject instanceof TFunctionCall)
321        {
322            return ((TFunctionCall)procedureObject).getFunctionName();
323        }
324        return null;
325    }
326
327    private void analyzeCreateTable(TCreateTableSqlStatement createTable) {
328        // A call-frame table (SQL Server #t) is registered at its declaration by resolver2, where it is visible.
329        if (!includeLocalTemporaryTables
330                && TemporaryTableSemantics.ofDeclaration(createTable) == TemporaryTableSemantics.Scope.CALL) return;
331        if (createTable.getTables() != null && createTable.getTables().size() > 0) {
332            if (createTable.getColumnList() != null && createTable.getColumnList().size() > 0) {
333                TObjectName tableName = createTable.getTableName();
334                TSQLTable table = addTableToSQLEnv(tableName);
335                for (int i = 0; i < createTable.getColumnList().size(); i++) {
336                    TColumnDefinition column = createTable.getColumnList().getColumn(i);
337                    if (column != null && column.getColumnName() != null) {
338                        TObjectName columnName = column.getColumnName();
339                        addTableColumnToSQLEnv(table, columnName, column.getDatatype());
340                    }
341                }
342                table.setPriority(2);
343            } else if (createTable.getSubQuery() != null && createTable.getSubQuery().getResultColumnList() != null) {
344                if(createTable.getSubQuery().getResultColumnList().toString().indexOf("*") != -1) {
345                        return;
346                }
347                TSQLTable table = addTableToSQLEnv(createTable.getTargetTable().getTableName());
348                if (table.getColumns(true) == null || table.getColumns(true).isEmpty()) {
349                    for (int i = 0; i < createTable.getSubQuery().getResultColumnList().size(); i++) {
350                        TResultColumn resultColumn = createTable.getSubQuery().getResultColumnList().getResultColumn(i);
351                        addTableColumnToSQLEnv(table, getObjectName(resultColumn));
352                    }
353                }
354            }
355        }
356    }
357
358    private void analyzeCreateView(TCreateViewSqlStatement createView) {
359        if (createView.getViewName() != null) {
360            if (createView.getViewAliasClause() != null) {
361                TSQLTable table = addTableToSQLEnv(createView.getViewName());
362                table.setView(true);
363                for (int i = 0; i < createView.getViewAliasClause().getViewAliasItemList().size(); i++) {
364                    TViewAliasItem column = createView.getViewAliasClause().getViewAliasItemList().getViewAliasItem(i);
365                    if (column != null && column.getAlias() != null) {
366                        TObjectName columnName = column.getAlias();
367                        addTableColumnToSQLEnv(table, columnName);
368                    }
369                }
370                table.setPriority(2);
371            } else if (createView.getSubquery().getResultColumnList() != null) {
372                if(createView.getSubquery().getResultColumnList().toString().indexOf("*") != -1) {
373                        return;
374                }
375                                TSQLTable table = addTableToSQLEnv(createView.getViewName());
376                                table.setView(true);
377                                for (int i = 0; i < createView.getSubquery().getResultColumnList().size(); i++) {
378                                        TResultColumn resultColumn = createView.getSubquery().getResultColumnList().getResultColumn(i);
379                                        addTableColumnToSQLEnv(table, getObjectName(resultColumn));
380                                }
381            }
382        }
383    }
384
385    private void analyzeCreateSynonym(TCreateSynonymStmt createSynonym) {
386        TObjectName synonymName = createSynonym.getSynonymName();
387        TObjectName forName = createSynonym.getForName();
388        if (synonymName == null || forName == null) {
389            return;
390        }
391        if (!isEnableGetMetadataFromDDL()) {
392            return;
393        }
394        String qualifiedSynonymName = buildFullyQualifiedTableName(synonymName);
395        TSQLSchemaObject schemaObject = doAddSchemaObject(qualifiedSynonymName, ESQLDataObjectType.dotSynonyms);
396        if (!(schemaObject instanceof TSQLSynonyms)) {
397            return;
398        }
399        TSQLSynonyms synonym = (TSQLSynonyms) schemaObject;
400        synonym.setBaseTarget(
401                TSQLEnv.getDatabaseName(forName),
402                TSQLEnv.getSchemaName(forName),
403                TSQLEnv.getObjectName(forName));
404        synonym.setPriority(2);
405    }
406
407    private TObjectName getObjectName(TResultColumn resultColumn) {
408        if (resultColumn.getAliasClause() != null && resultColumn.getAliasClause().getAliasName() != null) {
409            return resultColumn.getAliasClause().getAliasName();
410        }
411        if (resultColumn.getFieldAttr() != null) {
412            return resultColumn.getFieldAttr();
413        }
414        if (resultColumn.getExpr() != null
415                && resultColumn.getExpr().getExpressionType() == EExpressionType.simple_object_name_t) {
416            return resultColumn.getExpr().getObjectOperand();
417        } else if (!SQLUtil.isEmpty(resultColumn.getColumnNameOnly())) {
418            TObjectName objectName = new TObjectName();
419            objectName.setString(resultColumn.getColumnNameOnly());
420            return objectName;
421        } else {
422            return null;
423        }
424    }
425
426    private void analyzeUseDatabase(TUseDatabase useDatabase) {
427        TObjectName databaseName = useDatabase.getDatabaseName();
428
429        if (databaseName == null) {
430            return;
431        }
432
433        // MantisBT 4677 — Snowflake states the form outright, so do not re-guess
434        // it from catalog contents. There, USE <name> with an unqualified name
435        // is a DATABASE switch and USE <db>.<schema> is a SCHEMA switch; the
436        // splitter and the grammar already classified the statement on that
437        // rule. Guessing here would silently flip a bare USE back to a schema
438        // whenever the current database happens to contain a same-named schema
439        // — a wrong session context with nothing to flag it. Other vendors,
440        // whose bare USE really is ambiguous, keep the catalog-based guess.
441        if (getDBVendor() == EDbVendor.dbvsnowflake) {
442            if (useDatabase.isSchema()) {
443                globalSchema = databaseName.toString();
444                schemaUnsetByDatabaseSwitch = false;
445            } else {
446                String catalogName = databaseName.toString();
447                getSQLCatalog(catalogName, true);
448                globalDatabase = catalogName;
449                // The schema carried over from the PREVIOUS database is no
450                // longer valid once the database changes; leaving it would
451                // qualify later names as <newdb>.<oldschema>. See the matching
452                // note in DatabaseContextTracker.
453                globalSchema = null;
454                // Nulling globalSchema alone is not enough: getCurrentSchemaName
455                // falls back to getDefaultSchemaName(), which TUseSchema wrote
456                // while PARSING and which therefore holds the last USE SCHEMA of
457                // the whole script - the very value this switch invalidated. The
458                // flag says "unset", as opposed to "never set", so the fallback
459                // is skipped and objects created after the switch register under
460                // DEFAULT_SCHEMA_NAME instead of the old database's schema.
461                // GitHub #715.
462                schemaUnsetByDatabaseSwitch = true;
463            }
464            return;
465        }
466        String qualifiedSchemaName = databaseName.toString();
467        if (getNumberOfPart(databaseName) == 1) {
468            qualifiedSchemaName = getCurrentDatabaseName() + "." + qualifiedSchemaName;
469        }
470        if (getSQLSchema(qualifiedSchemaName, false) != null) {
471            globalSchema = databaseName.toString();
472        } else {
473            String catalogName = databaseName.toString();
474            getSQLCatalog(catalogName, true);
475            globalDatabase = catalogName;
476        }
477    }
478
479    private int getNumberOfPart(TObjectName name) {
480        return SQLUtil.parseNames(name.toString()).size();
481    }
482
483    private int getNumberOfPart(String tableName) {
484        return SQLUtil.parseNames(tableName).size();
485    }
486
487    private void analyzeCreateSchema(TCreateSchemaSqlStatement createSchema) {
488        TObjectName schemaName = createSchema.getSchemaName();
489        if (schemaName != null) {
490            String qualifiedSchemaName = schemaName.toString();
491            if (getNumberOfPart(schemaName) == 1) {
492                qualifiedSchemaName = getCurrentDatabaseName() + "." + qualifiedSchemaName;
493            }
494            getSQLSchema(qualifiedSchemaName, true);
495        }
496    }
497
498    private String getCurrentDatabaseName() {
499        Pair3<String, String, String> info = fillSchemaInfo();
500        if(!SQLUtil.isEmpty(info.second)) {
501                return info.second;
502        }
503        if (!SQLUtil.isEmpty(globalDatabase)) {
504            return globalDatabase;
505        } else if (!SQLUtil.isEmpty(getDefaultCatalogName())) {
506            return getDefaultCatalogName();
507        } else
508            getSQLCatalog(DEFAULT_DB_NAME, true);
509        return DEFAULT_DB_NAME;
510    }
511
512    private String getCurrentSchemaName() {
513        Pair3<String, String, String> info = fillSchemaInfo();
514        if(!SQLUtil.isEmpty(info.third)) {
515                return info.third;
516        }
517        if (!SQLUtil.isEmpty(globalSchema)) {
518            return globalSchema;
519        } else if (schemaUnsetByDatabaseSwitch) {
520            // Unset - unless this catalog proves the new database has a PUBLIC,
521            // which is what Snowflake makes current. See
522            // TSQLEnv.findSchemaAfterDatabaseSwitch.
523            String afterSwitch = findSchemaAfterDatabaseSwitch(getCurrentDatabaseName());
524            return SQLUtil.isEmpty(afterSwitch) ? DEFAULT_SCHEMA_NAME : afterSwitch;
525        } else if (!SQLUtil.isEmpty(getDefaultSchemaName())) {
526            return getDefaultSchemaName();
527        } else
528            return DEFAULT_SCHEMA_NAME;
529    }
530    
531        private Pair3<String, String, String> fillSchemaInfo() {
532                String server = null;
533                String database = null;
534                String schema = null;
535
536                TCustomSqlStatement stmt = DlineageUtil.getTopStmt(stmtStack.peek());
537                String sqlComment = null;
538                try {
539                        sqlComment = stmt.getCommentBeforeNode();
540                } catch (Exception e) {
541                        // e.printStackTrace();
542                }
543                if (!SQLUtil.isEmpty(sqlComment) && (sqlComment.indexOf("db") != -1 || sqlComment.indexOf("schema") != -1)) {
544                        Properties properties = new Properties();
545                        try {
546                                properties.load(
547                                                new ByteArrayInputStream(sqlComment.replace("--", "").trim().replace(",", "\n").getBytes()));
548                                if (SQLUtil.isEmpty(server) && properties.containsKey("db-instance")) {
549                                        server = properties.getProperty("db-instance");
550                                        if (!SQLUtil.isEmpty(server) && !server.equals(TSQLEnv.DEFAULT_SERVER_NAME)) {
551                                                if (SQLUtil.isEmpty(this.getDefaultServerName())
552                                                                || this.getDefaultServerName().equals(TSQLEnv.DEFAULT_SERVER_NAME)) {
553                                                        this.setDefaultServerName(server);
554                                                }
555                                        }
556                                }
557                                if (SQLUtil.isEmpty(database) && properties.containsKey("db")) {
558                                        database = properties.getProperty("db");
559                                        if (database.indexOf(".") != -1) {
560                                                database = SQLUtil.quoteDottedName(getDBVendor(), ESQLDataObjectType.dotCatalog, database);
561                                        }
562                                }
563                                if (SQLUtil.isEmpty(schema) && properties.containsKey("schema")) {
564                                        schema = properties.getProperty("schema");
565                                        if (schema.indexOf(".") != -1) {
566                                                schema = SQLUtil.quoteDottedName(getDBVendor(), ESQLDataObjectType.dotSchema, schema);
567                                        }
568                                }
569                        } catch (IOException e) {
570                                e.printStackTrace();
571                        }
572                }
573
574                Pair3<String, String, String> info = new Pair3<String, String, String>(server, database, schema);
575                return info;
576        }
577
578
579    private void analyzeMssqlCreateFunction(TMssqlCreateFunction createFunction) {
580        if (createFunction.getReturnTableVaraible() != null && createFunction.getReturnTableDefinitions() != null) {
581            TObjectName tableName = createFunction.getReturnTableVaraible();
582            TSQLTable table = addTableToSQLEnv(tableName);
583
584            for (int j = 0; j < createFunction.getReturnTableDefinitions().size(); j++) {
585                TTableElement tableElement = createFunction.getReturnTableDefinitions().getTableElement(j);
586                TColumnDefinition column = tableElement.getColumnDefinition();
587                if (column != null && column.getColumnName() != null) {
588                    TObjectName columnName = column.getColumnName();
589                    addTableColumnToSQLEnv(table, columnName);
590                }
591            }
592        }
593    }
594
595    private void addTableColumnToSQLEnv(TSQLTable table, TObjectName columnName) {
596        if (columnName == null) {
597            return;
598        }
599        String column = columnName.getColumnNameOnly();
600        if (!SQLUtil.isEmpty(column)) {
601            table.addColumn(column);
602        } else {
603            column = columnName.toString();
604            if (column.indexOf(".") != -1) {
605                List<String> splits = SQLUtil.parseNames(column);
606                column = splits.get(splits.size() - 1);
607            }
608            table.addColumn(column);
609        }
610    }
611    
612    private void addTableColumnToSQLEnv(TSQLTable table, TObjectName columnName, TTypeName dataType) {
613        if(columnName == null){
614            return;
615        }
616        String column = columnName.getColumnNameOnly();
617        if (!SQLUtil.isEmpty(column)) {
618            table.addColumn(column, dataType);
619        } else {
620            column = columnName.toString();
621            if (column.indexOf(".") != -1) {
622                List<String> splits = SQLUtil.parseNames(column);
623                column = splits.get(splits.size() - 1);
624            }
625            table.addColumn(column, dataType);
626        }
627    }
628
629    private TSQLTable addTableToSQLEnv(TObjectName tableName) {
630        return addTable(buildFullyQualifiedTableName(tableName), true);
631    }
632
633    private String buildFullyQualifiedTableName(TObjectName tableName) {
634        String qualifiedTableName = getQualifiedTableName(tableName);
635        if (getNumberOfPart(qualifiedTableName) == 1) {
636            if(!TSQLEnv.supportSchema(getDBVendor())){
637                qualifiedTableName = TSQLEnv.DEFAULT_DB_NAME + "." + getCurrentDatabaseName() + "." + qualifiedTableName;
638            }
639            else {
640                qualifiedTableName = getCurrentDatabaseName() + "." + getCurrentSchemaName() + "." + qualifiedTableName;
641            }
642        } else if (getNumberOfPart(qualifiedTableName) == 2) {
643            if(!TSQLEnv.supportSchema(getDBVendor())){
644                qualifiedTableName = TSQLEnv.DEFAULT_DB_NAME + "." + qualifiedTableName;
645            }
646            else {
647                qualifiedTableName = getCurrentDatabaseName() + "." + qualifiedTableName;
648            }
649        }
650        return qualifiedTableName;
651    }
652
653    private TSQLRoutine addSQLRoutineToSQLEnv(TObjectName procedureName, ESQLDataObjectType type) {
654        String qualifiedProcedureName = getQualifiedTableName(procedureName);
655        if (getNumberOfPart(qualifiedProcedureName) == 1) {
656            if(!TSQLEnv.supportSchema(getDBVendor())){
657                qualifiedProcedureName = TSQLEnv.DEFAULT_DB_NAME + "." + getCurrentDatabaseName() + "." + qualifiedProcedureName;
658            }
659            else {
660                qualifiedProcedureName = getCurrentDatabaseName() + "." + getCurrentSchemaName() + "." + qualifiedProcedureName;
661            }
662        } else if (getNumberOfPart(qualifiedProcedureName) == 2) {
663            if(!TSQLEnv.supportSchema(getDBVendor())){
664                qualifiedProcedureName = TSQLEnv.DEFAULT_DB_NAME + "." + qualifiedProcedureName;
665            }
666            else {
667                qualifiedProcedureName = getCurrentDatabaseName() + "." + qualifiedProcedureName;
668            }
669        }
670        return addSQLRoutine(qualifiedProcedureName, true, type);
671    }
672
673    private String getQualifiedTableName(TObjectName table) {
674        String tableName = table.toString();
675        if (metadataSQLEnv == null) {
676            return tableName;
677        }
678        List<String> segments = SQLUtil.parseNames(tableName);
679        if (segments.size() > 2) {
680            return tableName;
681        }
682        String schemaName = null;
683        if (segments.size() > 1) {
684            schemaName = segments.get(segments.size() - 2);
685        }
686        int occurrence = 0;
687        int maxPriority = -1;
688        String tableDatabase = null;
689        String tableSchema = null;
690        String simpleTableName = DlineageUtil.getSimpleTableName(tableName);
691        for (TSQLCatalog catalog : metadataSQLEnv.getCatalogList()) {
692            if (SQLUtil.isEmpty(schemaName)) {
693                for (TSQLSchema schema : catalog.getSchemaList()) {
694                    TSQLTable tsqlTable = schema.findTable(simpleTableName);
695                    if (tsqlTable != null) {
696                        occurrence += 1;
697                        if (tsqlTable.getPriority() > maxPriority) {
698                            tableDatabase = catalog.getName();
699                            tableSchema = schema.getName();
700                            maxPriority = tsqlTable.getPriority();
701                        }
702                    }
703                }
704                if (occurrence > 1) {
705                    break;
706                }
707            } else {
708                TSQLSchema schema = catalog.getSchema(schemaName, false);
709                if (schema != null) {
710                    TSQLTable tsqlTable = schema.findTable(simpleTableName);
711                    if (tsqlTable != null) {
712                        occurrence += 1;
713                        if (tsqlTable.getPriority() > maxPriority) {
714                            tableDatabase = catalog.getName();
715                            tableSchema = schema.getName();
716                            maxPriority = tsqlTable.getPriority();
717                        }
718                    }
719                }
720            }
721        }
722        if (occurrence == 1 || maxPriority > 0) {
723            StringBuilder qualifiedTableName = new StringBuilder();
724            if (tableDatabase != null && !TSQLEnv.DEFAULT_DB_NAME.equalsIgnoreCase(tableDatabase)) {
725                qualifiedTableName.append(tableDatabase).append(".");
726            }
727            if (tableSchema != null && !TSQLEnv.DEFAULT_SCHEMA_NAME.equalsIgnoreCase(tableSchema)) {
728                qualifiedTableName.append(tableSchema).append(".");
729            }
730            return qualifiedTableName.append(simpleTableName).toString();
731        }
732        return tableName;
733    }
734
735    private void analyzeMssqlDeclare(TMssqlDeclare declare) {
736        TDeclareVariableList variables = declare.getVariables();
737        if (variables == null) {
738            return;
739        }
740        for (int i = 0; i < variables.size(); i++) {
741            TDeclareVariable variable = variables.getDeclareVariable(i);
742            if (variable.getTableTypeDefinitions() == null || variable.getTableTypeDefinitions().size() == 0) {
743                continue;
744            }
745
746            TObjectName tableName = variable.getVariableName();
747            TSQLTable table = addTableToSQLEnv(tableName);
748
749            TTableElementList columns = variable.getTableTypeDefinitions();
750            for (int j = 0; j < columns.size(); j++) {
751                TTableElement tableElement = columns.getTableElement(j);
752                TColumnDefinition column = tableElement.getColumnDefinition();
753                if (column != null && column.getColumnName() != null) {
754                    TObjectName columnName = column.getColumnName();
755                    addTableColumnToSQLEnv(table, columnName);
756                }
757            }
758        }
759    }
760
761    public static void main(String[] args) {
762                TSQLEnv sqlenv = new TDDLSQLEnv("deawirddap001", "mio", "public", EDbVendor.dbvredshift, SQLUtil.getFileContent("C:\\Users\\KK\\Desktop\\ddl.sql"));
763                sqlenv.initSQLEnv();
764                System.out.println(sqlenv.toString());
765    }
766}