简介
我一直在开发一个向导,为没有任何编程/ SQL背景的用户创建复杂的数据库Postgres查询。由于存储在information_schema视图中的外键约束,用户可以选择任意数量的表,工具将找到正确的连接设置(因此,用户不必添加ON table_a.field_1 = table_b.field_2
)。
在开发过程中,我一直在使用管理数据库用户,现在想将其更改为只读用户以使其更安全。但是,此只读用户似乎无法访问外键约束。
现状
当选择了多个表时,该工具会尝试获取各个表之间的连接,以便了解如何连接它们。在此过程中,将执行以下查询:
SELECT
tc.constraint_name,
tc.table_name,
kcu.column_name,
ccu.table_name AS foreign_table_name,
ccu.column_name AS foreign_column_name
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
WHERE constraint_type = 'FOREIGN KEY'
AND ccu.table_name = 'TableB'
AND tc.table_name IN ('TableA');
(注意:最后一个WHERE
子句使用IN
,因为可以有多个基表可用.PableA是基表,每个成功连接/连接的表都可用于其他联接,例如第三个表可以使用AND ccu.table_name = 'TableC' AND tc.table_name IN ('TableA', 'TableB');
等等。)
当使用admin db用户(具有GRANT,SELECT,INSERT,UPDATE,DELETE,TRUNCATE等最常见的权限)执行查询时,结果如下所示:
constraint_name | table_name | column_name | foreign_table_name | foreign_column_name
----------------+------------+-------------+--------------------+---------------------
constraint1 | TableA | field_1 | TableB | field_2
(1 row)
但是当只读数据库用户运行该查询时,它会返回:
constraint_name | table_name | column_name | foreign_table_name | foreign_column_name
----------------+------------+-------------+--------------------+---------------------
(0 rows)
由于现有但未返回的外键约束条目,连接无法正确写入SQL,并且用户生成的查询(使用向导)失败。
我尝试了什么
首先,我认为只读用户(ro_user)可能没有权限访问数据库information_schema
中的表和视图。所以我跑了
GRANT SELECT ON ALL TABLES IN SCHEMA information_schema TO ro_user;
作为管理员但无济于事。深入了解文档的深度,我发现information_schema
中的所有表格和视图都可以在任何用户可以访问,默认情况下也可以在postgres中访问。因此授予select权限甚至不应该改变任何东西。
为了确保,我也跑了
GRANT REFERENCES ON ALL TABLES IN SCHEMA actual_database TO ro_user;
但当然,这并没有改变任何东西,因为REFERENCES
只需要创建新的外键,我只需要阅读它们。
接下来,我想,也许这个工具的sql由于某些信息不可用而失败,所以我通过运行分别查询了三个视图:
SELECT * FROM information_schema.table_constraints AS tc WHERE constraint_type = 'FOREIGN KEY';
SELECT * FROM information_schema.key_column_usage AS kcu;
SELECT * FROM information_schema.constraint_column_usage AS ccu;
果然,最后一行不会返回ro_user的任何一行:
psql=> SELECT * FROM information_schema.constraint_column_usage AS ccu;
table_catalog | table_schema | table_name | column_name | constraint_catalog | constraint_schema | constraint_name
---------------+--------------+------------+-------------+--------------------+-------------------+-----------------
(0 rows)
而管理员用户获得了大量结果。所以,它归结为那一个观点information_schema.constraint_column_usage
。
答案 0 :(得分:3)
当我在一个小时的时间里输入那个问题,回忆起我在最后几天尝试过的所有想法时,我终于找到了原因。
视图 constraint_column_usage 标识当前数据库中某些约束使用的所有列。 仅显示包含在当前启用的角色所拥有的表中的那些列。
通过我找到了解决方案
SELECT
conrelid::regclass AS table_from,
conname,
pg_get_constraintdef(c.oid) AS cdef
FROM pg_constraint c
JOIN pg_namespace n
ON n.oid = c.connamespace
WHERE contype IN ('f')
AND n.nspname = 'public'
AND pg_get_constraintdef(c.oid) LIKE '%"TableB"%'
AND conrelid::regclass::text IN ('"TableA"')
ORDER BY conrelid::regclass::text, contype DESC;
它不会输出与旧查询相同的格式,但它包含相同的信息,并且 - 最重要的是 - 可供ro_user使用。