001package gudusoft.gsqlparser.ir.builder.postgresql;
002
003import java.util.Collections;
004import java.util.HashSet;
005import java.util.Set;
006
007/**
008 * Classifies PostgreSQL built-in functions and pg_catalog system functions
009 * that should be treated as external dependencies rather than user-defined calls.
010 */
011public final class PostgresqlExternalDepClassifier {
012
013    private PostgresqlExternalDepClassifier() {}
014
015    private static final Set<String> BUILTIN_FUNCTIONS;
016    private static final Set<String> AGGREGATE_FUNCTIONS;
017    private static final Set<String> SYSTEM_FUNCTIONS;
018    private static final Set<String> PG_CATALOG_PREFIXES;
019    private static final Set<String> ALL_BUILTINS;
020
021    static {
022        BUILTIN_FUNCTIONS = unmodifiableSet(
023                // String
024                "LENGTH", "CHAR_LENGTH", "LOWER", "UPPER", "TRIM", "LTRIM", "RTRIM",
025                "SUBSTRING", "SUBSTR", "REPLACE", "TRANSLATE", "CONCAT", "CONCAT_WS",
026                "LEFT", "RIGHT", "LPAD", "RPAD", "REVERSE", "REPEAT", "POSITION",
027                "STRPOS", "INITCAP", "ASCII", "CHR", "ENCODE", "DECODE",
028                "MD5", "FORMAT", "QUOTE_IDENT", "QUOTE_LITERAL", "QUOTE_NULLABLE",
029                "REGEXP_MATCH", "REGEXP_MATCHES", "REGEXP_REPLACE", "REGEXP_SPLIT_TO_TABLE",
030                "REGEXP_SPLIT_TO_ARRAY", "SPLIT_PART",
031                // Numeric
032                "ABS", "CEIL", "CEILING", "FLOOR", "ROUND", "TRUNC", "MOD",
033                "POWER", "SQRT", "CBRT", "EXP", "LN", "LOG", "PI",
034                "SIGN", "RANDOM", "SETSEED", "WIDTH_BUCKET",
035                "SIN", "COS", "TAN", "ASIN", "ACOS", "ATAN", "ATAN2",
036                // Date/Time
037                "NOW", "CURRENT_TIMESTAMP", "CURRENT_DATE", "CURRENT_TIME",
038                "LOCALTIME", "LOCALTIMESTAMP", "CLOCK_TIMESTAMP", "STATEMENT_TIMESTAMP",
039                "TRANSACTION_TIMESTAMP", "TIMEOFDAY",
040                "AGE", "DATE_PART", "DATE_TRUNC", "EXTRACT", "MAKE_DATE",
041                "MAKE_TIME", "MAKE_TIMESTAMP", "MAKE_TIMESTAMPTZ",
042                "MAKE_INTERVAL", "TO_TIMESTAMP", "TO_DATE", "TO_CHAR",
043                // Type conversion
044                "CAST", "TO_NUMBER", "TO_CHAR", "TO_DATE", "TO_TIMESTAMP",
045                // Conditional
046                "COALESCE", "NULLIF", "GREATEST", "LEAST",
047                // Array
048                "ARRAY_AGG", "ARRAY_LENGTH", "ARRAY_DIMS", "ARRAY_LOWER", "ARRAY_UPPER",
049                "ARRAY_APPEND", "ARRAY_CAT", "ARRAY_PREPEND", "ARRAY_REMOVE",
050                "ARRAY_REPLACE", "ARRAY_TO_STRING", "STRING_TO_ARRAY", "UNNEST",
051                // JSON
052                "JSON_BUILD_OBJECT", "JSON_BUILD_ARRAY", "JSON_OBJECT",
053                "JSON_AGG", "JSON_ARRAY_AGG", "JSONB_BUILD_OBJECT", "JSONB_BUILD_ARRAY",
054                "ROW_TO_JSON", "TO_JSON", "TO_JSONB", "JSON_EXTRACT_PATH",
055                "JSONB_EXTRACT_PATH", "JSON_EXTRACT_PATH_TEXT", "JSONB_EXTRACT_PATH_TEXT",
056                "JSONB_SET", "JSONB_INSERT", "JSONB_PRETTY",
057                // System info
058                "CURRENT_USER", "SESSION_USER", "CURRENT_SCHEMA", "CURRENT_DATABASE",
059                "CURRENT_CATALOG", "VERSION", "PG_BACKEND_PID", "PG_POSTMASTER_START_TIME",
060                // Sequence
061                "NEXTVAL", "CURRVAL", "SETVAL", "LASTVAL",
062                // Misc
063                "GENERATE_SERIES", "GENERATE_SUBSCRIPTS",
064                "COALESCE", "ROW_NUMBER", "RANK", "DENSE_RANK",
065                "EXISTS", "NOT_EXISTS",
066                "PG_TYPEOF", "PG_COLUMN_SIZE", "PG_TABLE_SIZE",
067                "TXID_CURRENT", "TXID_SNAPSHOT_XIP"
068        );
069
070        AGGREGATE_FUNCTIONS = unmodifiableSet(
071                "AVG", "COUNT", "MAX", "MIN", "SUM",
072                "BOOL_AND", "BOOL_OR", "BIT_AND", "BIT_OR",
073                "EVERY", "ARRAY_AGG", "STRING_AGG", "JSON_AGG", "JSONB_AGG",
074                "JSON_OBJECT_AGG", "JSONB_OBJECT_AGG",
075                "PERCENTILE_CONT", "PERCENTILE_DISC",
076                "CORR", "COVAR_POP", "COVAR_SAMP",
077                "REGR_AVGX", "REGR_AVGY", "REGR_COUNT",
078                "REGR_INTERCEPT", "REGR_R2", "REGR_SLOPE",
079                "REGR_SXX", "REGR_SXY", "REGR_SYY",
080                "STDDEV", "STDDEV_POP", "STDDEV_SAMP",
081                "VARIANCE", "VAR_POP", "VAR_SAMP",
082                "XMLAGG"
083        );
084
085        SYSTEM_FUNCTIONS = unmodifiableSet(
086                "RAISE", "RAISE_EXCEPTION",
087                "PG_NOTIFY", "PG_SLEEP", "PG_ADVISORY_LOCK",
088                "PG_ADVISORY_UNLOCK", "PG_TRY_ADVISORY_LOCK",
089                "SET_CONFIG", "CURRENT_SETTING"
090        );
091
092        PG_CATALOG_PREFIXES = unmodifiableSet(
093                "PG_CATALOG", "INFORMATION_SCHEMA", "PG_TOAST"
094        );
095
096        Set<String> all = new HashSet<String>();
097        all.addAll(BUILTIN_FUNCTIONS);
098        all.addAll(AGGREGATE_FUNCTIONS);
099        all.addAll(SYSTEM_FUNCTIONS);
100        ALL_BUILTINS = Collections.unmodifiableSet(all);
101    }
102
103    public static boolean isExternal(String name) {
104        if (name == null || name.isEmpty()) return false;
105        String upper = name.toUpperCase().trim();
106
107        int dot = upper.indexOf('.');
108        if (dot > 0) {
109            String prefix = upper.substring(0, dot);
110            if (PG_CATALOG_PREFIXES.contains(prefix)) return true;
111        }
112
113        String objectName = dot >= 0 ? upper.substring(dot + 1) : upper;
114        return ALL_BUILTINS.contains(objectName);
115    }
116
117    public static String classify(String name) {
118        if (name == null) return "BUILTIN_FUNCTION";
119        String upper = name.toUpperCase().trim();
120
121        int dot = upper.indexOf('.');
122        if (dot > 0) {
123            String prefix = upper.substring(0, dot);
124            if (PG_CATALOG_PREFIXES.contains(prefix)) return "SYSTEM_CATALOG";
125        }
126
127        String objectName = dot >= 0 ? upper.substring(dot + 1) : upper;
128        if (AGGREGATE_FUNCTIONS.contains(objectName)) return "AGGREGATE_FUNCTION";
129        if (SYSTEM_FUNCTIONS.contains(objectName)) return "SYSTEM_FUNCTION";
130        return "BUILTIN_FUNCTION";
131    }
132
133    private static Set<String> unmodifiableSet(String... items) {
134        Set<String> set = new HashSet<String>();
135        for (String item : items) set.add(item);
136        return Collections.unmodifiableSet(set);
137    }
138}