001package gudusoft.gsqlparser.sqlenv;
002
003import gudusoft.gsqlparser.TBaseType;
004import gudusoft.gsqlparser.util.SQLUtil;
005
006import java.util.List;
007
008/**
009 * Hierarchical Resolver (Phase 2 - 分层索引解析器)
010 *
011 * <p>使用分层索引(LinkedHashMap)进行 O(1) schema 对象查找。
012 *
013 * <p><strong>核心设计:</strong>
014 * <ul>
015 * <li>每一层使用 IdentifierService 生成索引键
016 * <li>支持部分限定名(table, schema.table, catalog.schema.table)
017 * <li>当 USE_HIERARCHICAL_INDEX = false 时,回退到 legacy 查找
018 * </ul>
019 *
020 * <p>使用示例:
021 * <pre>
022 * TSQLEnv env = ...;
023 * HierarchicalResolver resolver = new HierarchicalResolver();
024 *
025 * // 查找表
026 * TSQLTable table = resolver.findTable(env, "mydb.myschema.mytable");
027 *
028 * // 查找列
029 * TSQLColumn column = resolver.findColumn(table, "mycolumn");
030 * </pre>
031 *
032 * @since 3.1.0.9
033 */
034public class HierarchicalResolver {
035
036    /**
037     * 查找 schema 对象(通用方法)
038     *
039     * <p>支持三种限定名格式:
040     * <ul>
041     * <li>单段:table(仅对象名)
042     * <li>双段:schema.table(schema + 对象名)
043     * <li>三段:catalog.schema.table(catalog + schema + 对象名)
044     * </ul>
045     *
046     * @param env SQL 环境
047     * @param qualifiedName 限定名(可能是部分限定)
048     * @param objectType 对象类型
049     * @return schema 对象,未找到时返回 null
050     */
051    public TSQLSchemaObject findSchemaObject(TSQLEnv env, String qualifiedName, ESQLDataObjectType objectType) {
052        if (!TBaseType.USE_HIERARCHICAL_INDEX) {
053            // Feature flag disabled, use legacy search
054            return null;
055        }
056
057        if (qualifiedName == null || qualifiedName.isEmpty()) {
058            return null;
059        }
060
061        try {
062            // 解析限定名为段列表(catalog, schema, object)
063            List<String> segments = SQLUtil.parseNames(qualifiedName);
064            if (segments.isEmpty()) {
065                return null;
066            }
067
068            int segmentCount = segments.size();
069
070            // 根据段数决定查找策略
071            if (segmentCount == 1) {
072                // 单段:仅对象名,需要在当前 schema 中查找
073                return findInCurrentSchema(env, segments.get(0), objectType);
074            } else if (segmentCount == 2) {
075                // 双段:schema.object
076                String schemaName = segments.get(0);
077                String objectName = segments.get(1);
078                return findInSchema(env, null, schemaName, objectName, objectType);
079            } else if (segmentCount == 3) {
080                // 三段:catalog.schema.object
081                String catalogName = segments.get(0);
082                String schemaName = segments.get(1);
083                String objectName = segments.get(2);
084                return findInSchema(env, catalogName, schemaName, objectName, objectType);
085            } else {
086                // 超过 3 段,不支持
087                return null;
088            }
089        } catch (Throwable ignore) {
090            // 任何异常都回退到 legacy 查找
091            return null;
092        }
093    }
094
095    /**
096     * 在当前 schema 中查找对象
097     *
098     * @param env SQL 环境
099     * @param objectName 对象名
100     * @param objectType 对象类型
101     * @return schema 对象,未找到时返回 null
102     */
103    private TSQLSchemaObject findInCurrentSchema(TSQLEnv env, String objectName, ESQLDataObjectType objectType) {
104        // Get default catalog and schema names
105        String defaultCatalogName = env.getDefaultCatalogName();
106        String defaultSchemaName = env.getDefaultSchemaName();
107
108        if (defaultSchemaName == null) {
109            return null;
110        }
111
112        // Find the schema object in the default schema
113        return findInSchema(env, defaultCatalogName, defaultSchemaName, objectName, objectType);
114    }
115
116    /**
117     * 在指定 schema 中查找对象
118     *
119     * @param env SQL 环境
120     * @param catalogName catalog 名称(可为 null,表示当前 catalog)
121     * @param schemaName schema 名称
122     * @param objectName 对象名
123     * @param objectType 对象类型
124     * @return schema 对象,未找到时返回 null
125     */
126    private TSQLSchemaObject findInSchema(TSQLEnv env, String catalogName, String schemaName,
127                                          String objectName, ESQLDataObjectType objectType) {
128        // 1. 找到 catalog
129        TSQLCatalog catalog;
130        if (catalogName == null || catalogName.isEmpty()) {
131            // Use default catalog
132            String defaultCatalog = env.getDefaultCatalogName();
133            if (defaultCatalog == null) {
134                return null;
135            }
136            catalog = env.getSQLCatalog(defaultCatalog, false);
137        } else {
138            catalog = env.getSQLCatalog(catalogName, false);
139        }
140
141        if (catalog == null) {
142            return null;
143        }
144
145        // 2. 找到 schema
146        TSQLSchema schema = catalog.searchSchema(schemaName);
147        if (schema == null) {
148            return null;
149        }
150
151        // 3. 找到对象
152        return schema.findSchemaObject(objectType, objectName);
153    }
154
155    /**
156     * 查找表(便捷方法)
157     *
158     * @param env SQL 环境
159     * @param qualifiedName 限定名
160     * @return 表对象,未找到时返回 null
161     */
162    public TSQLTable findTable(TSQLEnv env, String qualifiedName) {
163        TSQLSchemaObject obj = findSchemaObject(env, qualifiedName, ESQLDataObjectType.dotTable);
164        return (obj instanceof TSQLTable) ? (TSQLTable) obj : null;
165    }
166
167    /**
168     * 查找函数(便捷方法)
169     *
170     * @param env SQL 环境
171     * @param qualifiedName 限定名
172     * @return 函数对象,未找到时返回 null
173     */
174    public TSQLFunction findFunction(TSQLEnv env, String qualifiedName) {
175        TSQLSchemaObject obj = findSchemaObject(env, qualifiedName, ESQLDataObjectType.dotFunction);
176        return (obj instanceof TSQLFunction) ? (TSQLFunction) obj : null;
177    }
178
179    /**
180     * 查找存储过程(便捷方法)
181     *
182     * @param env SQL 环境
183     * @param qualifiedName 限定名
184     * @return 存储过程对象,未找到时返回 null
185     */
186    public TSQLProcedure findProcedure(TSQLEnv env, String qualifiedName) {
187        TSQLSchemaObject obj = findSchemaObject(env, qualifiedName, ESQLDataObjectType.dotProcedure);
188        return (obj instanceof TSQLProcedure) ? (TSQLProcedure) obj : null;
189    }
190
191    /**
192     * 查找列(便捷方法)
193     *
194     * @param table 表对象
195     * @param columnName 列名
196     * @return 列对象,未找到时返回 null
197     */
198    public TSQLColumn findColumn(TSQLTable table, String columnName) {
199        if (!TBaseType.USE_HIERARCHICAL_INDEX) {
200            return null;
201        }
202
203        if (table == null || columnName == null || columnName.isEmpty()) {
204            return null;
205        }
206
207        try {
208            return table.getColumn(columnName);
209        } catch (Throwable ignore) {
210            return null;
211        }
212    }
213
214    /**
215     * 批量查找列(便捷方法)
216     *
217     * <p>用于 SELECT * 等场景,需要获取表的所有列。
218     *
219     * @param table 表对象
220     * @return 列列表,表不存在时返回空列表
221     */
222    public List<TSQLColumn> findAllColumns(TSQLTable table) {
223        if (table == null) {
224            return java.util.Collections.emptyList();
225        }
226
227        try {
228            return table.getColumnList();
229        } catch (Throwable ignore) {
230            return java.util.Collections.emptyList();
231        }
232    }
233}