001package gudusoft.gsqlparser.catalog.runtime; 002 003import gudusoft.gsqlparser.sqlenv.IdentifierComponentRole; 004 005import java.util.ArrayList; 006import java.util.Collections; 007import java.util.List; 008 009/** 010 * Which container levels a namespace HAS — declared, never inferred (stage S0R of 011 * docs/refactor/identifier_identity_display_separation_plan.md). 012 * 013 * <h3>Why a resolved identity needs this</h3> 014 * Whether {@code db.t} is a complete identity depends on whether the namespace has a schema level. 015 * On MySQL it does not, so {@code db.t} names a table completely. On Oracle it does, so {@code db.t} 016 * is a name with an UNRESOLVED schema — and keying it as though it were complete makes it collide 017 * with the same table name in a different schema. The component list alone cannot tell those apart: 018 * both are "a catalog and an object". So the topology is declared, and validation checks the 019 * components against the declaration rather than guessing the declaration from the components. 020 * 021 * <p>{@link IdentifierComponentRole#SERVER} is deliberately not part of a topology. Every namespace 022 * can be reached through a link, and a name without a server means the local one — its absence is 023 * not a missing level. 024 */ 025public enum CatalogHierarchy { 026 027 /** Catalog and schema both exist: Oracle, SQL Server, PostgreSQL, Snowflake, most dialects. */ 028 CATALOG_SCHEMA(true, true), 029 030 /** A catalog (database) but no schema level: MySQL, Teradata, Hive, Impala. */ 031 CATALOG_ONLY(true, false), 032 033 /** A schema with no catalog above it — a rootless topology. */ 034 SCHEMA_ONLY(false, true), 035 036 /** Objects at the root, with no container level at all. */ 037 ROOT_ONLY(false, false); 038 039 private final boolean catalog; 040 private final boolean schema; 041 042 CatalogHierarchy(boolean catalog, boolean schema) { 043 this.catalog = catalog; 044 this.schema = schema; 045 } 046 047 public boolean hasCatalog() { 048 return catalog; 049 } 050 051 public boolean hasSchema() { 052 return schema; 053 } 054 055 /** The container roles this topology has, outermost first. Never includes SERVER. */ 056 public List<IdentifierComponentRole> containers() { 057 List<IdentifierComponentRole> roles = new ArrayList<IdentifierComponentRole>(2); 058 if (catalog) { 059 roles.add(IdentifierComponentRole.CATALOG); 060 } 061 if (schema) { 062 roles.add(IdentifierComponentRole.SCHEMA); 063 } 064 return Collections.unmodifiableList(roles); 065 } 066 067 /** 068 * Whether this topology has the given container level. 069 * 070 * <p>Only CATALOG and SCHEMA are topology questions. {@link IdentifierComponentRole#SERVER} is 071 * refused rather than answered true: a server is an optional endpoint qualifier, not a level a 072 * topology can lack, and answering "yes, always" contradicted this class's own definition. 073 */ 074 public boolean has(IdentifierComponentRole role) { 075 if (role == IdentifierComponentRole.SERVER) { 076 throw new IllegalArgumentException("SERVER is not a topology level: a name without one " 077 + "means the local endpoint, so its absence is not a missing level"); 078 } 079 if (role == IdentifierComponentRole.CATALOG) { 080 return catalog; 081 } 082 if (role == IdentifierComponentRole.SCHEMA) { 083 return schema; 084 } 085 return false; 086 } 087 088}