호출 예시

SELECT * FROM DESCRIBE('테이블명');
SELECT * FROM DESCRIBE('스키마명.테이블명');

정의

-- ---------------------------------------------------------------------------------------------
-- DESCRIBE
-- ---------------------------------------------------------------------------------------------
DROP FUNCTION IF EXISTS DESCRIBE;
CREATE FUNCTION DESCRIBE(table_identifier TEXT)
RETURNS TABLE(
    _column_name TEXT, 
    _data_type TEXT, 
    _null TEXT, 
    _default TEXT,
    _constraints TEXT,
    _comment TEXT
) AS $$
DECLARE
    _schema_name TEXT;
    _table_name  TEXT;
    _table_oid    OID;
BEGIN
    table_identifier = LOWER(table_identifier);
    
    IF strpos(table_identifier, '.') > 0 THEN
        _schema_name := split_part(table_identifier, '.', 1);
        _table_name  := split_part(table_identifier, '.', 2);
    ELSE
        _schema_name := 'public';
        _table_name  := table_identifier;
    END IF;
    
    -- 테이블의 OID 획득
    SELECT oid INTO _table_oid
    FROM pg_catalog.pg_class
    WHERE relname = _table_name
    AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = _schema_name);

    RETURN QUERY
    WITH RECURSIVE 
    WCOL AS(
        SELECT 
            column_name::TEXT AS _column_name, 
            CASE 
                WHEN (data_type::TEXT = 'character') THEN FORMAT('char(%s)', character_maximum_length)
                WHEN (data_type::TEXT = 'character varying') THEN FORMAT('varchar(%s)', character_maximum_length)
                WHEN (data_type::TEXT = 'timestamp with time zone') THEN 'timestamptz'
                WHEN (data_type::TEXT = 'integer' AND column_default::TEXT LIKE 'nextval%') THEN 'serial'
                ELSE data_type::TEXT
            END
                AS _data_type, 
            CASE WHEN (is_nullable::TEXT) = 'YES' THEN 'NULL' ELSE 'NOT NULL' END AS _null, 
            column_default::TEXT AS _default,
            d.description AS _comment
        FROM 
            information_schema.columns c
        LEFT JOIN 
            pg_catalog.pg_description d 
                ON _table_oid = d.objoid 
                AND c.ordinal_position = d.objsubid
        WHERE 
            table_schema = _schema_name AND 
            table_name = _table_name
        ORDER BY ordinal_position ASC
    ),
    WCON AS (
        SELECT c.column_name AS _column_name, c.data_type AS _data_type, STRING_AGG(DISTINCT constraint_type::TEXT, ', ') AS _constraints
        FROM information_schema.table_constraints tc 
        JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_name) 
        JOIN information_schema.columns AS c ON c.table_schema = tc.constraint_schema
          AND tc.table_name = c.table_name AND ccu.column_name = c.column_name
        WHERE tc.table_name = _table_name
        GROUP BY (c.column_name, c.data_type)
    )
    SELECT WCOL._column_name
         , WCOL._data_type
         , WCOL._null
         , WCOL._default
         , WCON._constraints
         , WCOL._comment
    FROM WCOL
         LEFT JOIN WCON
         USING (_column_name)
    ;
END;
$$ LANGUAGE plpgsql;