将 aclitem 数组转换为多行 redshift

convert array of aclitem into multiple rows redshift

我有一个列值为

的数组

{詹姆斯=UC/james,亚当=C/james,克里斯=UC/詹姆斯,约翰=U/james}

以上列值不是json。它们是以下形式的字符串:

{ username=privilegestring/grantor } 

如何将上面的列转换成多行

编辑#3:
更新了查询,通过 CTE pg_catalog 专门针对 pg_catalog.pg_namespace 授予 acl 权限。当前,此 CTE 在 where 子句中过滤为 select 单个命名空间名称 ('avengers');如果你想 select 来自多个命名空间名称,你应该能够将它们直接添加到此 CT​​E 的 WHERE 子句中,或者在需要所有命名空间名称的情况下,完全删除该子句。

同样值得注意的是,您需要扩展 access_privilege_types 中的 case 语句以处理所有权限案例:'r'、'w'、'a'、'd' 和 'x',用于操作:SELECTUPDATEINSERTDELETEREFERENCE, 分别.

编辑#2:
下面查询的最终发布版本应该以您想要的格式为您提供所需的数据。我不知道权限类型有多少可能的值;如果当前指定的两个以上,则需要在 CTE* access_privilege_types* 中扩展 case 语句。显然,您还需要在查询中替换您的 table 名称等。如果您 运行 遇到任何问题,请告诉我,我会在必要时提供帮助。

编辑#1:
能够验证此查询在 Redshift 中是否有效。更新了查询以按受让人和所有者拆分单独的行。当前版本还没有按行分解个人权限 -- 今晚晚些时候看看我是否也可以让它工作。

原文:
我目前无法访问我的 Redshift 集群来对此进行测试,但我会在回家后进行测试。以下方法背后的总体思路是创建一个编号索引 table 进行交叉连接,将权限字段中的数据扩展为基于行的表示形式。

我已经询问了大小限制,因为这目前只能处理 10,000 个可能的分隔值,但是如果您的特定应用需要,您可以调整 CTE 以扩大到更大的数量:

修订版 3 查询:

        WITH

    pg_namespace AS (
        SELECT
            nspname
            , nspowner
            , rtrim(ltrim(array_to_string(nspacl, ','), '{'), '}') as nspacl
        FROM pg_catalog.pg_namespace
        WHERE nspname = 'public'
    ),

    -- Generating a table with the numbers 1 - 10 in a single column.
    ten_numbers AS (
        SELECT
            1 AS num
            UNION SELECT 2
            UNION SELECT 3
            UNION SELECT 4
            UNION SELECT 5
            UNION SELECT 6
            UNION SELECT 7
            UNION SELECT 8
            UNION SELECT 9
            UNION SELECT 0
    ),

    -- Expands the values in ten_numbers to create a single column with the values 1 - 10,000.
    depivot_index AS (
        SELECT
            (1000 * t1.num) + (100 * t2.num) + (10 * t3.num) + t4.num AS gen_num
        FROM ten_numbers AS t1
            JOIN ten_numbers AS t2 ON 1 = 1
            JOIN ten_numbers AS t3 ON 1 = 1
            JOIN ten_numbers AS t4 ON 1 = 1
    ),

    -- Filters down generated_numbers to house only the numbers up to the maximum times that the delimiter appears.
    splitter AS (
        SELECT
            *
        FROM depivot_index
        WHERE gen_num BETWEEN 1 AND (
            SELECT max(REGEXP_COUNT(nspacl, '\,') + 1)
            FROM pg_namespace
            )
    ),

    -- Cross joins permissions_groups and splitter to populate all requests, delimited on ','.
    expanded_input AS (
        SELECT
            pg.nspname
            , pg.nspacl
            , trim(split_part(pg.nspacl, ',', s.gen_num)) AS raw_permissions_string
        FROM pg_namespace AS pg
            JOIN splitter AS s ON 1 = 1
        WHERE split_part(nspacl, ',', s.gen_num) <> ''
    ),

    -- Breaks out the owner and grantee fields into their own columns respectively.
    users_with_raw_permissions_data AS  (
        SELECT
            e.raw_permissions_string
            , e.nspname
            , trim(split_part(e.raw_permissions_string, '=', 1)) AS grantee
            , trim(split_part(trim(split_part(e.raw_permissions_string, '=', 2)), '/', 2)) AS owner
            , trim(split_part(trim(split_part(e.raw_permissions_string, '=', 2)), '/', 1)) AS raw_permissions_data

        FROM
        expanded_input e
    ),

    -- Mines privilege types from raw string data.
    access_privilege_types AS (
        SELECT
          u.nspname
          , u.owner
          , u.grantee
          ,CASE
            WHEN position('C*' IN u.raw_permissions_data) > 0 THEN 'C*'
            WHEN position('U*' IN u.raw_permissions_data) > 0 THEN 'U*'
            WHEN position('C' IN u.raw_permissions_data) > 0 THEN 'C'
            WHEN position('U' IN u.raw_permissions_data) > 0 THEN 'U'
            ELSE u.raw_permissions_data
          END AS first_access_privilege
          , CASE
            WHEN position('U*' IN u.raw_permissions_data) > 0 THEN 'U*'
            WHEN position('C*' IN u.raw_permissions_data) > 0 THEN 'C*'
            WHEN position('U' IN u.raw_permissions_data) > 0 THEN 'U'
            WHEN position('C' IN u.raw_permissions_data) > 0 THEN 'C'
             ELSE u.raw_permissions_data
          END AS second_access_privilege
        , first_access_privilege || ',' || second_access_privilege AS merged_access_privileges
        FROM users_with_raw_permissions_data u
    ),

    -- Cross joins access_privilge_types and splitter to populate all privilege_types, delimited on ','.
    expanded_access_privilege_types AS (
        SELECT
            a.nspname
            , a.owner
            , a.grantee
            , trim(split_part(a.merged_access_privileges, ',', s.gen_num)) AS access_privileges
        FROM access_privilege_types AS a
            JOIN splitter AS s ON 1 = 1
        WHERE split_part(a.merged_access_privileges, ',', s.gen_num) <> ''
        GROUP BY 1, 2, 3, 4
    )
SELECT
    ea.nspname
    , ea.owner
    , ea.grantee
    , LEFT(ea.access_privileges, 1) AS access_privilege
    , CASE
        WHEN POSITION('*' IN ea.access_privileges) > 0 THEN 'YES'
        ELSE 'NO'
      END AS is_grantable
FROM expanded_access_privilege_types ea
ORDER BY 1, 2, 3, 4, 5  

编辑#4:
添加一些关于 ten_numbersdepivot_indexsplitter table 如何拆分 pg_catalog.pg_namespace.nspacl 字段的说明。总体概述是 ten_numbersdepivot_index are created purely to return tables with numbered rows to use as an index when joining in thesplit_partvalues ofnspacl`.

ten_numbers 生成具有单列的 table,包含数字 0-9:

-------
| num |
-------
|  0  |
-------
|  1  |
-------
| etc |
-------
|  9  |
------- 

然后 table 在 CTE depivot_index 期间扩展到 0-9999 范围:

-----------
| gen_num |
-----------
|    0    |
-----------
|    1    |
-----------
|    2    |
-----------
|   etc   |
-----------
|   9998  |
-----------
|   9999  |
----------- 

splitter 然后缩小 table 以仅容纳 nspacl 字段中指定分隔符的最大计数的数字:

-------
| num |
-------
|  0  |
-------
|  1  |
-------
| etc |
-------
|  6  |
------- 

splitter 返回的 table 然后通过 CTE expanded_input1 = 1 的连接用作 CROSS JOIN 的目标。这确保 split_part 返回的每个成员都有自己的行:

---------------------------------------------------------------------------
|  nspname  |               nspacl              |  raw_permissions_string |
---------------------------------------------------------------------------
|  avengers |  "{james=UC/james,adam=C/james}"  |      "james=UC/james"   |
---------------------------------------------------------------------------
|  avengers |  "{james=UC/james,adam=C/james}"  |       "adam=C/james"    |
---------------------------------------------------------------------------
|  avengers |                 etc.              |            etc.         |
---------------------------------------------------------------------------