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}