001package gudusoft.gsqlparser.ir.semantic.builder;
002
003import gudusoft.gsqlparser.ir.semantic.Diagnostic;
004import gudusoft.gsqlparser.ir.semantic.StatementGraph;
005
006/**
007 * Caller-supplied knobs for a {@code SemanticIRBuilder.build*} invocation.
008 *
009 * <p>Immutable; instances are created from {@link #defaults()} and refined
010 * with the {@code with*} copiers. The no-options {@code build*} overloads
011 * behave exactly as {@link #defaults()}, so existing callers are unaffected.
012 *
013 * <h3>Degrade mode</h3>
014 *
015 * <p>By default the builder is all-or-nothing: a construct it does not
016 * support anywhere in the statement aborts the whole
017 * {@code build*} call with a
018 * {@code SemanticIRBuilder.SemanticIRBuildException}, and the caller loses
019 * the analysis of the blocks that <em>were</em> fully understood. With
020 * {@link #withDegradeUnsupportedNestedBlocks(boolean) degrade mode} enabled,
021 * an unsupported <em>nested</em> block is instead reported as a
022 * {@link Diagnostic} and the rest of the statement is still returned.
023 *
024 * <p>Degrade mode is deliberately narrow. It applies only to blocks the
025 * surrounding statement does not depend on for its own shape — today, the
026 * lifted body of a predicate subquery ({@code EXISTS (...)},
027 * {@code IN (SELECT ...)}, a comparison / {@code ANY} / {@code ALL} against a
028 * subquery). Those bodies are emitted as their own
029 * {@link StatementGraph} and are unreachable from the host block: no relation
030 * points at them and no lineage edge targets them, so dropping one cannot
031 * change a single fact the host block publishes.
032 *
033 * <p>It does <strong>not</strong> apply to a failure in the host block
034 * itself, nor to CTE bodies, FROM-clause subqueries or scalar-subquery
035 * projections — the host block's relations, output columns and lineage are
036 * derived from those, so skipping one would publish a host block that looks
037 * complete while silently missing sources. Those keep throwing.
038 *
039 * <p>A degraded block is never dropped silently. It is replaced, in the same
040 * position of {@code SemanticProgram.getStatements()}, by a placeholder
041 * whose {@link StatementGraph#getKind()} is
042 * {@value StatementGraph#KIND_UNANALYZED} and whose
043 * {@link StatementGraph#getUnanalyzedReason()} carries the original
044 * {@link Diagnostic} — code, message and, when the AST could be anchored,
045 * the {@link gudusoft.gsqlparser.ir.semantic.SourceSpan} of the offending
046 * block. The same diagnostic is returned atomically by the builder's
047 * {@code build*Result} methods and therefore appears in
048 * {@code AnalysisResult.getDiagnostics()} too. Legacy {@code build*} callers
049 * can still obtain the compatibility snapshot through
050 * {@code SemanticIRBuilder.drainBuildDiagnostics()}.
051 *
052 * <p><b>API status: advanced/preview.</b> Callers using these options own the
053 * low-level parsing, resolver, provider, diagnostics, and recovery contract.
054 * Prefer {@link gudusoft.gsqlparser.ir.semantic.SqlSemanticAnalyzer} for the
055 * supported SQL-text pipeline.
056 *
057 * @see StatementGraph#isUnanalyzed()
058 */
059public final class SemanticIRBuildOptions {
060
061    private static final SemanticIRBuildOptions DEFAULTS =
062            new SemanticIRBuildOptions(false);
063
064    private final boolean degradeUnsupportedNestedBlocks;
065
066    private SemanticIRBuildOptions(boolean degradeUnsupportedNestedBlocks) {
067        this.degradeUnsupportedNestedBlocks = degradeUnsupportedNestedBlocks;
068    }
069
070    /**
071     * @return the default options — degrade mode off, i.e. the historical
072     *         all-or-nothing behaviour of the two-argument
073     *         {@code build*} overloads.
074     */
075    public static SemanticIRBuildOptions defaults() {
076        return DEFAULTS;
077    }
078
079    /**
080     * @param degrade {@code true} to report an unsupported nested predicate
081     *                subquery body as a diagnostic plus an
082     *                {@value StatementGraph#KIND_UNANALYZED} placeholder
083     *                instead of aborting the build
084     * @return a copy of these options with the flag set
085     */
086    public SemanticIRBuildOptions withDegradeUnsupportedNestedBlocks(boolean degrade) {
087        if (degrade == this.degradeUnsupportedNestedBlocks) {
088            return this;
089        }
090        return new SemanticIRBuildOptions(degrade);
091    }
092
093    public boolean isDegradeUnsupportedNestedBlocks() {
094        return degradeUnsupportedNestedBlocks;
095    }
096
097    @Override
098    public boolean equals(Object o) {
099        if (this == o) return true;
100        if (!(o instanceof SemanticIRBuildOptions)) return false;
101        return degradeUnsupportedNestedBlocks
102                == ((SemanticIRBuildOptions) o).degradeUnsupportedNestedBlocks;
103    }
104
105    @Override
106    public int hashCode() {
107        return Boolean.valueOf(degradeUnsupportedNestedBlocks).hashCode();
108    }
109
110    @Override
111    public String toString() {
112        return "SemanticIRBuildOptions{degradeUnsupportedNestedBlocks="
113                + degradeUnsupportedNestedBlocks + "}";
114    }
115}