Class IdentifierService
提供所有标识符规范化和比较的统一入口。
为什么需要它(面向入门者):
不同数据库对名称的大小写和引号行为差别很大(是否区分大小写、是否折叠为大/小写、是否受 collation 影响)。
如果在业务代码里到处手写 toLowerCase/equalsIgnoreCase,很容易出错且难以维护。
本服务将“如何折叠与如何比较”的规则集中在一个地方,确保全局一致、可扩展、易测试。
与其它类的关系:
IdentifierRules:一张“规则卡片”(策略),描述未引号/带引号的折叠(fold)与比较(compare)。IdentifierProfile:一个“厂商档案”,为某个数据库厂商打包不同对象组(表/列/函数)的规则,并携带厂商开关(如 MySQL lower_case_table_names、SQL Server collation)。- IdentifierService:基于 Profile 执行规范化与比较,向外提供统一的
normalize/areEqual/keyForMap接口(门面)。
设计理念与收益:
- 一致性:所有地方都通过同一入口生成 Map 键与做比较,避免行为分裂。
- 可扩展:新增厂商仅需补一份规则工厂;旧调用不变。
- 可测试:规则与服务可单测覆盖,验证各厂商/对象类型是否符合预期。
- 性能友好:
keyForMap(java.lang.String, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType)先做规范化,Map 查找 O(1);SQL Server 经CollatorProvider做 collation 比较。
使用了哪些设计模式:
- 策略(Strategy):
IdentifierRules即为可替换的规则策略。 - 工厂(Factory):
IdentifierRules.forOracle()/forPostgreSQL()、IdentifierProfile.forVendor(...)产出预设策略组合。 - 门面(Facade):本类用少量方法对外隐藏折叠/比较/引号/Collator 细节。
- 依赖注入(DI):构造时注入
IdentifierProfile与可选CollatorProvider,便于替换与测试。 - 不可变值对象(Immutable):规则/flags 不可变,线程安全、可缓存。
- Provider:
CollatorProvider解耦 SQL Server 的 collation 依赖。
关键约束(必须遵守):
- 所有索引键构造必须通过
keyForMap(java.lang.String, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType) - 所有标识符比较必须通过
areEqual(java.lang.String, java.lang.String, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType) - 禁止在业务代码中直接调用
String.toUpperCase/toLowerCase/equalsIgnoreCase
使用示例:
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 Summary
ConstructorsConstructorDescriptionIdentifierService(IdentifierProfile profile, CollatorProvider collatorProvider) 构造标识符服务 -
Method Summary
Modifier and TypeMethodDescriptionbooleanareEqual(String ident1, String ident2, ESQLDataObjectType objectType) 比较两个标识符是否相等static booleanareEqualStatic(EDbVendor dbVendor, ESQLDataObjectType objectType, String ident1, String ident2) High-performance static identifier comparison method with cachingvoidassertSingleSegmentOrThrow(String identifier, ESQLDataObjectType objectType) 断言标识符为单段,否则抛出异常buildCompositeKey(String qualifiedName, ESQLDataObjectType objectType) 构造复合键(从完整限定名)buildCompositeKey(List<String> segments, ESQLDataObjectType objectType) 构造复合键(使用长度前缀编码避免冲突)booleancanUseCompositeKey(String qualifiedName) 判断是否可以使用复合键快速路径expandVendorSpecific(List<String> segments, EDbVendor vendor) 厂商级预处理(展开特殊语法)expandVendorSpecific(List<String> segments, EDbVendor vendor, String defaultSchema) 厂商级预处理(展开特殊语法)获取 Collator 提供者获取标识符配置档案keyForMap(String identifier, ESQLDataObjectType objectType) 为 Map 索引构造键(单个标识符段)keysForHierarchy(String qualifiedName, List<ESQLDataObjectType> partTypes) 生成层级索引所需的段级键列表normalize(String identifier, ESQLDataObjectType objectType) 规范化标识符(去引号 + 大小写折叠)normalizeQualifiedName(String qualifiedName, ESQLDataObjectType objectType) 规范化完整限定名(等价于 SQLUtil.getIdentifierNormalName)normalizeSegment(String segment, ESQLDataObjectType objectType) 规范化单个段(去引号 + 大小写折叠)static StringnormalizeStatic(EDbVendor dbVendor, ESQLDataObjectType objectType, String identifier) High-performance static normalize method with cachingparseQualifiedName(String qualifiedName) 解析完整限定名为段列表
-
Constructor Details
-
IdentifierService
构造标识符服务- 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 cachingThis 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 vendorobjectType- 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 cachingThis 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 vendorobjectType- 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
规范化标识符(去引号 + 大小写折叠)用于构造索引键,确保相同语义的标识符生成相同的键。
- Parameters:
identifier- 原始标识符(可能带引号)objectType- 对象类型- Returns:
- 规范化后的标识符
-
areEqual
比较两个标识符是否相等根据数据库厂商的大小写规则进行比较。
- Parameters:
ident1- 标识符 1ident2- 标识符 2objectType- 对象类型- Returns:
- true 如果相等
-
keyForMap
为 Map 索引构造键(单个标识符段)注意:此方法仅用于分层索引的单段键,不用于复合键。
对于 COLLATION_BASED(SQL Server),不做 fold,返回原始标识符 (后续通过桶+Collator 比较)。
- Parameters:
identifier- 标识符objectType- 对象类型- Returns:
- 索引键
-
canUseCompositeKey
判断是否可以使用复合键快速路径条件(必须全部满足):
- 所有对象组都是 SENSITIVE
- 输入没有引号字符
- 没有 COLLATION_BASED 类型
- Parameters:
qualifiedName- 完整限定名(如 "db.schema.table")- Returns:
- true 如果可以使用复合键
-
buildCompositeKey
构造复合键(使用长度前缀编码避免冲突)格式:
len1#segment1|len2#segment2|len3#segment3|objectType例如:
"3#db1|6#schema|5#table|dotTable"优势:避免分隔符冲突(标识符可能包含 '.' 或 '|')
- Parameters:
segments- 标识符段列表objectType- 对象类型- Returns:
- 复合键
-
buildCompositeKey
构造复合键(从完整限定名)- Parameters:
qualifiedName- 完整限定名(如 "db.schema.table")objectType- 对象类型- Returns:
- 复合键
-
parseQualifiedName
解析完整限定名为段列表包装
SQLUtil.parseNames(String, EDbVendor)以支持厂商特定解析:- MSSQL: 保留 ".." 以便后续展开
- BigQuery: 反引号内的点号仍视为层级分隔
- 其它: 按 '.' 分段,处理引号包裹的段
- Parameters:
qualifiedName- 完整限定名(如 "db.schema.table" 或 "db..table")- Returns:
- 段列表
-
expandVendorSpecific
厂商级预处理(展开特殊语法)处理厂商特定语法:
- 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
规范化单个段(去引号 + 大小写折叠)与
normalize(String, ESQLDataObjectType)类似,但语义明确为"单段"处理。- Parameters:
segment- 单个段标识符objectType- 对象类型- Returns:
- 规范化后的段
-
normalizeQualifiedName
规范化完整限定名(等价于 SQLUtil.getIdentifierNormalName)处理流程:
- 解析为段列表:
parseQualifiedName(String) - 展开厂商特定语法:
expandVendorSpecific(List, EDbVendor) - 根据 supportCatalog/supportSchema 决定各段类型
- 逐段规范化:
normalizeSegment(String, ESQLDataObjectType) - 重新拼接为 "catalog.schema.table.column" 格式
- Parameters:
qualifiedName- 完整限定名objectType- 最终对象类型(dotTable/dotColumn/dotSchema 等)- Returns:
- 规范化后的完整限定名
- 解析为段列表:
-
keysForHierarchy
生成层级索引所需的段级键列表用于分层索引(hierarchical index),确保各层键由
keyForMap(java.lang.String, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType)统一生成。注意:调用前应先调用
parseQualifiedName(String)和expandVendorSpecific(List, EDbVendor)完成解析与展开。- Parameters:
qualifiedName- 完整限定名partTypes- 各段对应的对象类型列表(长度应与段数一致)- Returns:
- 段级键列表
- Throws:
IllegalArgumentException- 如果段数与类型数不匹配
-
assertSingleSegmentOrThrow
断言标识符为单段,否则抛出异常用于在
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)
-