001package gudusoft.gsqlparser.catalog.runtime;
002
003import gudusoft.gsqlparser.catalog.diagnostic.CatalogException;
004
005/**
006 * Reference {@link CatalogProvider} implementation for tests, demos, and the in-memory
007 * fixture path. Backed by an {@link InMemoryCatalogSnapshot}.
008 *
009 * <p>Plan §6 / §7.2. {@link #snapshot(CatalogQuery)} ignores the query's narrowing
010 * options — the in-memory snapshot is fully materialized — and returns the same instance
011 * on every call until {@link #close()} is invoked. {@link #refresh(CatalogQuery)} forwards
012 * to the seed {@link CatalogProviderConfig} when present (the eager case) or returns the
013 * existing snapshot (the test-fixture case).</p>
014 *
015 * <p>Lifecycle: {@link #open(CatalogProviderConfig)} accepts a config carrying a
016 * {@code "snapshot"} property whose value is the {@link CatalogSnapshot} to serve;
017 * alternatively, a snapshot can be installed at construction time via
018 * {@link #InMemoryCatalogProvider(CatalogSnapshot)}.</p>
019 */
020public final class InMemoryCatalogProvider implements CatalogProvider {
021
022    /** Property key used by {@link #open(CatalogProviderConfig)} to receive the seed snapshot. */
023    public static final String CONFIG_SNAPSHOT = "snapshot";
024
025    private static final CatalogProviderId ID = CatalogProviderId.of("in-memory");
026
027    private CatalogSnapshot snapshot;
028    private boolean open;
029
030    public InMemoryCatalogProvider() {
031    }
032
033    public InMemoryCatalogProvider(CatalogSnapshot snapshot) {
034        this.snapshot = snapshot;
035    }
036
037    @Override
038    public CatalogProviderId id() {
039        return ID;
040    }
041
042    @Override
043    public void open(CatalogProviderConfig config) throws CatalogException {
044        Object seed = config != null ? config.property(CONFIG_SNAPSHOT) : null;
045        if (seed != null) {
046            if (!(seed instanceof CatalogSnapshot)) {
047                throw new CatalogException("InMemoryCatalogProvider.open: '"
048                    + CONFIG_SNAPSHOT + "' property must be a CatalogSnapshot");
049            }
050            this.snapshot = (CatalogSnapshot) seed;
051        }
052        this.open = true;
053    }
054
055    @Override
056    public CatalogSnapshot snapshot(CatalogQuery query) throws CatalogException {
057        if (!open) {
058            throw new CatalogException("InMemoryCatalogProvider must be opened before snapshot()");
059        }
060        if (snapshot == null) {
061            throw new CatalogException(
062                "InMemoryCatalogProvider has no snapshot installed; pass one via constructor "
063                    + "or via CatalogProviderConfig property '" + CONFIG_SNAPSHOT + "'");
064        }
065        return snapshot;
066    }
067
068    @Override
069    public CatalogSnapshot refresh(CatalogQuery query) throws CatalogException {
070        return snapshot(query);
071    }
072
073    @Override
074    public void close() throws CatalogException {
075        this.open = false;
076    }
077
078    /** ServiceLoader-discoverable factory. */
079    public static final class Factory implements CatalogProviderFactory {
080
081        public Factory() {
082            // Required no-arg constructor for ServiceLoader.
083        }
084
085        @Override
086        public CatalogProviderId id() {
087            return ID;
088        }
089
090        @Override
091        public CatalogProvider create() {
092            return new InMemoryCatalogProvider();
093        }
094    }
095}