SELECT
    c.table_name,
    c.column_name,
	CASE
		WHEN e.data_type IS NULL THEN c.data_type
		ELSE e.data_type || '[]'
	END,
    c.column_default,
    c.character_maximum_length,
    c.numeric_precision,
    c.numeric_scale,
    c.is_nullable
FROM (
        SELECT
            current_database()::information_schema.sql_identifier AS table_catalog,
            nc.nspname::information_schema.sql_identifier AS table_schema,
            c.relname::information_schema.sql_identifier AS table_name,
            a.attname::information_schema.sql_identifier AS column_name,
            pg_get_expr(ad.adbin, ad.adrelid)::information_schema.character_data AS column_default,
            CASE
                WHEN a.attnotnull OR t.typtype = 'd'::"char" AND t.typnotnull THEN 'NO'::text
                ELSE 'YES'::text
            END::information_schema.yes_or_no AS is_nullable,
            CASE
                WHEN t.typtype = 'd'::"char" THEN
                CASE
                    WHEN bt.typelem <> 0::oid AND bt.typlen = '-1'::integer THEN 'ARRAY'::text
                    WHEN nbt.nspname = 'pg_catalog'::name THEN format_type(t.typbasetype, NULL::integer)
                    ELSE 'USER-DEFINED'::text
                END
                ELSE
                CASE
                    WHEN t.typelem <> 0::oid AND t.typlen = '-1'::integer THEN 'ARRAY'::text
                    WHEN nt.nspname = 'pg_catalog'::name THEN format_type(a.atttypid, NULL::integer)
                    ELSE 'USER-DEFINED'::text
                END
            END::information_schema.character_data AS data_type,
            information_schema._pg_char_max_length(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS character_maximum_length,
            information_schema._pg_numeric_precision(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS numeric_precision,
            information_schema._pg_numeric_scale(information_schema._pg_truetypid(a.*, t.*), information_schema._pg_truetypmod(a.*, t.*))::information_schema.cardinal_number AS numeric_scale,
            a.attnum::information_schema.sql_identifier AS dtd_identifier
            FROM pg_attribute a
                LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
                JOIN (pg_class c
                JOIN pg_namespace nc ON c.relnamespace = nc.oid) ON a.attrelid = c.oid
                JOIN (pg_type t
                JOIN pg_namespace nt ON t.typnamespace = nt.oid) ON a.atttypid = t.oid
                LEFT JOIN (pg_type bt
                JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid) ON t.typtype = 'd'::"char" AND t.typbasetype = bt.oid
            WHERE NOT pg_is_other_temp_schema(nc.oid) AND a.attnum > 0 AND NOT a.attisdropped AND (c.relkind = ANY (ARRAY['r'::"char", 'v'::"char", 'f'::"char", 'p'::"char"]))
            AND (pg_has_role(c.relowner, 'USAGE'::text) OR has_column_privilege(c.oid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'::text))) AS c
LEFT JOIN (
    SELECT
        current_database()::information_schema.sql_identifier AS object_catalog,
        n.nspname::information_schema.sql_identifier AS object_schema,
        x.objname AS object_name,
        x.objtype::information_schema.character_data AS object_type,
        x.objdtdid::information_schema.sql_identifier AS collection_type_identifier,
        CASE
            WHEN nbt.nspname = 'pg_catalog'::name THEN format_type(bt.oid, NULL::integer)
            ELSE 'USER-DEFINED'::text
        END::information_schema.character_data AS data_type
    FROM
        pg_namespace n,
        pg_type at,
        pg_namespace nbt,
        pg_type bt,
        ( SELECT
            c.relnamespace,
            c.relname::information_schema.sql_identifier AS relname,
            CASE
                WHEN c.relkind = 'c'::"char" THEN 'USER-DEFINED TYPE'::text
                ELSE 'TABLE'::text
            END AS "case",
            a.attnum,
            a.atttypid
          FROM pg_class c,
            pg_attribute a
          WHERE c.oid = a.attrelid
          AND (c.relkind = ANY (ARRAY['r'::"char", 'v'::"char", 'f'::"char", 'c'::"char", 'p'::"char"]))
          AND a.attnum > 0 AND NOT a.attisdropped) x(objschema, objname, objtype, objdtdid, objtypeid)
    WHERE n.oid = x.objschema AND at.oid = x.objtypeid AND at.typelem <> 0::oid AND at.typlen = '-1'::integer AND at.typelem = bt.oid AND nbt.oid = bt.typnamespace
    AND ((n.nspname, x.objname::text, x.objtype, x.objdtdid::information_schema.sql_identifier::text)
    IN (
        SELECT data_type_privileges.object_schema,
            data_type_privileges.object_name,
            data_type_privileges.object_type,
            data_type_privileges.dtd_identifier
        FROM (
            SELECT
                current_database()::information_schema.sql_identifier AS object_catalog,
                x.objschema AS object_schema,
                x.objname AS object_name,
                x.objtype::information_schema.character_data AS object_type,
                x.objdtdid AS dtd_identifier
            FROM (
                SELECT
                    columns.table_schema,
                    columns.table_name,
                    'TABLE'::text AS text,
                    columns.dtd_identifier
                FROM (
                    SELECT
                        nc.nspname::information_schema.sql_identifier AS table_schema,
                        c.relname::information_schema.sql_identifier AS table_name,
                        a.attnum::information_schema.sql_identifier AS dtd_identifier
                    FROM pg_attribute a
                    JOIN (pg_class c
                    JOIN pg_namespace nc ON c.relnamespace = nc.oid) ON a.attrelid = c.oid
                    WHERE NOT pg_is_other_temp_schema(nc.oid)
                    AND a.attnum > 0 AND NOT a.attisdropped
                    AND (c.relkind = ANY (ARRAY['r'::"char", 'v'::"char", 'f'::"char"]))
                    AND (pg_has_role(c.relowner, 'USAGE'::text)
                    OR has_column_privilege(c.oid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'::text))) AS columns) x(objschema, objname, objtype, objdtdid)) AS data_type_privileges))) e
ON ((c.table_catalog, c.table_schema, c.table_name, 'TABLE', c.dtd_identifier) = (e.object_catalog, e.object_schema, e.object_name, e.object_type, e.collection_type_identifier)
AND e.data_type IS NOT NULL)
WHERE c.table_schema = 'public';