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}