001package gudusoft.gsqlparser.resolver;
002
003import gudusoft.gsqlparser.EDbObjectType;
004import gudusoft.gsqlparser.TStatementList;
005import gudusoft.gsqlparser.compiler.TContext;
006import gudusoft.gsqlparser.compiler.TScope;
007import gudusoft.gsqlparser.nodes.TObjectName;
008import gudusoft.gsqlparser.nodes.TParseTreeVisitor;
009import gudusoft.gsqlparser.stmt.TSetSchemaStmt;
010import gudusoft.gsqlparser.stmt.TUseDatabase;
011
012import java.util.Stack;
013
014/**
015 * TDatabaseObjectResolver handles the resolution of database objects in SQL statements.
016 * It primarily focuses on resolving schema and database names for objects, and handling
017 * USE DATABASE/SET SCHEMA statements to maintain current context.
018 * 
019 * 目的是确保所有数据库对象都有正确的database/schema 限定名称,通过维护当前上下文并填充隐式名称来实现这一目标
020 */
021public class TDatabaseObjectResolver extends TParseTreeVisitor {
022    private TContext globalScope;        // Stores global context including SQL environment
023    private TStatementList sqlStatements; // List of SQL statements to analyze
024    private Stack<TScope> scopeStack = new Stack<>();  // Maintains scope hierarchy
025    
026    /**
027     * Constructor for TDatabaseObjectResolver.
028     * Initializes the resolver with SQL statements and global context.
029     *
030     * @param sqls List of SQL statements to analyze
031     * @param scope Global context containing SQL environment information
032     */
033    public TDatabaseObjectResolver(TStatementList sqls, TContext scope) {
034        this.globalScope = scope;
035        sqlStatements = sqls;
036        scopeStack.push(globalScope);
037    }
038
039    /**
040     * Main resolution method that processes all SQL statements.
041     * Iterates through statements and resolves database objects in each one.
042     */
043    public void resolve() {
044        for (int i = 0; i < sqlStatements.size(); i++) {
045            sqlStatements.get(i).acceptChildren(this);
046        }
047    }
048
049    /**
050     * Handles USE DATABASE statements to set the current database context.
051     * In some databases (e.g., Databricks), USE DATABASE is equivalent to USE SCHEMA,
052     * so this method handles both cases based on the current state.
053     *
054     * @param node The USE DATABASE statement node
055     */
056    public void preVisit(TUseDatabase node) {
057        // Set default database in sqlEnv
058        // Note: In some databases like Databricks, USE DATABASE is equivalent to USE SCHEMA
059        if (node.getDatabaseName() != null) {
060            globalScope.getSqlEnv().setDefaultCatalogName(node.getDatabaseName().toString());
061        } else if (node.getSchemaName() != null) {
062            globalScope.getSqlEnv().setDefaultSchemaName(node.getSchemaName().toString());
063        }
064    }
065
066    /**
067     * Processes SET SCHEMA statements to update the current schema context.
068     *
069     * @param node The SET SCHEMA statement node
070     */
071    public void preVisit(TSetSchemaStmt node) {
072        globalScope.getSqlEnv().setDefaultSchemaName(node.getSchemaName().toString());
073    }
074
075    /**
076     * Resolves individual database object names by setting implicit database and schema names
077     * when they are not explicitly specified in the object name.
078     *
079     * @param node The object name node to resolve
080     */
081    public void preVisit(TObjectName node) {
082        // Skip resolution for column objects as they're handled separately
083        if (node.getDbObjectType() == EDbObjectType.column) return;
084
085        // Set default database name if not qualified
086        if ((globalScope.getSqlEnv().getDefaultCatalogName() != null) && 
087            (node.getDatabaseToken() == null)) {
088            node.setImplictDatabaseName(globalScope.getSqlEnv().getDefaultCatalogName());
089        }
090
091        // Set default schema name if not qualified
092        if ((globalScope.getSqlEnv().getDefaultSchemaName() != null) && 
093            (node.getSchemaToken() == null)) {
094            node.setImplictSchemaName(globalScope.getSqlEnv().getDefaultSchemaName());
095        }
096    }
097}