001package gudusoft.gsqlparser.sqlenv.catalog;
002
003import gudusoft.gsqlparser.EDbVendor;
004import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType;
005import gudusoft.gsqlparser.sqlenv.IdentifierProfile;
006import gudusoft.gsqlparser.sqlenv.IdentifierService;
007import gudusoft.gsqlparser.sqlenv.TSQLCatalog;
008import gudusoft.gsqlparser.sqlenv.TSQLEnv;
009import gudusoft.gsqlparser.sqlenv.TSQLSchemaObject;
010import gudusoft.gsqlparser.sqlenv.CatalogStore;
011
012import java.util.ArrayList;
013import java.util.List;
014import java.util.concurrent.ConcurrentHashMap;
015import java.util.Map;
016
017/**
018 * CatalogStore Provider(新实现)
019 *
020 * <p>此实现基于 CatalogStore,使用 IdentifierService 进行标识符处理。
021 *
022 * @since 3.2.0 (Phase 1)
023 */
024public class CatalogStoreProvider implements ICatalogProvider {
025
026    private final EDbVendor vendor;
027    private final CatalogStore catalogStore;
028    private final IdentifierService identifierService;
029    private String defaultCatalogName;
030
031    // Cache for TSQLCatalog objects (for compatibility with TSQLEnv API)
032    private final Map<String, TSQLCatalog> catalogCache = new ConcurrentHashMap<>();
033
034    /**
035     * Creates a CatalogStoreProvider with default flags
036     *
037     * @param vendor database vendor
038     */
039    public CatalogStoreProvider(EDbVendor vendor) {
040        this(vendor, IdentifierProfile.VendorFlags.defaults());
041    }
042
043    /**
044     * Creates a CatalogStoreProvider with custom vendor flags
045     *
046     * @param vendor database vendor
047     * @param flags vendor-specific flags
048     */
049    public CatalogStoreProvider(EDbVendor vendor, IdentifierProfile.VendorFlags flags) {
050        this(IdentifierProfile.forVendor(vendor, flags));
051    }
052
053    /**
054     * Creates a CatalogStoreProvider under ONE analysis's identifier configuration.
055     *
056     * <p>The profile reaches the catalog store and every index built inside it, so registration,
057     * lookup and enumeration observe the same per-category rules the rest of the analysis does.
058     * A provider built from a bare vendor keeps the vendor default.
059     *
060     * @param profile the effective identifier configuration; its vendor is the provider's vendor
061     */
062    public CatalogStoreProvider(IdentifierProfile profile) {
063        this(IdentifierService.forProfile(profile));
064    }
065
066    /**
067     * Creates a CatalogStoreProvider over an existing analysis service.
068     *
069     * @param identifierService the frozen service of the current analysis
070     */
071    public CatalogStoreProvider(IdentifierService identifierService) {
072        this.vendor = identifierService.getProfile().getVendor();
073        this.identifierService = identifierService;
074        this.catalogStore = new CatalogStore(this.vendor, identifierService);
075    }
076
077    /**
078     * Creates a CatalogStoreProvider wrapping existing CatalogStore
079     *
080     * @param catalogStore existing catalog store
081     * @param identifierService identifier service
082     */
083    public CatalogStoreProvider(CatalogStore catalogStore, IdentifierService identifierService) {
084        this.vendor = catalogStore.getVendor();
085        this.catalogStore = catalogStore;
086        this.identifierService = identifierService;
087    }
088
089    @Override
090    public TSQLCatalog getCatalog(String catalogName) {
091        if (catalogName == null || catalogName.isEmpty()) {
092            return null;
093        }
094
095        // Normalize catalog name
096        String normalizedName = identifierService.normalize(catalogName, ESQLDataObjectType.dotCatalog);
097
098        // Return cached catalog if exists
099        return catalogCache.get(normalizedName);
100    }
101
102    @Override
103    public TSQLCatalog createCatalog(String catalogName) {
104        if (catalogName == null || catalogName.isEmpty()) {
105            throw new IllegalArgumentException("catalogName cannot be null or empty");
106        }
107
108        // Normalize catalog name
109        String normalizedName = identifierService.normalize(catalogName, ESQLDataObjectType.dotCatalog);
110
111        // Return existing if already cached
112        TSQLCatalog existing = catalogCache.get(normalizedName);
113        if (existing != null) {
114            return existing;
115        }
116
117        // Note: We cannot create TSQLCatalog here because it requires TSQLEnv
118        // This is a limitation of the current architecture
119        // For now, return null and let the caller handle it
120        // In Phase 2-3, we'll refactor TSQLCatalog to not require TSQLEnv
121
122        return null;
123    }
124
125    @Override
126    public List<TSQLCatalog> getAllCatalogs() {
127        return new ArrayList<>(catalogCache.values());
128    }
129
130    @Override
131    public String getDefaultCatalogName() {
132        return defaultCatalogName;
133    }
134
135    @Override
136    public void setDefaultCatalogName(String name) {
137        this.defaultCatalogName = name;
138    }
139
140    @Override
141    public TSQLSchemaObject findObject(String catalog, String schema,
142                                        String objectName, ESQLDataObjectType type) {
143        // Delegate directly to CatalogStore (it handles normalization internally)
144        // The ICatalogProvider contract: TSQLEnv normalizes the segments before it asks.
145        return catalogStore.getNormalized(catalog, schema, objectName, type);
146    }
147
148    @Override
149    public void addObject(TSQLSchemaObject object) {
150        if (object == null) {
151            return;
152        }
153
154        // Add to CatalogStore
155        catalogStore.put(object);
156
157        // Update catalog cache if needed
158        if (object.getSchema() != null && object.getSchema().getCatalog() != null) {
159            TSQLCatalog catalog = object.getSchema().getCatalog();
160            String normalizedName = identifierService.normalize(
161                catalog.getName(),
162                ESQLDataObjectType.dotCatalog
163            );
164            catalogCache.putIfAbsent(normalizedName, catalog);
165        }
166    }
167
168    @Override
169    public boolean removeObject(TSQLSchemaObject object) {
170        if (object == null) {
171            return false;
172        }
173
174        return catalogStore.remove(object);
175    }
176
177    @Override
178    public void clear() {
179        catalogStore.clear();
180        catalogCache.clear();
181    }
182
183    @Override
184    public int size() {
185        return catalogStore.size();
186    }
187
188    /**
189     * Get the underlying CatalogStore
190     *
191     * @return catalog store
192     */
193    public CatalogStore getCatalogStore() {
194        return catalogStore;
195    }
196
197    /**
198     * Get the IdentifierService
199     *
200     * @return identifier service
201     */
202    public IdentifierService getIdentifierService() {
203        return identifierService;
204    }
205
206    /**
207     * Register a catalog in the cache (used by TSQLEnv during migration)
208     *
209     * @param catalog the catalog to register
210     */
211    public void registerCatalog(TSQLCatalog catalog) {
212        if (catalog == null) {
213            return;
214        }
215
216        String normalizedName = identifierService.normalize(
217            catalog.getName(),
218            ESQLDataObjectType.dotCatalog
219        );
220        catalogCache.put(normalizedName, catalog);
221    }
222}