001package gudusoft.gsqlparser.powerquery;
002
003import gudusoft.gsqlparser.EDbVendor;
004
005import java.util.Arrays;
006import java.util.Collections;
007import java.util.HashMap;
008import java.util.List;
009import java.util.Locale;
010import java.util.Map;
011
012/**
013 * Maps Power Query M connector-function names to their underlying
014 * {@link EDbVendor}, and describes the navigation hierarchy each connector
015 * exposes (Database / Schema / Table, Project / Dataset / Table, etc.).
016 *
017 * <p>This catalog is the single source of truth for "which SQL dialect
018 * does this M step belong to?"  Every new connector we support must be
019 * registered here, ideally with a quick corpus sample that demonstrates
020 * real-world use.
021 *
022 * <p>Aliases are intentionally case-insensitive because M is itself
023 * case-sensitive for identifier lookup, but different corpus sources
024 * occasionally normalize casing.
025 */
026public final class ConnectorCatalog {
027
028    private ConnectorCatalog() {}
029
030    private static final Map<String, EDbVendor> FUNCTION_TO_VENDOR = new HashMap<>();
031    private static final Map<EDbVendor, List<String>> VENDOR_HIERARCHY = new HashMap<>();
032
033    static {
034        // Snowflake
035        register("Snowflake.Databases",        EDbVendor.dbvsnowflake);
036        register("Snowflake.Database",         EDbVendor.dbvsnowflake);
037
038        // Microsoft SQL Server (and Azure SQL via same connector)
039        register("Sql.Database",               EDbVendor.dbvmssql);
040        register("Sql.Databases",              EDbVendor.dbvmssql);
041
042        // Oracle
043        register("Oracle.Database",            EDbVendor.dbvoracle);
044
045        // PostgreSQL
046        register("PostgreSQL.Database",        EDbVendor.dbvpostgresql);
047        register("PostgreSQL.Databases",       EDbVendor.dbvpostgresql);
048
049        // MySQL
050        register("MySQL.Database",             EDbVendor.dbvmysql);
051        register("MySQL.Databases",            EDbVendor.dbvmysql);
052
053        // BigQuery
054        register("GoogleBigQuery.Database",    EDbVendor.dbvbigquery);
055        register("GoogleBigQuery.Databases",   EDbVendor.dbvbigquery);
056        register("BigQuery.Database",          EDbVendor.dbvbigquery);
057
058        // Amazon Redshift (common enough to ship Tier 1)
059        register("AmazonRedshift.Database",    EDbVendor.dbvredshift);
060        register("AmazonRedshift.Databases",   EDbVendor.dbvredshift);
061
062        // Databricks
063        register("Databricks.Catalogs",        EDbVendor.dbvdatabricks);
064        register("Databricks.Database",        EDbVendor.dbvdatabricks);
065
066        // Hive via ODBC/custom connectors
067        register("Hive.Database",              EDbVendor.dbvhive);
068
069        // Hierarchies per vendor (outermost Kind → innermost Kind).
070        VENDOR_HIERARCHY.put(EDbVendor.dbvsnowflake,
071                Arrays.asList("Database", "Schema", "Table", "View"));
072        VENDOR_HIERARCHY.put(EDbVendor.dbvmssql,
073                Arrays.asList("Database", "Schema", "Table", "View"));
074        VENDOR_HIERARCHY.put(EDbVendor.dbvoracle,
075                Arrays.asList("Schema", "Table", "View"));
076        VENDOR_HIERARCHY.put(EDbVendor.dbvpostgresql,
077                Arrays.asList("Database", "Schema", "Table", "View"));
078        VENDOR_HIERARCHY.put(EDbVendor.dbvmysql,
079                Arrays.asList("Database", "Table", "View"));
080        VENDOR_HIERARCHY.put(EDbVendor.dbvbigquery,
081                Arrays.asList("Project", "Dataset", "Table", "View"));
082        VENDOR_HIERARCHY.put(EDbVendor.dbvredshift,
083                Arrays.asList("Database", "Schema", "Table", "View"));
084        VENDOR_HIERARCHY.put(EDbVendor.dbvdatabricks,
085                Arrays.asList("Catalog", "Database", "Table", "View"));
086        VENDOR_HIERARCHY.put(EDbVendor.dbvhive,
087                Arrays.asList("Database", "Table", "View"));
088    }
089
090    private static void register(String functionName, EDbVendor vendor) {
091        FUNCTION_TO_VENDOR.put(functionName.toLowerCase(Locale.ROOT), vendor);
092    }
093
094    /**
095     * Look up the vendor for a qualified function name such as
096     * {@code "Snowflake.Databases"}.  Returns {@code null} when the
097     * connector is unknown — callers must decide whether to fall back to
098     * a user-supplied {@code --inner-vendor} override or skip the step.
099     */
100    public static EDbVendor vendorFor(String qualifiedFunctionName) {
101        if (qualifiedFunctionName == null) return null;
102        return FUNCTION_TO_VENDOR.get(qualifiedFunctionName.toLowerCase(Locale.ROOT));
103    }
104
105    /**
106     * Canonical navigation-segment hierarchy for a given vendor.  Used to
107     * validate a {@code source{[Name=…,Kind=…]}[Data]} chain's shape and
108     * to drive synthetic SQL construction in
109     * {@code TPowerQueryAnalyzer}.
110     */
111    public static List<String> expectedHierarchy(EDbVendor vendor) {
112        List<String> h = VENDOR_HIERARCHY.get(vendor);
113        return h == null ? Collections.<String>emptyList() : h;
114    }
115
116    public static boolean isKnownConnector(String qualifiedFunctionName) {
117        return vendorFor(qualifiedFunctionName) != null;
118    }
119}