Class IdentifierService

Object
gudusoft.gsqlparser.sqlenv.IdentifierService

public class IdentifierService extends Object
标识符服务(Identifier Service)

提供所有标识符规范化和比较的统一入口。

为什么需要它(面向入门者):
不同数据库对名称的大小写和引号行为差别很大(是否区分大小写、是否折叠为大/小写、是否受 collation 影响)。 如果在业务代码里到处手写 toLowerCase/equalsIgnoreCase,很容易出错且难以维护。 本服务将“如何折叠与如何比较”的规则集中在一个地方,确保全局一致、可扩展、易测试。

与其它类的关系:

  • IdentifierRules:一张“规则卡片”(策略),描述未引号/带引号的折叠(fold)与比较(compare)。
  • IdentifierProfile:一个“厂商档案”,为某个数据库厂商打包不同对象组(表/列/函数)的规则,并携带厂商开关(如 MySQL lower_case_table_names、SQL Server collation)。
  • IdentifierService:基于 Profile 执行规范化与比较,向外提供统一的 normalize/areEqual/keyForMap 接口(门面)。

设计理念与收益:

使用了哪些设计模式:

  • 策略(Strategy):IdentifierRules 即为可替换的规则策略。
  • 工厂(Factory):IdentifierRules.forOracle()/forPostgreSQL()IdentifierProfile.forVendor(...) 产出预设策略组合。
  • 门面(Facade):本类用少量方法对外隐藏折叠/比较/引号/Collator 细节。
  • 依赖注入(DI):构造时注入 IdentifierProfile 与可选 CollatorProvider,便于替换与测试。
  • 不可变值对象(Immutable):规则/flags 不可变,线程安全、可缓存。
  • Provider:CollatorProvider 解耦 SQL Server 的 collation 依赖。

关键约束(必须遵守):

使用示例:

 IdentifierService service = new IdentifierService(profile, collatorProvider);

 // 规范化标识符(用于索引键)
 String key = service.normalize("MyTable", ESQLDataObjectType.dotTable);
 // Oracle: "MYTABLE", PostgreSQL: "mytable", ClickHouse: "MyTable"

 // 比较两个标识符
 boolean eq = service.areEqual("MyTable", "MYTABLE", ESQLDataObjectType.dotTable);
 // Oracle: true, ClickHouse: false

 // 构造索引键
 String mapKey = service.keyForMap("MyTable", ESQLDataObjectType.dotTable);
 
Since:
3.1.0.9
  • Constructor Details

    • IdentifierService

      public IdentifierService(IdentifierProfile profile, CollatorProvider collatorProvider)
      构造标识符服务
      Parameters:
      profile - 厂商标识符配置档案
      collatorProvider - Collator 提供者(SQL Server 专用,可为 null)
  • Method Details

    • normalizeStatic

      public static String normalizeStatic(EDbVendor dbVendor, ESQLDataObjectType objectType, String identifier)
      High-performance static normalize method with caching

      This method provides a convenient static interface for identifier normalization while leveraging a pre-populated cache of IdentifierService instances.

      Performance characteristics:

      • O(1) cache lookup using EnumMap
      • No object creation - services are pre-created and reused
      • Thread-safe - all cached instances are immutable
      • Handles all vendor-specific rules (collation, per-object-type rules, etc.)

      Usage example:

       String normalized = IdentifierService.normalizeStatic(
           EDbVendor.dbvoracle,
           ESQLDataObjectType.dotTable,
           "MyTable"
       );
       // Result: "MYTABLE"
       
      Parameters:
      dbVendor - database vendor
      objectType - object type (table, column, schema, etc.)
      identifier - identifier to normalize (may be quoted)
      Returns:
      normalized identifier (unquoted and case-folded)
    • areEqualStatic

      public static boolean areEqualStatic(EDbVendor dbVendor, ESQLDataObjectType objectType, String ident1, String ident2)
      High-performance static identifier comparison method with caching

      This method provides a convenient static interface for identifier comparison while leveraging a pre-populated cache of IdentifierService instances.

      Performance characteristics:

      • O(1) cache lookup using EnumMap
      • No object creation - services are pre-created and reused
      • Thread-safe - all cached instances are immutable
      • Handles all vendor-specific rules (case-sensitive, case-insensitive, collation-based)

      Usage example:

       boolean equal = IdentifierService.areEqualStatic(
           EDbVendor.dbvoracle,
           ESQLDataObjectType.dotTable,
           "MyTable",
           "MYTABLE"
       );
       // Result: true (Oracle is case-insensitive for unquoted identifiers)
       
      Parameters:
      dbVendor - database vendor
      objectType - object type (table, column, schema, etc.)
      ident1 - first identifier to compare (may be quoted)
      ident2 - second identifier to compare (may be quoted)
      Returns:
      true if identifiers are equal according to vendor rules
    • normalize

      public String normalize(String identifier, ESQLDataObjectType objectType)
      规范化标识符(去引号 + 大小写折叠)

      用于构造索引键,确保相同语义的标识符生成相同的键。

      Parameters:
      identifier - 原始标识符(可能带引号)
      objectType - 对象类型
      Returns:
      规范化后的标识符
    • areEqual

      public boolean areEqual(String ident1, String ident2, ESQLDataObjectType objectType)
      比较两个标识符是否相等

      根据数据库厂商的大小写规则进行比较。

      Parameters:
      ident1 - 标识符 1
      ident2 - 标识符 2
      objectType - 对象类型
      Returns:
      true 如果相等
    • keyForMap

      public String keyForMap(String identifier, ESQLDataObjectType objectType)
      为 Map 索引构造键(单个标识符段)

      注意:此方法仅用于分层索引的单段键,不用于复合键。

      对于 COLLATION_BASED(SQL Server),不做 fold,返回原始标识符 (后续通过桶+Collator 比较)。

      Parameters:
      identifier - 标识符
      objectType - 对象类型
      Returns:
      索引键
    • canUseCompositeKey

      public boolean canUseCompositeKey(String qualifiedName)
      判断是否可以使用复合键快速路径

      条件(必须全部满足):

      1. 所有对象组都是 SENSITIVE
      2. 输入没有引号字符
      3. 没有 COLLATION_BASED 类型
      Parameters:
      qualifiedName - 完整限定名(如 "db.schema.table")
      Returns:
      true 如果可以使用复合键
    • buildCompositeKey

      public String buildCompositeKey(List<String> segments, ESQLDataObjectType objectType)
      构造复合键(使用长度前缀编码避免冲突)

      格式: len1#segment1|len2#segment2|len3#segment3|objectType

      例如: "3#db1|6#schema|5#table|dotTable"

      优势:避免分隔符冲突(标识符可能包含 '.' 或 '|')

      Parameters:
      segments - 标识符段列表
      objectType - 对象类型
      Returns:
      复合键
    • buildCompositeKey

      public String buildCompositeKey(String qualifiedName, ESQLDataObjectType objectType)
      构造复合键(从完整限定名)
      Parameters:
      qualifiedName - 完整限定名(如 "db.schema.table")
      objectType - 对象类型
      Returns:
      复合键
    • parseQualifiedName

      public List<String> parseQualifiedName(String qualifiedName)
      解析完整限定名为段列表

      包装 SQLUtil.parseNames(String, EDbVendor) 以支持厂商特定解析:

      • MSSQL: 保留 ".." 以便后续展开
      • BigQuery: 反引号内的点号仍视为层级分隔
      • 其它: 按 '.' 分段,处理引号包裹的段
      Parameters:
      qualifiedName - 完整限定名(如 "db.schema.table" 或 "db..table")
      Returns:
      段列表
    • expandVendorSpecific

      public List<String> expandVendorSpecific(List<String> segments, EDbVendor vendor)
      厂商级预处理(展开特殊语法)

      处理厂商特定语法:

      • MSSQL/Azure SQL: 将 "db..table" 展开为 "db.<global or dbo>.table"
      Parameters:
      segments - 原始段列表
      vendor - 数据库厂商
      Returns:
      展开后的段列表
    • expandVendorSpecific

      public List<String> expandVendorSpecific(List<String> segments, EDbVendor vendor, String defaultSchema)
      厂商级预处理(展开特殊语法)

      处理厂商特定语法:

      • MSSQL/Azure SQL: 将 "db..table" 展开为 "db.<defaultSchema or dbo>.table"
      Parameters:
      segments - 原始段列表
      vendor - 数据库厂商
      defaultSchema - 默认 schema,如果为 null 则使用 "dbo"
      Returns:
      展开后的段列表
    • normalizeSegment

      public String normalizeSegment(String segment, ESQLDataObjectType objectType)
      规范化单个段(去引号 + 大小写折叠)

      normalize(String, ESQLDataObjectType) 类似,但语义明确为"单段"处理。

      Parameters:
      segment - 单个段标识符
      objectType - 对象类型
      Returns:
      规范化后的段
    • normalizeQualifiedName

      public String normalizeQualifiedName(String qualifiedName, ESQLDataObjectType objectType)
      规范化完整限定名(等价于 SQLUtil.getIdentifierNormalName)

      处理流程:

      1. 解析为段列表:parseQualifiedName(String)
      2. 展开厂商特定语法:expandVendorSpecific(List, EDbVendor)
      3. 根据 supportCatalog/supportSchema 决定各段类型
      4. 逐段规范化:normalizeSegment(String, ESQLDataObjectType)
      5. 重新拼接为 "catalog.schema.table.column" 格式
      Parameters:
      qualifiedName - 完整限定名
      objectType - 最终对象类型(dotTable/dotColumn/dotSchema 等)
      Returns:
      规范化后的完整限定名
    • keysForHierarchy

      public List<String> keysForHierarchy(String qualifiedName, List<ESQLDataObjectType> partTypes)
      生成层级索引所需的段级键列表

      用于分层索引(hierarchical index),确保各层键由 keyForMap(java.lang.String, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType) 统一生成。

      注意:调用前应先调用 parseQualifiedName(String)expandVendorSpecific(List, EDbVendor) 完成解析与展开。

      Parameters:
      qualifiedName - 完整限定名
      partTypes - 各段对应的对象类型列表(长度应与段数一致)
      Returns:
      段级键列表
      Throws:
      IllegalArgumentException - 如果段数与类型数不匹配
    • assertSingleSegmentOrThrow

      public void assertSingleSegmentOrThrow(String identifier, ESQLDataObjectType objectType)
      断言标识符为单段,否则抛出异常

      用于在 keyForMap(java.lang.String, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType) 入口处确保输入为单段标识符(不包含 '.' 分隔符)。

      如果 TBaseType.ALLOW_MULTI_SEGMENT_IN_KEY 为 true(兼容模式), 则仅记录警告日志而不抛异常。

      Parameters:
      identifier - 待检查的标识符
      Throws:
      IllegalArgumentException - 如果标识符包含多段且未启用兼容模式
    • getProfile

      获取标识符配置档案
      Returns:
      配置档案
    • getCollatorProvider

      获取 Collator 提供者
      Returns:
      Collator 提供者(可能为 null)