public final class CatalogIdentifierPolicy extends Object
IdentifierService that keeps the catalog/** dependency
direction one-way: the package depends on IdentifierService and
IdentifierConfig but on no other sqlenv type.
Plan §9.2 / §9.5. Every case-folding, normalization, qualified-name comparison, and
map key inside catalog/** routes through this class (or directly through
IdentifierService); the forbidden-apis Maven plugin enforces this mechanically.
IdentifierConfig fields that map to IdentifierProfile.VendorFlags
(currently mysqlLowerCaseTableNames and mssqlCollation) are honored by
constructing a per-call IdentifierService when they differ from defaults.
Round 1 scope cuts (tracked for P1B Round 2):
foldUnquotedToUpper / foldUnquotedToLower on
IdentifierConfig are recorded but not yet projected onto
IdentifierProfile — the per-call profile is still constructed via
IdentifierProfile.forVendor(vendor, flags) which uses vendor-default fold
rules. Callers who declare a non-default fold get vendor-default behavior.tableCaseSensitive / columnCaseSensitive (BigQuery split policy)
are accepted but not honored: extending IdentifierProfile with per-object-
group sensitivity flags is part of Round 2.areEqual(gudusoft.gsqlparser.catalog.runtime.CatalogQualifiedName, gudusoft.gsqlparser.catalog.runtime.CatalogQualifiedName) Tier-2 fallback for COLLATION_BASED dialects routes through
IdentifierService.areEqualStatic(gudusoft.gsqlparser.EDbVendor, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType, java.lang.String, java.lang.String) (cached vendor defaults) and therefore
does not honor a custom mssqlCollation or MySQL
lower_case_table_names=2; Tier 1 (cfg-aware normalized comparison)
handles the lctn=1 / lctn=2 case-insensitive cases since their normalize folds
to a single canonical form.keyForMap(gudusoft.gsqlparser.catalog.runtime.CatalogQualifiedName) can produce different keys for two names that
areEqual(gudusoft.gsqlparser.catalog.runtime.CatalogQualifiedName, gudusoft.gsqlparser.catalog.runtime.CatalogQualifiedName) considers equal in COLLATION_BASED dialects — same contract as
IdentifierService.canUseCompositeKey. Snapshot lookups must fall back to
areEqual on a key miss.The defaults-driven path (the primary use of this class in P1B/P1C) is fully covered by tests and is the one used by every Phase 1 reader and provider.
| Modifier and Type | Method and Description |
|---|---|
static boolean |
areEqual(CatalogQualifiedName a,
CatalogQualifiedName b)
Vendor-aware equality test for two qualified names.
|
static CatalogQualifiedName |
fromAlreadyNormalizedSegments(List<String> segments,
CatalogObjectKind kind,
EDbVendor vendor)
Build a
CatalogQualifiedName from segments that have already been normalized
by IdentifierService. |
static IdentifierService |
identifierServiceFor(IdentifierConfig cfg,
EDbVendor vendor)
Public façade over
serviceFor(IdentifierConfig, EDbVendor) for callers
outside this package (validator, readers, providers) that need an
IdentifierService matching a IdentifierConfig. |
static String |
keyForMap(CatalogQualifiedName name)
Map-friendly stable key for a
CatalogQualifiedName. |
static String |
normalize(String raw,
IdentifierConfig cfg)
Normalize a single identifier segment per
IdentifierConfig's vendor. |
static String |
normalizeSegment(String raw,
boolean wasQuoted,
IdentifierConfig cfg)
Normalize a segment with explicit quoted-ness (advisory).
|
static CatalogQualifiedName |
parse(String raw,
CatalogObjectKind kind,
IdentifierConfig cfg,
EDbVendor vendor)
Parse a possibly-quoted, possibly-multi-segment qualified name into a
CatalogQualifiedName with both raw and normalized segments tracked. |
public static CatalogQualifiedName parse(String raw, CatalogObjectKind kind, IdentifierConfig cfg, EDbVendor vendor)
CatalogQualifiedName with both raw and normalized segments tracked.
Each segment is normalized using the ESQLDataObjectType appropriate for its
position in the qualified name (catalog → schema → table → column), so vendor-specific
dual policies like BigQuery's case-sensitive tables vs case-insensitive columns are
applied per-segment rather than en bloc.
public static CatalogQualifiedName fromAlreadyNormalizedSegments(List<String> segments, CatalogObjectKind kind, EDbVendor vendor)
CatalogQualifiedName from segments that have already been normalized
by IdentifierService. Used by the lazy TSQLEnv bridge, where
TSQLEnv.doSearchSchemaObject hands over already-normalized catalog/schema/
object segments and rejoining + re-parsing would mis-handle quoted-embedded-dot
names like Oracle "a.b"."c".
Each segment is taken verbatim — no further normalization is applied — and the
resulting name carries identical raw and normalized segments. Quoted-flag bits are
empty by default; callers that have lost quoting information at this layer
(the legacy bridge path) accept the limitation that quoted-vs-unquoted distinction
is no longer recoverable. Snapshot lookups on this name still match because the
snapshot keys also derive from the same IdentifierService normalization.
public static String keyForMap(CatalogQualifiedName name)
CatalogQualifiedName.
Length-prefixes each normalized segment so quoted identifiers that contain dots —
e.g., Oracle "a.b"."c" vs "a"."b.c" — produce distinct keys despite
having the same flat join.
COLLATION_BASED limitation: for SQL Server / Azure SQL under a
case-insensitive collation, the legacy IdentifierService.normalize preserves
case (the legacy code-path uses Collator-bucketed lookup for those dialects).
Consequently keyForMap("dbo.Orders") and keyForMap("dbo.orders") can
differ even though areEqual(gudusoft.gsqlparser.catalog.runtime.CatalogQualifiedName, gudusoft.gsqlparser.catalog.runtime.CatalogQualifiedName) considers them equal. Snapshot/index lookups
that key by keyForMap on those dialects must therefore fall back to
areEqual(gudusoft.gsqlparser.catalog.runtime.CatalogQualifiedName, gudusoft.gsqlparser.catalog.runtime.CatalogQualifiedName) on a key miss; this is the same contract that
IdentifierService.canUseCompositeKey() exposes for the legacy code-path.
P1B/P1C may add a Collator-aware key bucket; tracked as a separate task.
public static boolean areEqual(CatalogQualifiedName a, CatalogQualifiedName b)
ESQLDataObjectType that matches each segment's role (catalog/schema/table/
column) so dual-policy dialects compare correctly.
Comparison is two-tier: first the cfg-aware normalized segments are
compared directly (which handles every dialect whose normalize captures
equality, including non-default IdentifierConfigs like MySQL
lower_case_table_names=1/2); on a mismatch we fall through to
IdentifierService.areEqualStatic(gudusoft.gsqlparser.EDbVendor, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType, java.lang.String, java.lang.String) so Collator-based dialects (SQL Server / Azure
SQL with case-insensitive collations) still report equality correctly.
public static String normalize(String raw, IdentifierConfig cfg)
IdentifierConfig's vendor.public static String normalizeSegment(String raw, boolean wasQuoted, IdentifierConfig cfg)
public static IdentifierService identifierServiceFor(IdentifierConfig cfg, EDbVendor vendor)
serviceFor(IdentifierConfig, EDbVendor) for callers
outside this package (validator, readers, providers) that need an
IdentifierService matching a IdentifierConfig. Vendor defaults
to cfg.vendor() when vendor is null.