001package gudusoft.gsqlparser.catalog.input;
002
003import gudusoft.gsqlparser.EDbVendor;
004import gudusoft.gsqlparser.catalog.diagnostic.CatalogDiagnosticSink;
005import gudusoft.gsqlparser.catalog.input.model.IdentifierConfig;
006import gudusoft.gsqlparser.catalog.runtime.CatalogSearchPath;
007
008import java.util.Collections;
009import java.util.LinkedHashMap;
010import java.util.Map;
011
012/**
013 * Caller-supplied knobs that drive both the input reader and the runtime provider.
014 *
015 * <p>Plan ยง7.1. Immutable; build via {@link #builder()}.</p>
016 *
017 * <p>Defaults match the Phase 1 reference paths: {@code loadingMode=EAGER},
018 * {@code strict=false}, {@code normalizeOnLoad=true}, includes columns / views /
019 * routines, no caps. {@code identifierConfig} defaults to
020 * {@link IdentifierConfig#defaultsFor(EDbVendor) defaultsFor(vendor)} when unset.</p>
021 */
022public final class CatalogLoadOptions {
023
024    private final EDbVendor vendor;
025    private final String defaultCatalog;
026    private final String defaultSchema;
027    private final String defaultServer;
028    private final CatalogSearchPath searchPath;
029    private final CatalogLoadingMode loadingMode;
030    private final boolean strict;
031    private final boolean normalizeOnLoad;
032    private final long ttlMillis;
033    private final int maxEagerObjects;
034    private final int maxFetchesPerAnalysis;
035    private final boolean includeColumns;
036    private final boolean includeViews;
037    private final boolean includeRoutines;
038    private final IdentifierConfig identifierConfig;
039    private final boolean explicitIdentifierConfig;
040    private final CatalogDiagnosticSink diagnosticSink;
041    private final Map<String, Object> providerProperties;
042
043    private CatalogLoadOptions(Builder b) {
044        if (b.vendor == null) {
045            throw new IllegalArgumentException("CatalogLoadOptions.vendor is required");
046        }
047        if (b.ttlMillis < 0) {
048            throw new IllegalArgumentException(
049                "CatalogLoadOptions.ttlMillis must be >= 0 (got " + b.ttlMillis + ")");
050        }
051        if (b.maxEagerObjects < 0) {
052            throw new IllegalArgumentException(
053                "CatalogLoadOptions.maxEagerObjects must be >= 0 (got " + b.maxEagerObjects + ")");
054        }
055        if (b.maxFetchesPerAnalysis < 0) {
056            throw new IllegalArgumentException(
057                "CatalogLoadOptions.maxFetchesPerAnalysis must be >= 0 (got "
058                    + b.maxFetchesPerAnalysis + ")");
059        }
060        this.vendor = b.vendor;
061        this.defaultCatalog = b.defaultCatalog;
062        this.defaultSchema = b.defaultSchema;
063        this.defaultServer = b.defaultServer;
064        this.searchPath = b.searchPath != null ? b.searchPath : CatalogSearchPath.empty();
065        this.loadingMode = b.loadingMode != null ? b.loadingMode : CatalogLoadingMode.EAGER;
066        this.strict = b.strict;
067        this.normalizeOnLoad = b.normalizeOnLoad;
068        this.ttlMillis = b.ttlMillis;
069        this.maxEagerObjects = b.maxEagerObjects;
070        this.maxFetchesPerAnalysis = b.maxFetchesPerAnalysis;
071        this.includeColumns = b.includeColumns;
072        this.includeViews = b.includeViews;
073        this.includeRoutines = b.includeRoutines;
074        // Only auto-default when the caller did not explicitly pass an identifierConfig.
075        // The caller-explicit flag matters because the validator and downstream consumers
076        // need to know whether to prefer the model's IdentifierConfig (when no override was
077        // supplied) or this one. When the caller did supply an explicit config we still
078        // validate vendor consistency.
079        this.explicitIdentifierConfig = b.identifierConfig != null;
080        this.identifierConfig = b.identifierConfig != null
081            ? b.identifierConfig : IdentifierConfig.defaultsFor(b.vendor);
082        if (this.identifierConfig.vendor() != b.vendor) {
083            throw new IllegalArgumentException(
084                "CatalogLoadOptions: identifierConfig.vendor=" + this.identifierConfig.vendor()
085                    + " does not match vendor=" + b.vendor);
086        }
087        this.diagnosticSink = b.diagnosticSink;
088        this.providerProperties = Collections.unmodifiableMap(
089            new LinkedHashMap<String, Object>(b.providerProperties));
090    }
091
092    public static Builder builder() {
093        return new Builder();
094    }
095
096    public EDbVendor vendor() {
097        return vendor;
098    }
099
100    public String defaultCatalog() {
101        return defaultCatalog;
102    }
103
104    public String defaultSchema() {
105        return defaultSchema;
106    }
107
108    public String defaultServer() {
109        return defaultServer;
110    }
111
112    public CatalogSearchPath searchPath() {
113        return searchPath;
114    }
115
116    public CatalogLoadingMode loadingMode() {
117        return loadingMode;
118    }
119
120    public boolean strict() {
121        return strict;
122    }
123
124    public boolean normalizeOnLoad() {
125        return normalizeOnLoad;
126    }
127
128    public long ttlMillis() {
129        return ttlMillis;
130    }
131
132    public int maxEagerObjects() {
133        return maxEagerObjects;
134    }
135
136    public int maxFetchesPerAnalysis() {
137        return maxFetchesPerAnalysis;
138    }
139
140    public boolean includeColumns() {
141        return includeColumns;
142    }
143
144    public boolean includeViews() {
145        return includeViews;
146    }
147
148    public boolean includeRoutines() {
149        return includeRoutines;
150    }
151
152    public IdentifierConfig identifierConfig() {
153        return identifierConfig;
154    }
155
156    /**
157     * {@code true} when the caller passed an explicit {@link IdentifierConfig} via
158     * {@link Builder#identifierConfig(IdentifierConfig)}; {@code false} when the value
159     * returned by {@link #identifierConfig()} is the auto-filled vendor default.
160     * Consumers that already have a model-supplied {@link IdentifierConfig} can use
161     * this flag to decide whether to prefer the model's value over the auto-fill.
162     */
163    public boolean hasExplicitIdentifierConfig() {
164        return explicitIdentifierConfig;
165    }
166
167    public CatalogDiagnosticSink diagnosticSink() {
168        return diagnosticSink;
169    }
170
171    public Map<String, Object> providerProperties() {
172        return providerProperties;
173    }
174
175    public static final class Builder {
176
177        private EDbVendor vendor;
178        private String defaultCatalog;
179        private String defaultSchema;
180        private String defaultServer;
181        private CatalogSearchPath searchPath;
182        private CatalogLoadingMode loadingMode;
183        private boolean strict;
184        private boolean normalizeOnLoad = true;
185        private long ttlMillis;
186        private int maxEagerObjects;
187        private int maxFetchesPerAnalysis;
188        private boolean includeColumns = true;
189        private boolean includeViews = true;
190        private boolean includeRoutines = true;
191        private IdentifierConfig identifierConfig;
192        private CatalogDiagnosticSink diagnosticSink;
193        private final Map<String, Object> providerProperties = new LinkedHashMap<String, Object>();
194
195        private Builder() {
196        }
197
198        public Builder vendor(EDbVendor v) {
199            this.vendor = v;
200            return this;
201        }
202
203        public Builder defaultCatalog(String v) {
204            this.defaultCatalog = v;
205            return this;
206        }
207
208        public Builder defaultSchema(String v) {
209            this.defaultSchema = v;
210            return this;
211        }
212
213        public Builder defaultServer(String v) {
214            this.defaultServer = v;
215            return this;
216        }
217
218        public Builder searchPath(CatalogSearchPath v) {
219            this.searchPath = v;
220            return this;
221        }
222
223        public Builder loadingMode(CatalogLoadingMode v) {
224            this.loadingMode = v;
225            return this;
226        }
227
228        public Builder strict(boolean v) {
229            this.strict = v;
230            return this;
231        }
232
233        public Builder normalizeOnLoad(boolean v) {
234            this.normalizeOnLoad = v;
235            return this;
236        }
237
238        public Builder ttlMillis(long v) {
239            this.ttlMillis = v;
240            return this;
241        }
242
243        public Builder maxEagerObjects(int v) {
244            this.maxEagerObjects = v;
245            return this;
246        }
247
248        public Builder maxFetchesPerAnalysis(int v) {
249            this.maxFetchesPerAnalysis = v;
250            return this;
251        }
252
253        public Builder includeColumns(boolean v) {
254            this.includeColumns = v;
255            return this;
256        }
257
258        public Builder includeViews(boolean v) {
259            this.includeViews = v;
260            return this;
261        }
262
263        public Builder includeRoutines(boolean v) {
264            this.includeRoutines = v;
265            return this;
266        }
267
268        public Builder identifierConfig(IdentifierConfig v) {
269            this.identifierConfig = v;
270            return this;
271        }
272
273        public Builder diagnosticSink(CatalogDiagnosticSink v) {
274            this.diagnosticSink = v;
275            return this;
276        }
277
278        public Builder providerProperty(String key, Object value) {
279            if (key == null) {
280                throw new IllegalArgumentException("providerProperty key may not be null");
281            }
282            this.providerProperties.put(key, value);
283            return this;
284        }
285
286        public CatalogLoadOptions build() {
287            return new CatalogLoadOptions(this);
288        }
289    }
290}