我有一个数组,其列值为
{詹姆斯=UC/詹姆斯,亚当=C/詹姆斯,克里斯=UC /詹姆斯,约翰=U/詹姆斯}
上面的列值不是 json。它们是以下形式的字符串:
{ username=privilegestring/grantor }
如何将上列转换为多行
我有一个数组,其列值为
{詹姆斯=UC/詹姆斯,亚当=C/詹姆斯,克里斯=UC /詹姆斯,约翰=U/詹姆斯}
上面的列值不是 json。它们是以下形式的字符串:
{ username=privilegestring/grantor }
如何将上列转换为多行
编辑#3:
通过 CTE pg_catalog 更新了查询以专门针对 acl 权限授予的pg_catalog.pg_namespace。目前,此 CTE 在 where 子句中被过滤以选择单个命名空间名称 ( 'avengers'
);如果您想从多个命名空间名称中进行选择,您应该能够直接将它们添加WHERE
到此 CTE 的子句中,或者在想要所有命名空间名称的情况下,完全删除该子句。
同样值得注意的是,您需要扩展 case 语句access_privilege_types
以处理所有权限情况:'r'、'w'、'a'、'd' 和 'x',用于操作:SELECT
、、、、, , 分别。UPDATE
INSERT
DELETE
REFERENCE
编辑#2:
下面查询的最终发布版本应该以您想要的格式为您提供您想要的数据。我不知道权限类型有多少可能的值;如果您目前有两个以上指定,则需要扩展 CTE* access_privilege_types
* 中的 case 语句。显然,您还需要在查询中替换您的表名等。如果您遇到任何问题,请告诉我,我会在必要时提供帮助。
编辑 #1:
能够验证此查询在 Redshift 中是否有效。更新了查询以按被授予者和所有者划分单独的行。当前版本还没有按行划分个人权限——今晚晚些时候会看看我是否也能做到这一点。
原文:
我目前无法访问我的 Redshift 集群来测试这个,但我回家后会这样做。以下方法背后的一般思想是创建一个编号索引表以交叉连接,将权限字段中的数据扩展为基于行的表示。
我询问了大小限制,因为这目前只能处理 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_numbers
工作depivot_index
以splitter
分解pg_catalog.pg_namespace.nspacl
字段的说明。一般的概述,就是ten_numbers
和depivot_index are created purely to return tables with numbered rows to use as an index when joining in the
split_part values of
npacl`。
ten_numbers
生成一个包含数字 0-9 的单列表:
-------- | 编号 | -------- | 0 | -------- | 1 | -------- | 等| -------- | 9 | --------
然后在 CTE 期间扩展此表以容纳 0-9999 范围depivot_index
:
------------ | gen_num | ------------ | 0 | ------------ | 1 | ------------ | 2 | ------------ | 等| ------------ | 9998 | ------------ | 9999 | ------------
splitter
然后缩小表格以仅容纳nspacl
字段内指定分隔符的最大计数的数字:
-------- | 编号 | -------- | 0 | -------- | 1 | -------- | 等| -------- | 6 | --------
然后通过CTE中的连接将由返回的表splitter
用作 a 的目标。这确保了返回的每个成员都有自己的行:CROSS JOIN
1 = 1
expanded_input
split_part
-------------------------------------------------- ------------------------- | nsp 名称 | 空间站 | raw_permissions_string | -------------------------------------------------- ------------------------- | 复仇者联盟 | "{james=UC/james,adam=C/james}" | “詹姆斯=UC/詹姆斯” | -------------------------------------------------- ------------------------- | 复仇者联盟 | "{james=UC/james,adam=C/james}" | “亚当=C/詹姆斯” | -------------------------------------------------- ------------------------- | 复仇者联盟 | 等| 等| -------------------------------------------------- -------------------------