001package gudusoft.gsqlparser.ir.semantic.validation.oracle;
002
003import gudusoft.gsqlparser.ir.semantic.Diagnostic;
004import gudusoft.gsqlparser.ir.semantic.RecoveryMetadata;
005import gudusoft.gsqlparser.nodes.TExpression;
006
007import java.util.ArrayDeque;
008import java.util.ArrayList;
009import java.util.Collections;
010import java.util.Deque;
011import java.util.IdentityHashMap;
012import java.util.List;
013import java.util.Map;
014
015/** Immutable result of validating one Oracle query block's legacy (+) facts. */
016public final class OracleLegacyOuterJoinValidationResult {
017
018    private static final OracleLegacyOuterJoinValidationResult EMPTY =
019            new OracleLegacyOuterJoinValidationResult(
020                    Collections.<Diagnostic>emptyList(),
021                    Collections.<RecoveryMetadata.Entry>emptyList(),
022                    new IdentityHashMap<TExpression, PredicateJoinIntent>());
023
024    private final List<Diagnostic> diagnostics;
025    private final List<RecoveryMetadata.Entry> recoveryEntries;
026    private final IdentityHashMap<TExpression, PredicateJoinIntent> intents;
027
028    OracleLegacyOuterJoinValidationResult(
029            List<Diagnostic> diagnostics,
030            List<RecoveryMetadata.Entry> recoveryEntries,
031            Map<TExpression, PredicateJoinIntent> intents) {
032        this.diagnostics = Collections.unmodifiableList(
033                new ArrayList<Diagnostic>(diagnostics));
034        this.recoveryEntries = Collections.unmodifiableList(
035                new ArrayList<RecoveryMetadata.Entry>(recoveryEntries));
036        this.intents = new IdentityHashMap<TExpression, PredicateJoinIntent>();
037        this.intents.putAll(intents);
038    }
039
040    static OracleLegacyOuterJoinValidationResult empty() {
041        return EMPTY;
042    }
043
044    public List<Diagnostic> getDiagnostics() {
045        return diagnostics;
046    }
047
048    public List<RecoveryMetadata.Entry> getRecoveryEntries() {
049        return recoveryEntries;
050    }
051
052    public boolean hasErrors() {
053        return !diagnostics.isEmpty();
054    }
055
056    /**
057     * Return the intent for a promotion conjunct. Parenthesis wrappers are
058     * handled without re-running validation; all decisions come from facts
059     * indexed during the Sema pass.
060     */
061    public PredicateJoinIntent intentForConjunct(TExpression conjunct) {
062        if (conjunct == null) return null;
063        PredicateJoinIntent exact = intents.get(conjunct);
064        if (exact != null) return exact;
065
066        Deque<TExpression> work = new ArrayDeque<TExpression>();
067        work.push(conjunct);
068        PredicateJoinIntent found = null;
069        while (!work.isEmpty()) {
070            TExpression current = work.pop();
071            PredicateJoinIntent candidate = intents.get(current);
072            if (candidate != null) {
073                if (candidate.getValidity() == PredicateJoinIntent.Validity.INVALID) {
074                    return candidate;
075                }
076                if (found != null && found != candidate) {
077                    return PredicateJoinIntent.unknown(
078                            candidate.getReferencedRelationInstanceIds());
079                }
080                found = candidate;
081            }
082            pushExpressionChildren(current, work);
083        }
084        return found;
085    }
086
087    private static void pushExpressionChildren(TExpression expression,
088                                               Deque<TExpression> work) {
089        if (expression.getSubQuery() != null) return;
090        if (expression.getExprList() != null) {
091            for (int i = expression.getExprList().size() - 1; i >= 0; i--) {
092                TExpression child = expression.getExprList().getExpression(i);
093                if (child != null) work.push(child);
094            }
095        }
096        if (expression.getBetweenOperand() != null) {
097            work.push(expression.getBetweenOperand());
098        }
099        if (expression.getRightOperand() != null) {
100            work.push(expression.getRightOperand());
101        }
102        if (expression.getLeftOperand() != null) {
103            work.push(expression.getLeftOperand());
104        }
105    }
106}