A development on the answer from mike_pdb I came up with the following
WITH object_list(schema_name,object_name,permission_info)
AS (
SELECT N.nspname, C.relname, array_to_string(relacl,',')
FROM pg_class AS C
INNER JOIN pg_namespace AS N
ON C.relnamespace = N.oid
WHERE C.relkind in ('v','r')
AND N.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
AND C.relacl[1] IS NOT NULL
),
object_permissions(schema_name,object_name,permission_string)
AS (
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',1) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',2) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',3) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',4) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',5) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',6) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',7) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',8) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',9) FROM object_list
UNION ALL
SELECT schema_name,object_name, SPLIT_PART(permission_info,',',10) FROM object_list
),
permission_parts(schema_name, object_name,security_principal, permission_pattern)
AS (
SELECT
schema_name,
object_name,
LEFT(permission_string ,CHARINDEX('=',permission_string)-1),
SPLIT_PART(SPLIT_PART(permission_string,'=',2),'/',1)
FROM object_permissions
WHERE permission_string >''
)
SELECT
schema_name,
object_name,
'GRANT ' ||
SUBSTRING(
case when charindex('r',permission_pattern) > 0 then ',SELECT ' else '' end
||case when charindex('w',permission_pattern) > 0 then ',UPDATE ' else '' end
||case when charindex('a',permission_pattern) > 0 then ',INSERT ' else '' end
||case when charindex('d',permission_pattern) > 0 then ',DELETE ' else '' end
||case when charindex('R',permission_pattern) > 0 then ',RULE ' else '' end
||case when charindex('x',permission_pattern) > 0 then ',REFERENCES ' else '' end
||case when charindex('t',permission_pattern) > 0 then ',TRIGGER ' else '' end
||case when charindex('X',permission_pattern) > 0 then ',EXECUTE ' else '' end
||case when charindex('U',permission_pattern) > 0 then ',USAGE ' else '' end
||case when charindex('C',permission_pattern) > 0 then ',CREATE ' else '' end
||case when charindex('T',permission_pattern) > 0 then ',TEMPORARY ' else '' end
,2,10000
)
|| ' ON ' || schema_name||'.'||object_name
|| ' TO ' || security_principal
|| ';' as grantsql
FROM permission_parts
;
There are 3 common table expressions used here.
- object_list - Tables and views with their permissions array as a comma delimited string.
- object_permissions - schema/object (table or view) and a record per permission string. Note that the SPLIT_PART function does not allow a dynamic part position so the assumption here is that there are no more than 10 users or groups assigned direct permissions
- permission_parts The schema/object, security principal to whom permissions are granted and the security attributes that are set.
As per mike_pdb's solution the individual permission characters are converted into a concatenated list of grants. As we don't which grants will be used we use SUBSTRING from position 2 to discard the first comma in the list.
You can use exactly the same approach for scripting off schema permissions
WITH schema_list(schema_name, permission_info)
AS (
SELECT nspname, array_to_string(nspacl,',')
FROM pg_namespace
WHERE nspacl[1] IS NOT NULL
AND nspname NOT LIKE 'pg%' AND nspname NOT IN ('public','information_schema')
),
schema_permissions(schema_name,permission_string)
AS (
SELECT schema_name,SPLIT_PART(permission_info,',',1) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',2) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',3) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',4) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',5) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',6) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',7) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',8) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',9) FROM schema_list
UNION ALL
SELECT schema_name,SPLIT_PART(permission_info,',',10) FROM schema_list
),
permission_parts(schema_name, security_principal, permission_pattern)
AS (
SELECT
schema_name,
LEFT(permission_string ,CHARINDEX('=',permission_string)-1),
SPLIT_PART(SPLIT_PART(permission_string,'=',2),'/',1)
FROM schema_permissions
WHERE permission_string >''
)
SELECT
schema_name,
'GRANT ' ||
SUBSTRING(
case when charindex('r',permission_pattern) > 0 then ',SELECT ' else '' end
||case when charindex('w',permission_pattern) > 0 then ',UPDATE ' else '' end
||case when charindex('a',permission_pattern) > 0 then ',INSERT ' else '' end
||case when charindex('d',permission_pattern) > 0 then ',DELETE ' else '' end
||case when charindex('R',permission_pattern) > 0 then ',RULE ' else '' end
||case when charindex('x',permission_pattern) > 0 then ',REFERENCES ' else '' end
||case when charindex('t',permission_pattern) > 0 then ',TRIGGER ' else '' end
||case when charindex('X',permission_pattern) > 0 then ',EXECUTE ' else '' end
||case when charindex('U',permission_pattern) > 0 then ',USAGE ' else '' end
||case when charindex('C',permission_pattern) > 0 then ',CREATE ' else '' end
||case when charindex('T',permission_pattern) > 0 then ',TEMPORARY ' else '' end
,2,10000
)
|| ' ON SCHEMA ' || schema_name
|| ' TO ' || security_principal
|| ';' as grantsql
FROM permission_parts;
relacl
column? – Venitaselect 'postgres=arwdDxt/postgres'::aclitem::text
work OK? How aboutselect '{postgres=arwdDxt/postgres,=r/postgres}'::aclitem[]::text[]::text
? If those work, then you should be able to changec.relacl::text
toc.relacl::text[]::text
. If not, you'll need to find another way of looking inside the ACLs (or create a custom cast). – Venitaselect 'anmediaextract01=arwdxt/anmediaextract01'::aclitem::text
(note no 'D') fails with same error. Same with the other variant. Don't thnk it's possible to create custom casts in redshift btw. – Sutter