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}