public final class QualifiedNameHierarchy extends Object
ESQLDataObjectType to the segments of a qualified SQL name
(stage S0 of docs/refactor/identifier_identity_display_separation_plan.md).
A qualified name does not have one object type — db.s.t.c is a catalog, a
schema, a table and a column, and each segment is compared under the rules of its own
kind. Which position means what depends on the vendor's hierarchy depth, the kind of
object the name as a whole denotes, and how many segments were written. This class is
the single place that decision is expressed.
It is deliberately a pure function of (vendor, objectType, segments).
It reads no session state, no ThreadLocal and no catalog, so the same name always
plans the same way.
Relation to SQLUtil#compareIdentifier. That method carries the
same assignment inline, as nested conditionals around pairwise comparisons. This class
exists so an identity key can be built from the assignment
(QualifiedCanonKey). It is NOT a copy: it keeps segment boundaries that method
joins away, and it parses vendor-aware where that method does not, so the two diverge on
an enumerated set of shapes where this class is correct and that method is not.
QualifiedCanonKeyEquivalenceProofTest walks every vendor, every mapped object type
and every ordered pair of shapes, and every divergence is pinned there with its exact
expected values. Stage S2 must make compareIdentifier delegate here, at which point
those divergences become fixes; S0 changes no production call site.
db.s.t.c.sub.sub2 plans as
catalog db, schema s, table t, then three column-typed levels
c, sub, sub2.
This deliberately differs from SQLUtil#compareIdentifier, which joins the
surplus back into one dotted string and compares it as a single name. That join loses the
distinction between a path and a name that contains a dot, and an identity key built on it
merges two different objects:
Oracle, dotColumn: c.d.s.a.f vs c.d.s."A.F" 5 segments [c,d,s,a,f] vs 4 segments [c,d,s,"A.F"] joined leaf text: A.F vs A.F <- collideThe second name is one column whose own text contains a dot — legal on Oracle (
CREATE TABLE t ("A.F" NUMBER)) — and it is not the field f of column
a. Case folding changes letters, never where the boundaries are, so the two can
only be told apart by keeping the segmentation. compareIdentifier happens to
answer false for this pair, but only because it compares raw segment counts first
and never reaches its own join; the join does decide pairs of equal arity, and there this
class and that method diverge. Every such divergence is enumerated with exact expected
values in QualifiedCanonKeyEquivalenceProofTest.| Modifier and Type | Class and Description |
|---|---|
static class |
QualifiedNameHierarchy.Segment
One planned segment: its text exactly as written, plus the kind it is compared as.
|
| Modifier and Type | Method and Description |
|---|---|
static boolean |
hasHierarchyMapping(EDbVendor vendor,
ESQLDataObjectType objectType)
Whether this (vendor, objectType) pair has a hierarchy mapping at all.
|
static List<QualifiedNameHierarchy.Segment> |
plan(EDbVendor vendor,
ESQLDataObjectType objectType,
List<String> segments)
Plans the segment types for a qualified name.
|
static List<String> |
split(EDbVendor vendor,
String qualifiedName)
Splits
qualifiedName into segments the way the identity path must see it. |
public static List<String> split(EDbVendor vendor, String qualifiedName)
qualifiedName into segments the way the identity path must see it.
Vendor-aware: BigQuery's `project.dataset.table` becomes three segments.
SQLUtil#compareIdentifier calls the vendor-blind overload and therefore sees
that shape as ONE segment — a defect of that method, recorded as a pinned divergence
in the S0 proof test and due for repair in S2. Do not "fix" the divergence by making
this method vendor-blind.
public static List<QualifiedNameHierarchy.Segment> plan(EDbVendor vendor, ESQLDataObjectType objectType, List<String> segments)
vendor - the dialect whose hierarchy depth appliesobjectType - what the name as a whole denotessegments - the already-split segments, in written orderQualifiedNameHierarchy.Segment per input segment — hierarchy types while they
last, then the innermost type for each surplus segment, with no text joined;
null when this (vendor, objectType) pair has no hierarchy mapping —
see hasHierarchyMapping(gudusoft.gsqlparser.EDbVendor, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType)public static boolean hasHierarchyMapping(EDbVendor vendor, ESQLDataObjectType objectType)
dotUnknown, dotRoutine, dotDblink, dotDataType and
dotParameter have none. Note what SQLUtil#compareIdentifier does for
those: it falls through every branch and returns false — so it reports two
identical qualified names as different. An identity key cannot model a
non-reflexive relation, so QualifiedCanonKey refuses to build one instead of
reproducing that. The refusal is the honest answer; the false is a defect of
compareIdentifier, tracked for S2.