001package gudusoft.gsqlparser.catalog.runtime;
002
003import java.util.Collections;
004import java.util.EnumSet;
005import java.util.Set;
006
007/**
008 * What is known about a child object's PARENT kind before resolution (stage S0R of
009 * docs/refactor/identifier_identity_display_separation_plan.md).
010 *
011 * <h3>Why a constraint and not a kind</h3>
012 * A resolved identity always knows its parent's exact kind. A LOOKUP often does not: parsing
013 * {@code schema.relation.column} establishes that the parent is a relation, but not whether the
014 * object it resolves to will be a table, a view or a materialized view. An API that demanded the
015 * exact kind would force the caller to invent {@code TABLE} to get past it — which is the
016 * arity-style guess in a new costume, and it would then reject the perfectly correct answer when the
017 * name turned out to be a view.
018 *
019 * <p>So a lookup carries a family; only {@link CatalogObjectKey} carries an exact kind.
020 */
021public final class CatalogParentConstraint {
022
023    /** Relations own columns. */
024    public static final CatalogParentConstraint RELATION = new CatalogParentConstraint(
025            "RELATION", EnumSet.of(CatalogObjectKind.TABLE, CatalogObjectKind.VIEW,
026            CatalogObjectKind.MATERIALIZED_VIEW));
027
028    /** Containers that own routines. */
029    public static final CatalogParentConstraint ROUTINE_CONTAINER = new CatalogParentConstraint(
030            "ROUTINE_CONTAINER", EnumSet.of(CatalogObjectKind.PACKAGE));
031
032    private final String name;
033    private final Set<CatalogObjectKind> allowed;
034
035    private CatalogParentConstraint(String name, Set<CatalogObjectKind> allowed) {
036        this.name = name;
037        this.allowed = Collections.unmodifiableSet(allowed);
038    }
039
040    /** Exactly one kind — for a caller whose metadata genuinely states it. */
041    public static CatalogParentConstraint exactly(CatalogObjectKind kind) {
042        if (kind == null) {
043            throw new IllegalArgumentException("an exact parent constraint needs a kind");
044        }
045        return new CatalogParentConstraint("EXACT(" + kind + ")", EnumSet.of(kind));
046    }
047
048    public boolean admits(CatalogObjectKind kind) {
049        return kind != null && allowed.contains(kind);
050    }
051
052    /** The single kind, when this constraint names exactly one; null for a family. */
053    public CatalogObjectKind exactKind() {
054        return allowed.size() == 1 ? allowed.iterator().next() : null;
055    }
056
057    public Set<CatalogObjectKind> allowedKinds() {
058        return allowed;
059    }
060
061    @Override
062    public boolean equals(Object o) {
063        if (this == o) {
064            return true;
065        }
066        if (o == null || getClass() != o.getClass()) {
067            return false;
068        }
069        return allowed.equals(((CatalogParentConstraint) o).allowed);
070    }
071
072    @Override
073    public int hashCode() {
074        return allowed.hashCode();
075    }
076
077    @Override
078    public String toString() {
079        return name;
080    }
081}