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}