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 * 构造 vendor flags 121 * 122 * @param mysqlLowerCaseTableNames MySQL lower_case_table_names (0, 1, 2) 123 * @param defaultCollation SQL Server collation 名称 124 * @param redshiftEnableCaseSensitive Redshift case sensitive 开关 125 * @param snowflakeQuotedIdentifiersIgnoreCase Snowflake quoted ignore case 开关 126 */ 127 public VendorFlags(int mysqlLowerCaseTableNames, 128 String defaultCollation, 129 boolean redshiftEnableCaseSensitive, 130 boolean snowflakeQuotedIdentifiersIgnoreCase) { 131 this.mysqlLowerCaseTableNames = mysqlLowerCaseTableNames; 132 this.defaultCollation = defaultCollation; 133 this.redshiftEnableCaseSensitive = redshiftEnableCaseSensitive; 134 this.snowflakeQuotedIdentifiersIgnoreCase = snowflakeQuotedIdentifiersIgnoreCase; 135 } 136 137 /** 138 * 默认 flags(用于大部分数据库) 139 */ 140 public static VendorFlags defaults() { 141 return new VendorFlags( 142 0, // MySQL lower_case_table_names = 0(敏感) 143 "SQL_Latin1_General_CP1_CI_AS", // SQL Server 默认 collation 144 false, // Redshift case sensitive = false 145 false // Snowflake quoted ignore case = false 146 ); 147 } 148 149 @Override 150 public boolean equals(Object o) { 151 if (this == o) return true; 152 if (o == null || getClass() != o.getClass()) return false; 153 VendorFlags that = (VendorFlags) o; 154 return mysqlLowerCaseTableNames == that.mysqlLowerCaseTableNames && 155 redshiftEnableCaseSensitive == that.redshiftEnableCaseSensitive && 156 snowflakeQuotedIdentifiersIgnoreCase == that.snowflakeQuotedIdentifiersIgnoreCase && 157 Objects.equals(defaultCollation, that.defaultCollation); 158 } 159 160 @Override 161 public int hashCode() { 162 return Objects.hash(mysqlLowerCaseTableNames, defaultCollation, 163 redshiftEnableCaseSensitive, snowflakeQuotedIdentifiersIgnoreCase); 164 } 165 } 166 167 // ===== 构造函数 ===== 168 169 /** 170 * 构造标识符配置档案 171 * 172 * @param vendor 数据库厂商 173 * @param rulesByGroup 按对象组的规则 174 * @param flags vendor-specific flags 175 */ 176 private IdentifierProfile(EDbVendor vendor, 177 EnumMap<ObjectGroup, IdentifierRules> rulesByGroup, 178 VendorFlags flags) { 179 this.vendor = Objects.requireNonNull(vendor, "vendor"); 180 this.rulesByGroup = new EnumMap<>(Objects.requireNonNull(rulesByGroup, "rulesByGroup")); 181 this.flags = Objects.requireNonNull(flags, "flags"); 182 } 183 184 // ===== 工厂方法:根据厂商和 flags 创建 ===== 185 186 /** 187 * 为指定厂商创建标识符配置档案 188 * 189 * @param vendor 数据库厂商 190 * @param flags vendor-specific flags 191 * @return 标识符配置档案 192 */ 193 public static IdentifierProfile forVendor(EDbVendor vendor, VendorFlags flags) { 194 EnumMap<ObjectGroup, IdentifierRules> rules = defaultRulesFor(vendor, flags); 195 return new IdentifierProfile(vendor, rules, flags); 196 } 197 198 // ===== Builder entry points ===== 199 200 /** 201 * Create a builder seeded from default rules for the given vendor 202 * with default VendorFlags. 203 * 204 * @param vendor database vendor 205 * @return a new Builder 206 */ 207 public static Builder builder(EDbVendor vendor) { 208 return builder(vendor, VendorFlags.defaults()); 209 } 210 211 /** 212 * Create a builder seeded from default rules for the given vendor 213 * and the specified VendorFlags. 214 * 215 * @param vendor database vendor 216 * @param flags vendor-specific flags 217 * @return a new Builder 218 */ 219 public static Builder builder(EDbVendor vendor, VendorFlags flags) { 220 Objects.requireNonNull(vendor, "vendor"); 221 Objects.requireNonNull(flags, "flags"); 222 return new Builder(vendor, flags, defaultRulesFor(vendor, flags)); 223 } 224 225 /** 226 * Create a builder pre-populated from this profile's current state. 227 * Enables deriving a variant without re-specifying everything. 228 * 229 * @return a new Builder seeded from this profile 230 */ 231 public Builder toBuilder() { 232 return new Builder(this.vendor, this.flags, new EnumMap<>(this.rulesByGroup)); 233 } 234 235 // ===== Internal: single source of truth for default rules ===== 236 237 /** 238 * Compute the default per-group rules for the given vendor and flags. 239 * This is the sole source of truth for default rules -- both forVendor() 240 * and the Builder use this method. 241 * 242 * @param vendor database vendor 243 * @param flags vendor-specific flags 244 * @return mutable EnumMap with rules for all three groups 245 */ 246 static EnumMap<ObjectGroup, IdentifierRules> defaultRulesFor(EDbVendor vendor, VendorFlags flags) { 247 EnumMap<ObjectGroup, IdentifierRules> rules = new EnumMap<>(ObjectGroup.class); 248 249 switch (vendor) { 250 case dbvoracle: { 251 IdentifierRules rule = IdentifierRules.forOracle(); 252 rules.put(ObjectGroup.NAME_GROUP, rule); 253 rules.put(ObjectGroup.COLUMN_GROUP, rule); 254 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 255 break; 256 } 257 258 case dbvdameng: { 259 IdentifierRules rule = IdentifierRules.forDameng(); 260 rules.put(ObjectGroup.NAME_GROUP, rule); 261 rules.put(ObjectGroup.COLUMN_GROUP, rule); 262 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 263 break; 264 } 265 266 case dbvpostgresql: 267 case dbvduckdb: 268 case dbvgreenplum: 269 case dbvgaussdb: 270 case dbvedb: 271 case dbvsqlite: { 272 IdentifierRules rule = IdentifierRules.forPostgreSQL(); 273 rules.put(ObjectGroup.NAME_GROUP, rule); 274 rules.put(ObjectGroup.COLUMN_GROUP, rule); 275 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 276 break; 277 } 278 279 case dbvredshift: { 280 IdentifierRules rule = flags.redshiftEnableCaseSensitive 281 ? IdentifierRules.forCouchbase() 282 : IdentifierRules.forPostgreSQL(); 283 rules.put(ObjectGroup.NAME_GROUP, rule); 284 rules.put(ObjectGroup.COLUMN_GROUP, rule); 285 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 286 break; 287 } 288 289 case dbvclickhouse: { 290 IdentifierRules rule = IdentifierRules.forCouchbase(); 291 rules.put(ObjectGroup.NAME_GROUP, rule); 292 rules.put(ObjectGroup.COLUMN_GROUP, rule); 293 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 294 break; 295 } 296 297 case dbvcouchbase: { 298 IdentifierRules rule = IdentifierRules.forCouchbase(); 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 dbvmssql: 306 case dbvazuresql: { 307 IdentifierRules rule = IdentifierRules.forSQLServer(flags.defaultCollation); 308 rules.put(ObjectGroup.NAME_GROUP, rule); 309 rules.put(ObjectGroup.COLUMN_GROUP, rule); 310 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 311 break; 312 } 313 314 case dbvmysql: { 315 rules.put(ObjectGroup.NAME_GROUP, 316 IdentifierRules.forMySQL(flags.mysqlLowerCaseTableNames)); 317 rules.put(ObjectGroup.COLUMN_GROUP, IdentifierRules.forMySQLColumn()); 318 rules.put(ObjectGroup.ROUTINE_GROUP, IdentifierRules.forMySQLRoutine()); 319 break; 320 } 321 322 case dbvoceanbase: { 323 // Phase 1: identifier rules mirror MySQL because the default 324 // EOBTenantMode is MYSQL and the IdentifierProfile is built 325 // before the user has had a chance to call setOBTenantMode. 326 // TODO(oceanbase Phase 3): split this into MYSQL/ORACLE 327 // branches once the EOBTenantMode is queryable from 328 // IdentifierProfileFlags or wherever the profile is built. 329 rules.put(ObjectGroup.NAME_GROUP, 330 IdentifierRules.forMySQL(flags.mysqlLowerCaseTableNames)); 331 rules.put(ObjectGroup.COLUMN_GROUP, IdentifierRules.forMySQLColumn()); 332 rules.put(ObjectGroup.ROUTINE_GROUP, IdentifierRules.forMySQLRoutine()); 333 break; 334 } 335 336 case dbvdoris: { 337 rules.put(ObjectGroup.NAME_GROUP, IdentifierRules.forDoris()); 338 rules.put(ObjectGroup.COLUMN_GROUP, IdentifierRules.forDorisColumn()); 339 rules.put(ObjectGroup.ROUTINE_GROUP, IdentifierRules.forMySQLRoutine()); 340 break; 341 } 342 343 case dbvstarrocks: { 344 rules.put(ObjectGroup.NAME_GROUP, IdentifierRules.forStarrocks()); 345 rules.put(ObjectGroup.COLUMN_GROUP, IdentifierRules.forStarrocksColumn()); 346 rules.put(ObjectGroup.ROUTINE_GROUP, IdentifierRules.forMySQLRoutine()); 347 break; 348 } 349 350 case dbvbigquery: { 351 rules.put(ObjectGroup.NAME_GROUP, IdentifierRules.forBigQueryTable()); 352 rules.put(ObjectGroup.COLUMN_GROUP, IdentifierRules.forBigQueryColumn()); 353 rules.put(ObjectGroup.ROUTINE_GROUP, IdentifierRules.forBigQueryTable()); 354 break; 355 } 356 357 case dbvdb2: 358 case dbvnetezza: 359 case dbvexasol: { 360 IdentifierRules rule = IdentifierRules.forDB2(); 361 rules.put(ObjectGroup.NAME_GROUP, rule); 362 rules.put(ObjectGroup.COLUMN_GROUP, rule); 363 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 364 break; 365 } 366 367 case dbvsnowflake: { 368 IdentifierRules rule = IdentifierRules.forSnowflake( 369 flags.snowflakeQuotedIdentifiersIgnoreCase); 370 rules.put(ObjectGroup.NAME_GROUP, rule); 371 rules.put(ObjectGroup.COLUMN_GROUP, rule); 372 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 373 break; 374 } 375 376 case dbvhana: { 377 IdentifierRules rule = IdentifierRules.forHANA(); 378 rules.put(ObjectGroup.NAME_GROUP, rule); 379 rules.put(ObjectGroup.COLUMN_GROUP, rule); 380 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 381 break; 382 } 383 384 case dbvpresto: 385 case dbvtrino: { 386 IdentifierRules rule = IdentifierRules.forPresto(); 387 rules.put(ObjectGroup.NAME_GROUP, rule); 388 rules.put(ObjectGroup.COLUMN_GROUP, rule); 389 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 390 break; 391 } 392 393 case dbvvertica: { 394 IdentifierRules rule = IdentifierRules.forVertica(); 395 rules.put(ObjectGroup.NAME_GROUP, rule); 396 rules.put(ObjectGroup.COLUMN_GROUP, rule); 397 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 398 break; 399 } 400 401 case dbvhive: 402 case dbvsparksql: 403 case dbvflink: 404 case dbvimpala: 405 case dbvdatabricks: { 406 IdentifierRules rule = IdentifierRules.forHive(); 407 rules.put(ObjectGroup.NAME_GROUP, rule); 408 rules.put(ObjectGroup.COLUMN_GROUP, rule); 409 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 410 break; 411 } 412 413 case dbvteradata: { 414 IdentifierRules rule = IdentifierRules.forTeradata(); 415 rules.put(ObjectGroup.NAME_GROUP, rule); 416 rules.put(ObjectGroup.COLUMN_GROUP, rule); 417 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 418 break; 419 } 420 421 case dbvathena: { 422 IdentifierRules rule = IdentifierRules.forAthena(); 423 rules.put(ObjectGroup.NAME_GROUP, rule); 424 rules.put(ObjectGroup.COLUMN_GROUP, rule); 425 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 426 break; 427 } 428 429 default: { 430 IdentifierRules rule = IdentifierRules.forGeneric(); 431 rules.put(ObjectGroup.NAME_GROUP, rule); 432 rules.put(ObjectGroup.COLUMN_GROUP, rule); 433 rules.put(ObjectGroup.ROUTINE_GROUP, rule); 434 break; 435 } 436 } 437 438 return rules; 439 } 440 441 // ===== Builder inner class ===== 442 443 /** 444 * Builder for creating customized IdentifierProfile instances. 445 * 446 * <p>The builder is seeded from vendor defaults. Overrides can be applied 447 * per object group. The built profile is immutable. 448 * 449 * <p>Usage example: 450 * <pre> 451 * IdentifierProfile profile = IdentifierProfile 452 * .builder(EDbVendor.dbvmssql) 453 * .withColumnRules(customColumnRules) 454 * .build(); 455 * </pre> 456 */ 457 public static final class Builder { 458 459 private EDbVendor vendor; 460 private VendorFlags flags; 461 private EnumMap<ObjectGroup, IdentifierRules> seededRules; 462 private final EnumMap<ObjectGroup, IdentifierRules> overrides; 463 464 Builder(EDbVendor vendor, VendorFlags flags, 465 EnumMap<ObjectGroup, IdentifierRules> seedRules) { 466 this.vendor = vendor; 467 this.flags = flags; 468 this.seededRules = new EnumMap<>(seedRules); 469 this.overrides = new EnumMap<>(ObjectGroup.class); 470 } 471 472 /** 473 * Override VendorFlags (replaces default-seeded flags). 474 * Re-seeds rules that depend on flags while preserving 475 * explicit overrides set via withRules(). 476 * 477 * @param flags new vendor flags 478 * @return this builder 479 */ 480 public Builder withFlags(VendorFlags flags) { 481 this.flags = Objects.requireNonNull(flags, "flags"); 482 this.seededRules = defaultRulesFor(this.vendor, flags); 483 return this; 484 } 485 486 /** 487 * Override the rules for a specific object group. 488 * 489 * @param group the object group to override 490 * @param rules the custom rules 491 * @return this builder 492 */ 493 public Builder withRules(ObjectGroup group, IdentifierRules rules) { 494 Objects.requireNonNull(group, "group"); 495 Objects.requireNonNull(rules, "rules"); 496 overrides.put(group, rules); 497 return this; 498 } 499 500 /** 501 * Convenience: override NAME_GROUP rules. 502 * 503 * @param rules the custom rules for names 504 * @return this builder 505 */ 506 public Builder withNameRules(IdentifierRules rules) { 507 return withRules(ObjectGroup.NAME_GROUP, rules); 508 } 509 510 /** 511 * Convenience: override COLUMN_GROUP rules. 512 * 513 * @param rules the custom rules for columns 514 * @return this builder 515 */ 516 public Builder withColumnRules(IdentifierRules rules) { 517 return withRules(ObjectGroup.COLUMN_GROUP, rules); 518 } 519 520 /** 521 * Convenience: override ROUTINE_GROUP rules. 522 * 523 * @param rules the custom rules for routines 524 * @return this builder 525 */ 526 public Builder withRoutineRules(IdentifierRules rules) { 527 return withRules(ObjectGroup.ROUTINE_GROUP, rules); 528 } 529 530 /** 531 * Build an immutable IdentifierProfile. 532 * Merges seeded rules with overrides (overrides win). 533 * 534 * @return the built profile 535 */ 536 public IdentifierProfile build() { 537 EnumMap<ObjectGroup, IdentifierRules> merged = new EnumMap<>(seededRules); 538 merged.putAll(overrides); 539 return new IdentifierProfile(vendor, merged, flags); 540 } 541 } 542 543 // ===== 查询接口 ===== 544 545 /** 546 * 获取指定对象类型的标识符规则 547 * 548 * @param objectType 对象类型 549 * @return 标识符规则 550 */ 551 public IdentifierRules getRules(ESQLDataObjectType objectType) { 552 ObjectGroup group = mapToGroup(objectType); 553 return rulesByGroup.get(group); 554 } 555 556 /** 557 * 获取 vendor flags 558 * 559 * @return vendor flags 560 */ 561 public VendorFlags getFlags() { 562 return flags; 563 } 564 565 /** 566 * 获取数据库厂商 567 * 568 * @return 厂商 569 */ 570 public EDbVendor getVendor() { 571 return vendor; 572 } 573 574 /** 575 * 按对象组获取规则(内部使用) 576 * 577 * @param group 对象组 578 * @return 标识符规则 579 */ 580 IdentifierRules getRulesByGroup(ObjectGroup group) { 581 return rulesByGroup.get(group); 582 } 583 584 /** 585 * 将对象类型映射到对象组 586 */ 587 private ObjectGroup mapToGroup(ESQLDataObjectType type) { 588 if (type == null) return ObjectGroup.NAME_GROUP; 589 590 switch (type) { 591 case dotColumn: 592 return ObjectGroup.COLUMN_GROUP; 593 594 case dotFunction: 595 return ObjectGroup.ROUTINE_GROUP; 596 597 // 其他所有类型归入 NAME_GROUP 598 case dotCatalog: 599 case dotSchema: 600 case dotTable: 601 case dotProcedure: 602 case dotTrigger: 603 case dotOraclePackage: 604 default: 605 return ObjectGroup.NAME_GROUP; 606 } 607 } 608 609 // ===== 指纹计算(用于缓存失效) ===== 610 611 /** 612 * 计算配置指纹(用于 TObjectName 缓存失效) 613 * 614 * <p>当 vendor 或 flags 变化时,指纹会改变,触发缓存失效 615 * 616 * @return 配置指纹(64位哈希值) 617 */ 618 public long getFingerprint() { 619 return Objects.hash( 620 vendor, 621 flags, 622 rulesByGroup.get(ObjectGroup.NAME_GROUP), 623 rulesByGroup.get(ObjectGroup.COLUMN_GROUP), 624 rulesByGroup.get(ObjectGroup.ROUTINE_GROUP) 625 ); 626 } 627 628 // ===== toString 方法(用于调试) ===== 629 630 @Override 631 public String toString() { 632 return String.format("IdentifierProfile{vendor=%s, flags=%s}", 633 vendor, flags); 634 } 635}