001package gudusoft.gsqlparser.sqlenv; 002 003import gudusoft.gsqlparser.EDbVendor; 004 005import java.util.EnumMap; 006import java.util.Objects; 007 008/** 009 * 厂商标识符配置档案(Vendor Identifier Profile) 010 * 011 * <p>封装每个数据库厂商的完整标识符配置,包括: 012 * <ul> 013 * <li>按对象组(NAME_GROUP, COLUMN_GROUP, ROUTINE_GROUP)的标识符规则 014 * <li>Vendor-specific flags(如 MySQL lower_case_table_names, SQL Server collation) 015 * </li> 016 * 017 * <p>设计目标: 018 * <ul> 019 * <li>集中管理所有 vendor-specific 配置 020 * <li>统一注入路径,避免配置散落在各处 021 * <li>支持缓存失效(通过 fingerprint) 022 * </ul> 023 * 024 * <p>使用示例: 025 * <pre> 026 * // 创建 Oracle profile(使用默认 flags) 027 * IdentifierProfile oracleProfile = IdentifierProfile.forVendor( 028 * EDbVendor.dbvoracle, 029 * VendorFlags.defaults() 030 * ); 031 * 032 * // 创建 MySQL profile(指定 lower_case_table_names) 033 * IdentifierProfile mysqlProfile = IdentifierProfile.forVendor( 034 * EDbVendor.dbvmysql, 035 * new VendorFlags(1, null, false, false) // lower_case_table_names = 1 036 * ); 037 * 038 * // 查询规则 039 * IdentifierRules tableRules = oracleProfile.getRules(ESQLDataObjectType.dotTable); 040 * </pre> 041 * 042 * @since 3.1.0.9 043 */ 044public final class IdentifierProfile { 045 046 private final EDbVendor vendor; 047 048 // ===== 按对象组的规则 ===== 049 private final EnumMap<ObjectGroup, IdentifierRules> rulesByGroup; 050 051 // ===== Vendor-specific flags ===== 052 private final VendorFlags flags; 053 054 // ===== 对象组定义 ===== 055 056 /** 057 * 对象组(用于区分不同对象类型的标识符规则) 058 * 059 * <p>某些数据库对不同对象类型使用不同的大小写规则: 060 * <ul> 061 * <li>BigQuery: 表名敏感,列名不敏感 062 * <li>MySQL: 表名根据 lower_case_table_names,列名始终不敏感 063 * </ul> 064 */ 065 public enum ObjectGroup { 066 /** 067 * 名称组(catalog, schema, table, view, procedure, trigger) 068 */ 069 NAME_GROUP, 070 071 /** 072 * 列组(column) 073 */ 074 COLUMN_GROUP, 075 076 /** 077 * 函数组(function) 078 * <p>某些数据库函数名规则与表名不同 079 */ 080 ROUTINE_GROUP 081 } 082 083 // ===== Vendor flags 封装 ===== 084 085 /** 086 * Vendor-specific flags(厂商特定配置) 087 * 088 * <p>封装各数据库厂商的特殊配置参数 089 */ 090 public static class VendorFlags { 091 /** 092 * MySQL: lower_case_table_names 系统变量(0, 1, 2) 093 * <ul> 094 * <li>0: 大小写敏感(Unix/Linux) 095 * <li>1: 存储为小写,比较不敏感(Windows) 096 * <li>2: 存储保留原样,比较不敏感(macOS) 097 * </ul> 098 */ 099 public final int mysqlLowerCaseTableNames; 100 101 /** 102 * SQL Server / Azure SQL: 默认 collation 名称 103 * <p>例如: "SQL_Latin1_General_CP1_CI_AS" 104 */ 105 public final String defaultCollation; 106 107 /** 108 * Redshift: enable_case_sensitive_identifier 参数 109 * <p>默认 false(与 PostgreSQL 一致) 110 */ 111 public final boolean redshiftEnableCaseSensitive; 112 113 /** 114 * Snowflake: QUOTED_IDENTIFIERS_IGNORE_CASE 会话参数 115 * <p>默认 false(quoted 标识符大小写敏感) 116 */ 117 public final boolean snowflakeQuotedIdentifiersIgnoreCase; 118 119 /** 120 * MySQL 家族: sql_mode 中的 ANSI_QUOTES 选项 121 * 122 * <p>为 true 时,双引号 {@code "} 界定标识符(而非字符串字面量)。 123 * 只影响 {@link IdentifierCodec} 的引号识别/编解码,不影响大小写 124 * 折叠/比较规则(fold/compare 与 ANSI_QUOTES 无关)。 125 * <p>默认 false(MySQL 出厂默认,{@code "} 是字符串定界符)。 126 */ 127 public final boolean mysqlAnsiQuotes; 128 129 /** 130 * 构造 vendor flags(ansiQuotes 取默认值 false) 131 * 132 * @param mysqlLowerCaseTableNames MySQL lower_case_table_names (0, 1, 2) 133 * @param defaultCollation SQL Server collation 名称 134 * @param redshiftEnableCaseSensitive Redshift case sensitive 开关 135 * @param snowflakeQuotedIdentifiersIgnoreCase Snowflake quoted ignore case 开关 136 */ 137 public VendorFlags(int mysqlLowerCaseTableNames, 138 String defaultCollation, 139 boolean redshiftEnableCaseSensitive, 140 boolean snowflakeQuotedIdentifiersIgnoreCase) { 141 this(mysqlLowerCaseTableNames, defaultCollation, redshiftEnableCaseSensitive, 142 snowflakeQuotedIdentifiersIgnoreCase, false); 143 } 144 145 /** 146 * 构造 vendor flags(完整参数) 147 * 148 * @param mysqlLowerCaseTableNames MySQL lower_case_table_names (0, 1, 2) 149 * @param defaultCollation SQL Server collation 名称 150 * @param redshiftEnableCaseSensitive Redshift case sensitive 开关 151 * @param snowflakeQuotedIdentifiersIgnoreCase Snowflake quoted ignore case 开关 152 * @param mysqlAnsiQuotes MySQL 家族 sql_mode ANSI_QUOTES 开关 153 */ 154 public VendorFlags(int mysqlLowerCaseTableNames, 155 String defaultCollation, 156 boolean redshiftEnableCaseSensitive, 157 boolean snowflakeQuotedIdentifiersIgnoreCase, 158 boolean mysqlAnsiQuotes) { 159 this.mysqlLowerCaseTableNames = mysqlLowerCaseTableNames; 160 this.defaultCollation = defaultCollation; 161 this.redshiftEnableCaseSensitive = redshiftEnableCaseSensitive; 162 this.snowflakeQuotedIdentifiersIgnoreCase = snowflakeQuotedIdentifiersIgnoreCase; 163 this.mysqlAnsiQuotes = mysqlAnsiQuotes; 164 } 165 166 /** 167 * 默认 flags(用于大部分数据库) 168 */ 169 public static VendorFlags defaults() { 170 return new VendorFlags( 171 // MySQL lower_case_table_names = 1 (P0d.2): the unconfigured default keeps 172 // the legacy case-INSENSITIVE table equality, and mode 1's LOWER fold keeps 173 // keyForMap bucket keys coherent with that relation (mode 2 would bucket 174 // case-variants apart while areEqual equates them). Mode 0 (Linux 175 // case-sensitive) remains available through explicit configuration. 176 1, 177 "SQL_Latin1_General_CP1_CI_AS", // SQL Server 默认 collation 178 false, // Redshift case sensitive = false 179 false // Snowflake quoted ignore case = false 180 ); 181 } 182 183 @Override 184 public boolean equals(Object o) { 185 if (this == o) return true; 186 if (o == null || getClass() != o.getClass()) return false; 187 VendorFlags that = (VendorFlags) o; 188 return mysqlLowerCaseTableNames == that.mysqlLowerCaseTableNames && 189 redshiftEnableCaseSensitive == that.redshiftEnableCaseSensitive && 190 snowflakeQuotedIdentifiersIgnoreCase == that.snowflakeQuotedIdentifiersIgnoreCase && 191 mysqlAnsiQuotes == that.mysqlAnsiQuotes && 192 Objects.equals(defaultCollation, that.defaultCollation); 193 } 194 195 @Override 196 public int hashCode() { 197 return Objects.hash(mysqlLowerCaseTableNames, defaultCollation, 198 redshiftEnableCaseSensitive, snowflakeQuotedIdentifiersIgnoreCase, 199 mysqlAnsiQuotes); 200 } 201 } 202 203 // ===== 构造函数 ===== 204 205 /** 206 * 构造标识符配置档案 207 * 208 * @param vendor 数据库厂商 209 * @param rulesByGroup 按对象组的规则 210 * @param flags vendor-specific flags 211 */ 212 private IdentifierProfile(EDbVendor vendor, 213 EnumMap<ObjectGroup, IdentifierRules> rulesByGroup, 214 VendorFlags flags) { 215 this.vendor = Objects.requireNonNull(vendor, "vendor"); 216 this.rulesByGroup = new EnumMap<>(Objects.requireNonNull(rulesByGroup, "rulesByGroup")); 217 this.flags = Objects.requireNonNull(flags, "flags"); 218 // Immutable → compute once; getFingerprint sits on the per-compare hot path 219 // since areEqual/canonKey stamp keys with it (P0d). 220 this.fingerprint = Objects.hash( 221 vendor, 222 flags, 223 this.rulesByGroup.get(ObjectGroup.NAME_GROUP), 224 this.rulesByGroup.get(ObjectGroup.COLUMN_GROUP), 225 this.rulesByGroup.get(ObjectGroup.ROUTINE_GROUP) 226 ); 227 } 228 229 private final long fingerprint; 230 231 // ===== 工厂方法:根据厂商和 flags 创建 ===== 232 233 /** 234 * 为指定厂商创建标识符配置档案 235 * 236 * @param vendor 数据库厂商 237 * @param flags vendor-specific flags 238 * @return 标识符配置档案 239 */ 240 public static IdentifierProfile forVendor(EDbVendor vendor, VendorFlags flags) { 241 EnumMap<ObjectGroup, IdentifierRules> rules = defaultRulesFor(vendor, flags); 242 return new IdentifierProfile(vendor, rules, flags); 243 } 244 245 // ===== Builder entry points ===== 246 247 /** 248 * Create a builder seeded from default rules for the given vendor 249 * with default VendorFlags. 250 * 251 * @param vendor database vendor 252 * @return a new Builder 253 */ 254 public static Builder builder(EDbVendor vendor) { 255 return builder(vendor, VendorFlags.defaults()); 256 } 257 258 /** 259 * Create a builder seeded from default rules for the given vendor 260 * and the specified VendorFlags. 261 * 262 * @param vendor database vendor 263 * @param flags vendor-specific flags 264 * @return a new Builder 265 */ 266 public static Builder builder(EDbVendor vendor, VendorFlags flags) { 267 Objects.requireNonNull(vendor, "vendor"); 268 Objects.requireNonNull(flags, "flags"); 269 return new Builder(vendor, flags, defaultRulesFor(vendor, flags)); 270 } 271 272 /** 273 * Create a builder pre-populated from this profile's current state. 274 * Enables deriving a variant without re-specifying everything. 275 * 276 * @return a new Builder seeded from this profile 277 */ 278 public Builder toBuilder() { 279 return new Builder(this.vendor, this.flags, new EnumMap<>(this.rulesByGroup)); 280 } 281 282 // ===== Internal: single source of truth for default rules ===== 283 284 /** 285 * Compute the default per-group rules for the given vendor and flags. 286 * This is the sole source of truth for default rules -- both forVendor() 287 * and the Builder use this method. 288 * 289 * @param vendor database vendor 290 * @param flags vendor-specific flags 291 * @return mutable EnumMap with rules for all three groups 292 */ 293 static EnumMap<ObjectGroup, IdentifierRules> defaultRulesFor(EDbVendor vendor, VendorFlags flags) { 294 EnumMap<ObjectGroup, IdentifierRules> rules = new EnumMap<>(ObjectGroup.class); 295 296 switch (vendor) { 297 case dbvoracle: { 298 IdentifierRules rule = IdentifierRules.forOracle(); 299 rules.put(ObjectGroup.NAME_GROUP, rule); 300 rules.put(ObjectGroup.COLUMN_GROUP, rule); 301 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 302 break; 303 } 304 305 case dbvdameng: { 306 IdentifierRules rule = IdentifierRules.forDameng(); 307 rules.put(ObjectGroup.NAME_GROUP, rule); 308 rules.put(ObjectGroup.COLUMN_GROUP, rule); 309 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 310 break; 311 } 312 313 case dbvpostgresql: 314 case dbvduckdb: 315 case dbvgreenplum: 316 case dbvgaussdb: 317 case dbvedb: 318 case dbvsqlite: { 319 IdentifierRules rule = IdentifierRules.forPostgreSQL(); 320 rules.put(ObjectGroup.NAME_GROUP, rule); 321 rules.put(ObjectGroup.COLUMN_GROUP, rule); 322 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 323 break; 324 } 325 326 case dbvredshift: { 327 IdentifierRules rule = flags.redshiftEnableCaseSensitive 328 ? IdentifierRules.forCouchbase() 329 : IdentifierRules.forPostgreSQL(); 330 rules.put(ObjectGroup.NAME_GROUP, rule); 331 rules.put(ObjectGroup.COLUMN_GROUP, rule); 332 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 333 break; 334 } 335 336 case dbvclickhouse: { 337 IdentifierRules rule = IdentifierRules.forCouchbase(); 338 rules.put(ObjectGroup.NAME_GROUP, rule); 339 rules.put(ObjectGroup.COLUMN_GROUP, rule); 340 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 341 break; 342 } 343 344 case dbvcouchbase: { 345 IdentifierRules rule = IdentifierRules.forCouchbase(); 346 rules.put(ObjectGroup.NAME_GROUP, rule); 347 rules.put(ObjectGroup.COLUMN_GROUP, rule); 348 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 349 break; 350 } 351 352 case dbvmssql: 353 case dbvazuresql: { 354 IdentifierRules rule = IdentifierRules.forSQLServer(flags.defaultCollation); 355 rules.put(ObjectGroup.NAME_GROUP, rule); 356 rules.put(ObjectGroup.COLUMN_GROUP, rule); 357 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 358 break; 359 } 360 361 case dbvmysql: { 362 rules.put(ObjectGroup.NAME_GROUP, 363 IdentifierRules.forMySQL(flags.mysqlLowerCaseTableNames)); 364 rules.put(ObjectGroup.COLUMN_GROUP, IdentifierRules.forMySQLColumn()); 365 rules.put(ObjectGroup.ROUTINE_GROUP, IdentifierRules.forMySQLRoutine()); 366 break; 367 } 368 369 case dbvoceanbase: { 370 // Phase 1: identifier rules mirror MySQL because the default 371 // EOBTenantMode is MYSQL and the IdentifierProfile is built 372 // before the user has had a chance to call setOBTenantMode. 373 // TODO(oceanbase Phase 3): split this into MYSQL/ORACLE 374 // branches once the EOBTenantMode is queryable from 375 // IdentifierProfileFlags or wherever the profile is built. 376 rules.put(ObjectGroup.NAME_GROUP, 377 IdentifierRules.forMySQL(flags.mysqlLowerCaseTableNames)); 378 rules.put(ObjectGroup.COLUMN_GROUP, IdentifierRules.forMySQLColumn()); 379 rules.put(ObjectGroup.ROUTINE_GROUP, IdentifierRules.forMySQLRoutine()); 380 break; 381 } 382 383 case dbvdoris: { 384 rules.put(ObjectGroup.NAME_GROUP, IdentifierRules.forDoris()); 385 rules.put(ObjectGroup.COLUMN_GROUP, IdentifierRules.forDorisColumn()); 386 rules.put(ObjectGroup.ROUTINE_GROUP, IdentifierRules.forMySQLRoutine()); 387 break; 388 } 389 390 case dbvstarrocks: { 391 rules.put(ObjectGroup.NAME_GROUP, IdentifierRules.forStarrocks()); 392 rules.put(ObjectGroup.COLUMN_GROUP, IdentifierRules.forStarrocksColumn()); 393 rules.put(ObjectGroup.ROUTINE_GROUP, IdentifierRules.forMySQLRoutine()); 394 break; 395 } 396 397 case dbvbigquery: { 398 rules.put(ObjectGroup.NAME_GROUP, IdentifierRules.forBigQueryTable()); 399 rules.put(ObjectGroup.COLUMN_GROUP, IdentifierRules.forBigQueryColumn()); 400 rules.put(ObjectGroup.ROUTINE_GROUP, IdentifierRules.forBigQueryTable()); 401 break; 402 } 403 404 case dbvdb2: 405 case dbvnetezza: 406 case dbvexasol: { 407 IdentifierRules rule = IdentifierRules.forDB2(); 408 rules.put(ObjectGroup.NAME_GROUP, rule); 409 rules.put(ObjectGroup.COLUMN_GROUP, rule); 410 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 411 break; 412 } 413 414 case dbvsnowflake: { 415 IdentifierRules rule = IdentifierRules.forSnowflake( 416 flags.snowflakeQuotedIdentifiersIgnoreCase); 417 rules.put(ObjectGroup.NAME_GROUP, rule); 418 rules.put(ObjectGroup.COLUMN_GROUP, rule); 419 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 420 break; 421 } 422 423 case dbvhana: { 424 IdentifierRules rule = IdentifierRules.forHANA(); 425 rules.put(ObjectGroup.NAME_GROUP, rule); 426 rules.put(ObjectGroup.COLUMN_GROUP, rule); 427 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 428 break; 429 } 430 431 case dbvpresto: 432 case dbvtrino: { 433 IdentifierRules rule = IdentifierRules.forPresto(); 434 rules.put(ObjectGroup.NAME_GROUP, rule); 435 rules.put(ObjectGroup.COLUMN_GROUP, rule); 436 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 437 break; 438 } 439 440 case dbvvertica: { 441 IdentifierRules rule = IdentifierRules.forVertica(); 442 rules.put(ObjectGroup.NAME_GROUP, rule); 443 rules.put(ObjectGroup.COLUMN_GROUP, rule); 444 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 445 break; 446 } 447 448 case dbvhive: 449 case dbvsparksql: 450 case dbvflink: 451 case dbvimpala: 452 case dbvdatabricks: { 453 IdentifierRules rule = IdentifierRules.forHive(); 454 rules.put(ObjectGroup.NAME_GROUP, rule); 455 rules.put(ObjectGroup.COLUMN_GROUP, rule); 456 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 457 break; 458 } 459 460 case dbvteradata: { 461 IdentifierRules rule = IdentifierRules.forTeradata(); 462 rules.put(ObjectGroup.NAME_GROUP, rule); 463 rules.put(ObjectGroup.COLUMN_GROUP, rule); 464 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 465 break; 466 } 467 468 case dbvathena: { 469 IdentifierRules rule = IdentifierRules.forAthena(); 470 rules.put(ObjectGroup.NAME_GROUP, rule); 471 rules.put(ObjectGroup.COLUMN_GROUP, rule); 472 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 473 break; 474 } 475 476 default: { 477 IdentifierRules rule = IdentifierRules.forGeneric(); 478 rules.put(ObjectGroup.NAME_GROUP, rule); 479 rules.put(ObjectGroup.COLUMN_GROUP, rule); 480 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 481 break; 482 } 483 } 484 485 return rules; 486 } 487 488 // ===== Builder inner class ===== 489 490 /** 491 * Builder for creating customized IdentifierProfile instances. 492 * 493 * <p>The builder is seeded from vendor defaults. Overrides can be applied 494 * per object group. The built profile is immutable. 495 * 496 * <p>Usage example: 497 * <pre> 498 * IdentifierProfile profile = IdentifierProfile 499 * .builder(EDbVendor.dbvmssql) 500 * .withColumnRules(customColumnRules) 501 * .build(); 502 * </pre> 503 */ 504 public static final class Builder { 505 506 private EDbVendor vendor; 507 private VendorFlags flags; 508 private EnumMap<ObjectGroup, IdentifierRules> seededRules; 509 private final EnumMap<ObjectGroup, IdentifierRules> overrides; 510 511 Builder(EDbVendor vendor, VendorFlags flags, 512 EnumMap<ObjectGroup, IdentifierRules> seedRules) { 513 this.vendor = vendor; 514 this.flags = flags; 515 this.seededRules = new EnumMap<>(seedRules); 516 this.overrides = new EnumMap<>(ObjectGroup.class); 517 } 518 519 /** 520 * Override VendorFlags (replaces default-seeded flags). 521 * Re-seeds rules that depend on flags while preserving 522 * explicit overrides set via withRules(). 523 * 524 * @param flags new vendor flags 525 * @return this builder 526 */ 527 public Builder withFlags(VendorFlags flags) { 528 this.flags = Objects.requireNonNull(flags, "flags"); 529 this.seededRules = defaultRulesFor(this.vendor, flags); 530 return this; 531 } 532 533 /** 534 * Override the rules for a specific object group. 535 * 536 * @param group the object group to override 537 * @param rules the custom rules 538 * @return this builder 539 */ 540 public Builder withRules(ObjectGroup group, IdentifierRules rules) { 541 Objects.requireNonNull(group, "group"); 542 Objects.requireNonNull(rules, "rules"); 543 overrides.put(group, rules); 544 return this; 545 } 546 547 /** 548 * Convenience: override NAME_GROUP rules. 549 * 550 * @param rules the custom rules for names 551 * @return this builder 552 */ 553 public Builder withNameRules(IdentifierRules rules) { 554 return withRules(ObjectGroup.NAME_GROUP, rules); 555 } 556 557 /** 558 * Convenience: override COLUMN_GROUP rules. 559 * 560 * @param rules the custom rules for columns 561 * @return this builder 562 */ 563 public Builder withColumnRules(IdentifierRules rules) { 564 return withRules(ObjectGroup.COLUMN_GROUP, rules); 565 } 566 567 /** 568 * Convenience: override ROUTINE_GROUP rules. 569 * 570 * @param rules the custom rules for routines 571 * @return this builder 572 */ 573 public Builder withRoutineRules(IdentifierRules rules) { 574 return withRules(ObjectGroup.ROUTINE_GROUP, rules); 575 } 576 577 /** 578 * Build an immutable IdentifierProfile. 579 * Merges seeded rules with overrides (overrides win). 580 * 581 * @return the built profile 582 */ 583 public IdentifierProfile build() { 584 EnumMap<ObjectGroup, IdentifierRules> merged = new EnumMap<>(seededRules); 585 merged.putAll(overrides); 586 return new IdentifierProfile(vendor, merged, flags); 587 } 588 } 589 590 // ===== 查询接口 ===== 591 592 /** 593 * 获取指定对象类型的标识符规则 594 * 595 * @param objectType 对象类型 596 * @return 标识符规则 597 */ 598 public IdentifierRules getRules(ESQLDataObjectType objectType) { 599 ObjectGroup group = mapToGroup(objectType); 600 return rulesByGroup.get(group); 601 } 602 603 /** 604 * 获取 vendor flags 605 * 606 * @return vendor flags 607 */ 608 public VendorFlags getFlags() { 609 return flags; 610 } 611 612 /** 613 * 获取数据库厂商 614 * 615 * @return 厂商 616 */ 617 public EDbVendor getVendor() { 618 return vendor; 619 } 620 621 /** 622 * 按对象组获取规则(内部使用) 623 * 624 * @param group 对象组 625 * @return 标识符规则 626 */ 627 IdentifierRules getRulesByGroup(ObjectGroup group) { 628 return rulesByGroup.get(group); 629 } 630 631 /** 632 * 将对象类型映射到对象组(公开访问器,供 {@link CanonKey} 的规则域标识使用) 633 * 634 * @param objectType 对象类型 635 * @return 对象组 636 */ 637 public ObjectGroup groupOf(ESQLDataObjectType objectType) { 638 return mapToGroup(objectType); 639 } 640 641 /** 642 * 将对象类型映射到对象组 643 */ 644 private ObjectGroup mapToGroup(ESQLDataObjectType type) { 645 if (type == null) return ObjectGroup.NAME_GROUP; 646 647 switch (type) { 648 case dotColumn: 649 return ObjectGroup.COLUMN_GROUP; 650 651 case dotFunction: 652 return ObjectGroup.ROUTINE_GROUP; 653 654 // 其他所有类型归入 NAME_GROUP 655 case dotCatalog: 656 case dotSchema: 657 case dotTable: 658 case dotProcedure: 659 case dotTrigger: 660 case dotOraclePackage: 661 default: 662 return ObjectGroup.NAME_GROUP; 663 } 664 } 665 666 // ===== 指纹计算(用于缓存失效) ===== 667 668 /** 669 * 计算配置指纹(用于 TObjectName 缓存失效) 670 * 671 * <p>当 vendor 或 flags 变化时,指纹会改变,触发缓存失效 672 * 673 * @return 配置指纹(64位哈希值) 674 */ 675 public long getFingerprint() { 676 return fingerprint; 677 } 678 679 // ===== toString 方法(用于调试) ===== 680 681 @Override 682 public String toString() { 683 return String.format("IdentifierProfile{vendor=%s, flags=%s}", 684 vendor, flags); 685 } 686}