001package gudusoft.gsqlparser.sqlenv; 002 003/** 004 * 标识符规则四元组(per vendor, per object group) 005 * 006 * <p>定义数据库厂商的标识符大小写规则,区分 quoted 和 unquoted 标识符的处理方式。 007 * 008 * <p>设计来源:dbobject_search.md 资深设计师方案 009 * 010 * <p>使用示例: 011 * <pre> 012 * // Oracle: unquoted 折叠为大写且不敏感,quoted 保留原样且敏感 013 * IdentifierRules oracleRules = IdentifierRules.forOracle(); 014 * 015 * // ClickHouse: 全部大小写敏感 016 * IdentifierRules clickhouseRules = IdentifierRules.forClickHouse(); 017 * </pre> 018 * 019 * @since 3.1.0.9 020 */ 021public final class IdentifierRules { 022 023 // ===== 四元组定义 ===== 024 025 /** 026 * Unquoted 标识符的大小写折叠规则 027 */ 028 public final CaseFold unquotedFold; 029 030 /** 031 * Unquoted 标识符的大小写比较规则 032 */ 033 public final CaseCompare unquotedCompare; 034 035 /** 036 * Quoted 标识符的大小写折叠规则(通常为 NONE,保留原样) 037 */ 038 public final CaseFold quotedFold; 039 040 /** 041 * Quoted 标识符的大小写比较规则 042 */ 043 public final CaseCompare quotedCompare; 044 045 // ===== 枚举定义 ===== 046 047 /** 048 * 大小写折叠规则(Case Folding) 049 * 050 * <p>决定如何规范化标识符的大小写 051 */ 052 public enum CaseFold { 053 /** 054 * 不转换(保留原样) 055 * <p>例如:ClickHouse unquoted, 所有 quoted identifiers 056 */ 057 NONE, 058 059 /** 060 * 转大写 061 * <p>例如:Oracle unquoted, DB2 unquoted, Snowflake unquoted 062 */ 063 UPPER, 064 065 /** 066 * 转小写 067 * <p>例如:PostgreSQL unquoted, Redshift unquoted, Greenplum unquoted 068 */ 069 LOWER 070 } 071 072 /** 073 * 大小写比较规则(Case Comparison) 074 * 075 * <p>决定如何比较两个标识符是否相等 076 */ 077 public enum CaseCompare { 078 /** 079 * 大小写敏感 080 * <p>例如:ClickHouse 所有标识符, 所有 quoted identifiers 081 */ 082 SENSITIVE, 083 084 /** 085 * 大小写不敏感 086 * <p>例如:Oracle unquoted, MySQL unquoted 087 */ 088 INSENSITIVE, 089 090 /** 091 * 基于 collation(运行时决定) 092 * <p>例如:SQL Server, Azure SQL 093 * <p>需要使用 {@link java.text.Collator} 进行比较 094 */ 095 COLLATION_BASED, 096 097 /** 098 * 与 unquoted 规则一致 099 * <p>例如:Presto quoted identifiers, Vertica quoted identifiers 100 * <p>在解析时需要回退到 unquotedCompare 101 */ 102 SAME_AS_UNQUOTED 103 } 104 105 // ===== 构造函数 ===== 106 107 /** 108 * 构造标识符规则 109 * 110 * @param unquotedFold unquoted 标识符的折叠规则 111 * @param unquotedCompare unquoted 标识符的比较规则 112 * @param quotedFold quoted 标识符的折叠规则 113 * @param quotedCompare quoted 标识符的比较规则 114 */ 115 public IdentifierRules(CaseFold unquotedFold, CaseCompare unquotedCompare, 116 CaseFold quotedFold, CaseCompare quotedCompare) { 117 this.unquotedFold = unquotedFold; 118 this.unquotedCompare = unquotedCompare; 119 this.quotedFold = quotedFold; 120 this.quotedCompare = quotedCompare; 121 } 122 123 // ===== 预设规则工厂方法 ===== 124 125 /** 126 * Oracle 标识符规则 127 * 128 * <p><strong>实际数据库行为(Oracle 12c+):</strong> 129 * <ul> 130 * <li>Unquoted: 折叠为大写,比较不敏感 (CREATE TABLE foo → stored as FOO, foo=FOO=Foo) 131 * <li>Quoted: 保留原样,比较敏感 (CREATE TABLE "foo" → stored as foo, "foo"!="FOO") 132 * </ul> 133 * 134 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 135 * <ul> 136 * <li>columnCollationCaseSensitive = {@code true} → ❌ <strong>INCOMPATIBLE</strong> (legacy preserved case, new folds to UPPER) 137 * <li>functionCollationCaseSensitive = {@code true} → ❌ <strong>INCOMPATIBLE</strong> (legacy preserved case, new folds to UPPER) 138 * <li>tableCollationCaseSensitive = {@code true} → ❌ <strong>INCOMPATIBLE</strong> (legacy preserved case, new folds to UPPER) 139 * <li>catalogCollationCaseSensitive = {@code false} → ✅ <strong>COMPATIBLE</strong> (both fold to UPPER) 140 * </ul> 141 * 142 * <p><strong>测试用例影响:</strong> 143 * <ul> 144 * <li>✅ 新规则正确:Oracle 确实将 unquoted identifiers 折叠为大写</li> 145 * <li>⚠️ 如果旧测试期望保留原始大小写 (如 "myTable" 保持为 "myTable"),则测试会失败</li> 146 * <li>⚠️ 应更新测试期望为大写 (如 "myTable" → "MYTABLE")</li> 147 * </ul> 148 * 149 * <p><strong>IdentifierRules 配置:</strong> 150 * <ul> 151 * <li>Unquoted: 折叠为大写 ({@link CaseFold#UPPER}), 比较不敏感 ({@link CaseCompare#INSENSITIVE}) 152 * <li>Quoted: 保留原样 ({@link CaseFold#NONE}), 比较敏感 ({@link CaseCompare#SENSITIVE}) 153 * </ul> 154 */ 155 public static IdentifierRules forOracle() { 156 return new IdentifierRules( 157 CaseFold.UPPER, // unquoted 折叠为大写 158 CaseCompare.INSENSITIVE, // unquoted 比较不敏感 159 CaseFold.NONE, // quoted 保留原样 160 CaseCompare.SENSITIVE // quoted 比较敏感 161 ); 162 } 163 164 /** 165 * PostgreSQL / Redshift / Greenplum 标识符规则 166 * 167 * <p><strong>实际数据库行为(PostgreSQL 12+):</strong> 168 * <ul> 169 * <li>Unquoted: 折叠为小写,比较不敏感 (CREATE TABLE MyTable → stored as mytable, MyTable=mytable=MYTABLE) 170 * <li>Quoted: 保留原样,比较敏感 (CREATE TABLE "MyTable" → stored as MyTable, "MyTable"!="mytable") 171 * </ul> 172 * 173 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 174 * <ul> 175 * <li>columnCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new folds to LOWER) 176 * <li>functionCollationCaseSensitive = {@code true} → ❌ <strong>INCOMPATIBLE</strong> (legacy preserved case, new folds to LOWER) 177 * <li>tableCollationCaseSensitive = {@code true} → ❌ <strong>INCOMPATIBLE</strong> (legacy preserved case, new folds to LOWER) 178 * <li>catalogCollationCaseSensitive = {@code true} → ❌ <strong>INCOMPATIBLE</strong> (legacy preserved case, new folds to LOWER) 179 * </ul> 180 * 181 * <p><strong>测试用例影响:</strong> 182 * <ul> 183 * <li>✅ 新规则正确:PostgreSQL 确实将 unquoted identifiers 折叠为小写</li> 184 * <li>⚠️ 如果旧测试期望大写 (如 "MyTable" → "MYTABLE"),则测试会失败</li> 185 * <li>⚠️ 应更新测试期望为小写 (如 "MyTable" → "mytable")</li> 186 * <li>⚠️ 如果旧测试期望保留原始大小写,也会失败</li> 187 * </ul> 188 * 189 * <p><strong>IdentifierRules 配置:</strong> 190 * <ul> 191 * <li>Unquoted: 折叠为小写 ({@link CaseFold#LOWER}), 比较不敏感 ({@link CaseCompare#INSENSITIVE}) 192 * <li>Quoted: 保留原样 ({@link CaseFold#NONE}), 比较敏感 ({@link CaseCompare#SENSITIVE}) 193 * </ul> 194 */ 195 public static IdentifierRules forPostgreSQL() { 196 return new IdentifierRules( 197 CaseFold.LOWER, // unquoted 折叠为小写 198 CaseCompare.INSENSITIVE, // unquoted 比较不敏感 199 CaseFold.NONE, // quoted 保留原样 200 CaseCompare.SENSITIVE // quoted 比较敏感 201 ); 202 } 203 204 /** 205 * ClickHouse 标识符规则 206 * 207 * <p><strong>实际数据库行为(ClickHouse 20+):</strong> 208 * <ul> 209 * <li>Unquoted: 保留原样,比较敏感 (CREATE TABLE MyTable → stored as MyTable, MyTable!=mytable) 210 * <li>Quoted: 保留原样,比较敏感 (CREATE TABLE `MyTable` → stored as MyTable, MyTable!=mytable) 211 * </ul> 212 * 213 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 214 * <ul> 215 * <li>columnCollationCaseSensitive = {@code false} → ❌ <strong>INCOMPATIBLE</strong> (legacy folded to UPPER, new preserves case) 216 * <li>functionCollationCaseSensitive = {@code false} → ❌ <strong>INCOMPATIBLE</strong> (legacy folded to UPPER, new preserves case) 217 * <li>tableCollationCaseSensitive = {@code false} → ❌ <strong>INCOMPATIBLE</strong> (legacy folded to UPPER, new preserves case) 218 * <li>catalogCollationCaseSensitive = {@code false} → ❌ <strong>INCOMPATIBLE</strong> (legacy folded to UPPER, new preserves case) 219 * </ul> 220 * 221 * <p><strong>测试用例影响:</strong> 222 * <ul> 223 * <li>✅ 新规则正确:ClickHouse 是完全大小写敏感的数据库</li> 224 * <li>⚠️ 如果旧测试期望 "MyTable" → "MYTABLE",则测试会失败</li> 225 * <li>⚠️ 应更新测试期望为保留原样 (如 "MyTable" 保持为 "MyTable")</li> 226 * <li>⚠️ 旧代码可能错误地匹配了不同大小写的标识符 (foo 匹配 FOO),新代码会正确拒绝</li> 227 * </ul> 228 * 229 * <p><strong>IdentifierRules 配置:</strong> 230 * <ul> 231 * <li>Unquoted: 保留原样 ({@link CaseFold#NONE}), 比较敏感 ({@link CaseCompare#SENSITIVE}) 232 * <li>Quoted: 保留原样 ({@link CaseFold#NONE}), 比较敏感 ({@link CaseCompare#SENSITIVE}) 233 * </ul> 234 */ 235 public static IdentifierRules forClickHouse() { 236 return new IdentifierRules( 237 CaseFold.NONE, // unquoted 保留原样 238 CaseCompare.SENSITIVE, // unquoted 比较敏感 239 CaseFold.NONE, // quoted 保留原样 240 CaseCompare.SENSITIVE // quoted 比较敏感 241 ); 242 } 243 244 /** 245 * Couchbase N1QL 标识符规则 246 * 247 * <p><strong>实际数据库行为(Couchbase N1QL):</strong> 248 * <ul> 249 * <li>Unquoted: 保留原样,比较敏感 (与 ClickHouse 相同) 250 * <li>Quoted: 保留原样,比较敏感 251 * </ul> 252 * 253 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 254 * <ul> 255 * <li>tableCollationCaseSensitive = {@code false} → ❌ <strong>INCOMPATIBLE</strong> (legacy folded to UPPER, new preserves case) 256 * <li>参见 {@link #forClickHouse()} 的详细说明 257 * </ul> 258 */ 259 public static IdentifierRules forCouchbase() { 260 return forClickHouse(); // 与 ClickHouse 相同 261 } 262 263 /** 264 * SQL Server / Azure SQL 标识符规则 265 * 266 * <p><strong>实际数据库行为(SQL Server 2019+):</strong> 267 * <ul> 268 * <li>Unquoted: 保留原样,比较由 collation 决定 (CREATE TABLE MyTable → stored as MyTable) 269 * <li>Quoted: 保留原样,比较由 collation 决定 (CREATE TABLE [MyTable] → stored as MyTable) 270 * <li>默认 collation (SQL_Latin1_General_CP1_CI_AS): 大小写不敏感 (MyTable=mytable=MYTABLE) 271 * <li>CS collation (SQL_Latin1_General_CP1_CS_AS): 大小写敏感 (MyTable!=mytable) 272 * </ul> 273 * 274 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 275 * <ul> 276 * <li>columnCollationCaseSensitive = {@code false} → ❌ <strong>INCOMPATIBLE</strong> (legacy folded to UPPER, new preserves case) 277 * <li>functionCollationCaseSensitive = {@code false} → ❌ <strong>INCOMPATIBLE</strong> (legacy folded to UPPER, new preserves case) 278 * <li>tableCollationCaseSensitive = {@code false} → ❌ <strong>INCOMPATIBLE</strong> (legacy folded to UPPER, new preserves case) 279 * <li>catalogCollationCaseSensitive = {@code false} → ❌ <strong>INCOMPATIBLE</strong> (legacy folded to UPPER, new preserves case) 280 * </ul> 281 * 282 * <p><strong>测试用例影响:</strong> 283 * <ul> 284 * <li>✅ 新规则正确:SQL Server 保留标识符原始大小写,使用 collation 进行比较</li> 285 * <li>⚠️ 如果旧测试期望 "MyTable" → "MYTABLE",则测试会失败</li> 286 * <li>⚠️ 应更新测试期望为保留原样 (如 "MyTable" 保持为 "MyTable")</li> 287 * <li>⚠️ 这是导致 dataflow 测试 processId 变化的根本原因!</li> 288 * <li>📝 参考: investigation_findings_2025_10_20.md</li> 289 * </ul> 290 * 291 * <p><strong>IdentifierRules 配置:</strong> 292 * <ul> 293 * <li>Unquoted: 不折叠 ({@link CaseFold#NONE}), 基于 collation 比较 ({@link CaseCompare#COLLATION_BASED}) 294 * <li>Quoted: 不折叠 ({@link CaseFold#NONE}), 基于 collation 比较 ({@link CaseCompare#COLLATION_BASED}) 295 * <li>需要配合 {@link CollatorProvider} 使用,默认使用 SQL_Latin1_General_CP1_CI_AS (大小写不敏感) 296 * </ul> 297 * 298 * <p><strong>注意:</strong>SQL Server 的大小写行为完全由 collation 决定,无法简单折叠。 299 */ 300 public static IdentifierRules forSQLServer() { 301 return new IdentifierRules( 302 CaseFold.UPPER, // unquoted 不折叠(保留原样) 303 CaseCompare.COLLATION_BASED, // unquoted 基于 collation 比较 304 CaseFold.NONE, // quoted 不折叠(保留原样) 305 CaseCompare.COLLATION_BASED // quoted 基于 collation 比较 306 ); 307 } 308 309 /** 310 * MySQL 标识符规则(table/schema names) 311 * 312 * <p><strong>实际数据库行为(MySQL 8.0+):</strong> 313 * <p>根据 {@code lower_case_table_names} 系统变量决定: 314 * <ul> 315 * <li>0 (Unix/Linux): 大小写敏感,保留原样 (CREATE TABLE MyTable → stored as MyTable, MyTable!=mytable) 316 * <li>1 (Windows): 存储为小写,比较不敏感 (CREATE TABLE MyTable → stored as mytable, MyTable=mytable=MYTABLE) 317 * <li>2 (macOS): 存储保留原样,比较不敏感 (CREATE TABLE MyTable → stored as MyTable, MyTable=mytable=MYTABLE) 318 * </ul> 319 * 320 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 321 * <ul> 322 * <li>columnCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new doesn't fold or folds to LOWER) 323 * <li>functionCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new doesn't fold) 324 * <li>tableCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new behavior depends on lower_case_table_names) 325 * <li>catalogCollationCaseSensitive = {@code true} → ⚠️ <strong>PARTIAL</strong> (legacy preserved case, new is insensitive) 326 * </ul> 327 * 328 * <p><strong>测试用例影响:</strong> 329 * <ul> 330 * <li>✅ 新规则正确:MySQL 的行为确实依赖 lower_case_table_names 设置</li> 331 * <li>⚠️ 如果旧测试期望 "MyTable" → "MYTABLE",则在模式 1/2 下测试会失败</li> 332 * <li>⚠️ 模式 0: 应期望保留原样 (如 "MyTable" 保持为 "MyTable"),区分大小写</li> 333 * <li>⚠️ 模式 1: 应期望小写 (如 "MyTable" → "mytable"),不区分大小写</li> 334 * <li>⚠️ 模式 2: 应期望保留原样 (如 "MyTable" 保持为 "MyTable"),不区分大小写</li> 335 * </ul> 336 * 337 * <p><strong>IdentifierRules 配置:</strong> 338 * <ul> 339 * <li>模式 0: 不折叠 ({@link CaseFold#NONE}), 敏感 ({@link CaseCompare#SENSITIVE}) 340 * <li>模式 1: 折叠为小写 ({@link CaseFold#LOWER}), 不敏感 ({@link CaseCompare#INSENSITIVE}) 341 * <li>模式 2: 不折叠 ({@link CaseFold#NONE}), 不敏感 ({@link CaseCompare#INSENSITIVE}) 342 * <li>Quoted: 保留原样,但也不敏感 (MySQL 特殊行为) 343 * </ul> 344 * 345 * @param lowerCaseTableNames {@code lower_case_table_names} 值(0, 1, 2) 346 */ 347 public static IdentifierRules forMySQL(int lowerCaseTableNames) { 348 if (lowerCaseTableNames == 0) { 349 // Unix/Linux: 大小写敏感 350 return new IdentifierRules( 351 CaseFold.NONE, 352 CaseCompare.SENSITIVE, 353 CaseFold.NONE, 354 CaseCompare.SENSITIVE 355 ); 356 } else { 357 // Windows/macOS: 比较不敏感 358 CaseFold fold = (lowerCaseTableNames == 1) ? CaseFold.LOWER : CaseFold.NONE; 359 return new IdentifierRules( 360 fold, // 根据设置决定是否折叠 361 CaseCompare.INSENSITIVE, // 比较不敏感 362 CaseFold.NONE, // quoted 保留原样 363 CaseCompare.INSENSITIVE // quoted 也不敏感(MySQL 特性) 364 ); 365 } 366 } 367 368 /** 369 * MySQL 列名规则(始终大小写不敏感) 370 * 371 * <p><strong>实际数据库行为(MySQL 8.0+):</strong> 372 * <ul> 373 * <li>列名始终大小写不敏感,不受 lower_case_table_names 影响 374 * <li>存储时保留原样,比较时不敏感 (SELECT MyColumn → stored as MyColumn, MyColumn=mycolumn) 375 * </ul> 376 * 377 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 378 * <ul> 379 * <li>columnCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new preserves case but is INSENSITIVE) 380 * </ul> 381 */ 382 public static IdentifierRules forMySQLColumn() { 383 return new IdentifierRules( 384 CaseFold.NONE, // 不折叠(但比较时不敏感) 385 CaseCompare.INSENSITIVE, 386 CaseFold.NONE, 387 CaseCompare.INSENSITIVE 388 ); 389 } 390 391 /** 392 * MySQL 函数名规则(始终大小写不敏感) 393 * 394 * <p><strong>实际数据库行为(MySQL 8.0+):</strong> 395 * <ul> 396 * <li>函数名/存储过程名始终大小写不敏感 397 * <li>存储时保留原样,比较时不敏感 (与列名相同) 398 * </ul> 399 * 400 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 401 * <ul> 402 * <li>functionCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new preserves case but is INSENSITIVE) 403 * </ul> 404 */ 405 public static IdentifierRules forMySQLRoutine() { 406 return forMySQLColumn(); 407 } 408 409 /** 410 * BigQuery 表名规则(大小写敏感) 411 * 412 * <p><strong>实际数据库行为(BigQuery Standard SQL):</strong> 413 * <ul> 414 * <li>Unquoted: 保留原样,比较敏感 (CREATE TABLE MyTable → stored as MyTable, MyTable!=mytable) 415 * <li>Quoted: 保留原样,比较敏感 (CREATE TABLE `MyTable` → stored as MyTable, MyTable!=mytable) 416 * <li>表名/dataset名/project名都是大小写敏感的</li> 417 * </ul> 418 * 419 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 420 * <ul> 421 * <li>tableCollationCaseSensitive = {@code false} → ❌ <strong>INCOMPATIBLE</strong> (legacy folded to UPPER, new preserves case and is SENSITIVE) 422 * <li>catalogCollationCaseSensitive = {@code false} → ❌ <strong>INCOMPATIBLE</strong> (legacy folded to UPPER, new preserves case and is SENSITIVE) 423 * </ul> 424 * 425 * <p><strong>测试用例影响:</strong> 426 * <ul> 427 * <li>✅ 新规则正确:BigQuery 表名确实是大小写敏感的</li> 428 * <li>⚠️ 如果旧测试期望 "MyTable" → "MYTABLE",则测试会失败</li> 429 * <li>⚠️ 应更新测试期望为保留原样 (如 "MyTable" 保持为 "MyTable")</li> 430 * <li>⚠️ 旧代码可能错误地匹配了不同大小写的表名,新代码会正确拒绝</li> 431 * </ul> 432 */ 433 public static IdentifierRules forBigQueryTable() { 434 return new IdentifierRules( 435 CaseFold.NONE, 436 CaseCompare.SENSITIVE, // 表名大小写敏感 437 CaseFold.NONE, 438 CaseCompare.SENSITIVE 439 ); 440 } 441 442 /** 443 * BigQuery 列名规则(大小写不敏感) 444 * 445 * <p><strong>实际数据库行为(BigQuery Standard SQL):</strong> 446 * <ul> 447 * <li>Unquoted: 保留原样,比较不敏感 (SELECT MyColumn → stored as MyColumn, MyColumn=mycolumn=MYCOLUMN) 448 * <li>Quoted: 保留原样,比较不敏感 (SELECT `MyColumn` → MyColumn=mycolumn) 449 * <li>列名是大小写不敏感的(与表名不同)</li> 450 * </ul> 451 * 452 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 453 * <ul> 454 * <li>columnCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new preserves case but is INSENSITIVE) 455 * </ul> 456 * 457 * <p><strong>测试用例影响:</strong> 458 * <ul> 459 * <li>✅ 新规则正确:BigQuery 列名确实是大小写不敏感的</li> 460 * <li>⚠️ 如果旧测试期望 "MyColumn" → "MYCOLUMN",则测试会失败</li> 461 * <li>⚠️ 应更新测试期望为保留原样 (如 "MyColumn" 保持为 "MyColumn")</li> 462 * <li>✅ 但比较时应忽略大小写 (MyColumn = mycolumn = MYCOLUMN)</li> 463 * </ul> 464 */ 465 public static IdentifierRules forBigQueryColumn() { 466 return new IdentifierRules( 467 CaseFold.NONE, 468 CaseCompare.INSENSITIVE, // 列名大小写不敏感 469 CaseFold.NONE, 470 CaseCompare.INSENSITIVE 471 ); 472 } 473 474 /** 475 * DB2 / Netezza / Exasol 标识符规则(与 Oracle 相同) 476 * 477 * <p><strong>实际数据库行为(DB2 11+):</strong> 478 * <ul> 479 * <li>Unquoted: 折叠为大写,比较不敏感 (与 Oracle 相同) 480 * <li>Quoted: 保留原样,比较敏感 481 * </ul> 482 * 483 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 484 * <ul> 485 * <li>DB2 tableCollationCaseSensitive = {@code true} → ❌ <strong>INCOMPATIBLE</strong> (legacy preserved case, new folds to UPPER) 486 * <li>DB2 catalogCollationCaseSensitive = {@code true} → ❌ <strong>INCOMPATIBLE</strong> (legacy preserved case, new folds to UPPER) 487 * <li>参见 {@link #forOracle()} 的详细说明 488 * </ul> 489 */ 490 public static IdentifierRules forDB2() { 491 return forOracle(); 492 } 493 494 /** 495 * Snowflake 标识符规则(与 Oracle 相同) 496 * 497 * <p><strong>实际数据库行为(Snowflake):</strong> 498 * <ul> 499 * <li>Unquoted: 折叠为大写,比较不敏感 (与 Oracle 相同) 500 * <li>Quoted: 保留原样,比较敏感 501 * </ul> 502 * 503 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 504 * <ul> 505 * <li>tableCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (both fold to UPPER, COMPATIBLE) 506 * <li>columnCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (both fold to UPPER, COMPATIBLE) 507 * <li>参见 {@link #forOracle()} 的详细说明 508 * </ul> 509 */ 510 public static IdentifierRules forSnowflake() { 511 return forOracle(); 512 } 513 514 /** 515 * SAP HANA 标识符规则(与 Oracle 相同) 516 * 517 * <p><strong>实际数据库行为(SAP HANA):</strong> 518 * <ul> 519 * <li>Unquoted: 折叠为大写,比较不敏感 (与 Oracle 相同) 520 * <li>Quoted: 保留原样,比较敏感 521 * </ul> 522 * 523 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 524 * <ul> 525 * <li>tableCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (both fold to UPPER, COMPATIBLE) 526 * <li>参见 {@link #forOracle()} 的详细说明 527 * </ul> 528 */ 529 public static IdentifierRules forHANA() { 530 return forOracle(); 531 } 532 533 /** 534 * Presto / Trino 标识符规则 535 * 536 * <p><strong>实际数据库行为(Presto/Trino):</strong> 537 * <ul> 538 * <li>Unquoted: 折叠为小写,比较不敏感 (CREATE TABLE MyTable → stored as mytable) 539 * <li>Quoted: 保留原样,但与 unquoted 规则一致(比较时仍不敏感) 540 * </ul> 541 * 542 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 543 * <ul> 544 * <li>tableCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new folds to LOWER) 545 * <li>columnCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new folds to LOWER) 546 * </ul> 547 * 548 * <p><strong>测试用例影响:</strong> 549 * <ul> 550 * <li>✅ 新规则正确:Presto/Trino 折叠为小写,quoted 标识符与 unquoted 规则一致</li> 551 * <li>⚠️ 如果旧测试期望 "MyTable" → "MYTABLE",则测试会失败</li> 552 * <li>⚠️ 应更新测试期望为小写 (如 "MyTable" → "mytable")</li> 553 * </ul> 554 * 555 * <p><strong>IdentifierRules 配置:</strong> 556 * <ul> 557 * <li>Unquoted: 折叠为小写 ({@link CaseFold#LOWER}), 不敏感 ({@link CaseCompare#INSENSITIVE}) 558 * <li>Quoted: 保留原样 ({@link CaseFold#NONE}), 与 unquoted 一致 ({@link CaseCompare#SAME_AS_UNQUOTED}) 559 * </ul> 560 */ 561 public static IdentifierRules forPresto() { 562 return new IdentifierRules( 563 CaseFold.LOWER, 564 CaseCompare.INSENSITIVE, 565 CaseFold.NONE, 566 CaseCompare.SAME_AS_UNQUOTED // Presto 特性:quoted 与 unquoted 一致 567 ); 568 } 569 570 /** 571 * Vertica 标识符规则(与 Presto 相同) 572 * 573 * <p><strong>实际数据库行为(Vertica):</strong> 574 * <ul> 575 * <li>Unquoted: 折叠为小写,比较不敏感 (与 Presto 相同) 576 * <li>Quoted: 保留原样,但与 unquoted 规则一致 577 * </ul> 578 * 579 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 580 * <ul> 581 * <li>tableCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new folds to LOWER) 582 * <li>参见 {@link #forPresto()} 的详细说明 583 * </ul> 584 */ 585 public static IdentifierRules forVertica() { 586 return forPresto(); 587 } 588 589 /** 590 * Hive / SparkSQL / Impala 标识符规则(与 PostgreSQL 相同) 591 * 592 * <p><strong>实际数据库行为(Hive 3+, SparkSQL 3+):</strong> 593 * <ul> 594 * <li>Unquoted: 折叠为小写,比较不敏感 (与 PostgreSQL 相同) 595 * <li>Quoted: 保留原样,比较敏感 596 * </ul> 597 * 598 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 599 * <ul> 600 * <li>tableCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new folds to LOWER) 601 * <li>参见 {@link #forPostgreSQL()} 的详细说明 602 * </ul> 603 */ 604 public static IdentifierRules forHive() { 605 return forPostgreSQL(); 606 } 607 608 /** 609 * Teradata 标识符规则(与 PostgreSQL 相同) 610 * 611 * <p><strong>实际数据库行为(Teradata 16+):</strong> 612 * <ul> 613 * <li>Unquoted: 折叠为小写,比较不敏感 (与 PostgreSQL 相同) 614 * <li>Quoted: 保留原样,比较敏感 615 * </ul> 616 * 617 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 618 * <ul> 619 * <li>tableCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new folds to LOWER) 620 * <li>参见 {@link #forPostgreSQL()} 的详细说明 621 * </ul> 622 */ 623 public static IdentifierRules forTeradata() { 624 return forPostgreSQL(); 625 } 626 627 /** 628 * Athena 标识符规则(与 Presto 相同) 629 * 630 * <p><strong>实际数据库行为(AWS Athena):</strong> 631 * <ul> 632 * <li>Unquoted: 折叠为小写,比较不敏感 (与 Presto 相同,基于 Trino/Presto) 633 * <li>Quoted: 保留原样,但与 unquoted 规则一致 634 * </ul> 635 * 636 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 637 * <ul> 638 * <li>tableCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new folds to LOWER) 639 * <li>参见 {@link #forPresto()} 的详细说明 640 * </ul> 641 */ 642 public static IdentifierRules forAthena() { 643 return forPresto(); 644 } 645 646 /** 647 * GaussDB 标识符规则(与 PostgreSQL 相同) 648 * 649 * <p><strong>实际数据库行为(华为 GaussDB):</strong> 650 * <ul> 651 * <li>Unquoted: 折叠为小写,比较不敏感 (与 PostgreSQL 相同,基于 PostgreSQL) 652 * <li>Quoted: 保留原样,比较敏感 653 * </ul> 654 * 655 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 656 * <ul> 657 * <li>tableCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new folds to LOWER) 658 * <li>参见 {@link #forPostgreSQL()} 的详细说明 659 * </ul> 660 */ 661 public static IdentifierRules forGaussDB() { 662 return forPostgreSQL(); 663 } 664 665 /** 666 * Databricks 标识符规则(与 Hive 相同) 667 * 668 * <p><strong>实际数据库行为(Databricks SQL):</strong> 669 * <ul> 670 * <li>Unquoted: 折叠为小写,比较不敏感 (与 Hive/SparkSQL 相同) 671 * <li>Quoted: 保留原样,比较敏感 672 * </ul> 673 * 674 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 675 * <ul> 676 * <li>tableCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new folds to LOWER) 677 * <li>参见 {@link #forHive()} 和 {@link #forPostgreSQL()} 的详细说明 678 * </ul> 679 */ 680 public static IdentifierRules forDatabricks() { 681 return forHive(); 682 } 683 684 /** 685 * 通用规则(默认:与 PostgreSQL 相同) 686 * 687 * <p><strong>说明:</strong> 688 * <ul> 689 * <li>当数据库类型未知或不在支持列表时使用此规则 690 * <li>默认采用 PostgreSQL 的行为(折叠为小写,比较不敏感) 691 * </ul> 692 * 693 * <p><strong>与 Legacy TSQLEnv 兼容性:</strong> 694 * <ul> 695 * <li>defaultCollationCaseSensitive = {@code false} → ⚠️ <strong>PARTIAL</strong> (legacy folded to UPPER, new folds to LOWER) 696 * <li>参见 {@link #forPostgreSQL()} 的详细说明 697 * </ul> 698 */ 699 public static IdentifierRules forGeneric() { 700 return forPostgreSQL(); 701 } 702 703 // ===== toString 方法(用于调试) ===== 704 705 @Override 706 public String toString() { 707 return String.format("IdentifierRules{unquoted=%s/%s, quoted=%s/%s}", 708 unquotedFold, unquotedCompare, quotedFold, quotedCompare); 709 } 710}