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}