在sqlalchemy中管理用户权限
我有一个使用sqlalchemy的脚本,它创建并使用了很多引擎实例,这些实例代表了许多用户的连接。所有的引擎都配置为指向同一个postgres数据库。
我有一个引擎,叫做oSuperEngine
,它可以做一些超级的事情。还有另一个引擎,叫做oBobsEngine
,是给Bob用的。
现在我在做这样的操作:
sSQL = "GRANT ALL PRIVILEGES ON TABLE \"NICE_TABLE\" to bob;"
oSuperEngine.execute(sSQL)
sSQL = "insert into \"NICE_TABLE\" (foo) values (bar)"
oBobsEngine.execute(sSQL) # ERROR HERE
结果是:
ProgrammingError: (ProgrammingError) permission denied for relation NICE_TABLE
这是为什么呢?
在psql中,输入\dp
命令告诉我,Bob从来没有被授予权限。如果我用超级用户登录psql,并手动给Bob授予权限,那么一切就都正常了。在这种情况下,我使用的命令和通过sqlalchemy执行的完全一样。
是不是有什么东西没有正确刷新?sqlalchemy是否因为某种原因在尝试授予权限时喜欢静默失败?我该如何让这个工作正常呢?
抱歉没有提供更多代码,代码库有点复杂。事件的顺序已经通过日志确认过了……如果你需要更多代码来理解我的问题,请告诉我。
一些日志信息
2014-03-10 10:07:24,767 - common.sqlalchemy_tools - DEBUG - connection string = "postgresql+psycopg2://super:password@localhost/db_name"
2014-03-10 10:07:24,767 - sqlalchemy.engine.base.Engine - INFO - BEGIN;
2014-03-10 10:07:24,767 - sqlalchemy.engine.base.Engine - INFO - {}
2014-03-10 10:07:24,767 - sqlalchemy.engine.base.Engine - INFO - GRANT ALL PRIVILEGES ON TABLE "MY_TABLE" to bob;
2014-03-10 10:07:24,767 - sqlalchemy.engine.base.Engine - INFO - {}
2014-03-10 10:07:24,768 - sqlalchemy.engine.base.Engine - INFO - COMMIT;
2014-03-10 10:07:24,768 - sqlalchemy.engine.base.Engine - INFO - {}
...
2014-03-10 10:07:24,804 - common.sqlalchemy_tools - DEBUG - connection string = "postgresql+psycopg2://bob:password@localhost/db_name"
2014-03-10 10:07:24,814 - sqlalchemy.engine.base.Engine - INFO - BEGIN;
2014-03-10 10:07:24,815 - sqlalchemy.engine.base.Engine - INFO - {}
2014-03-10 10:07:24,827 - sqlalchemy.engine.base.Engine - INFO - insert into "MY_TABLE" (stuff) values (other stuff);
2014-03-10 10:07:24,827 - sqlalchemy.engine.base.Engine - INFO - {}
2014-03-10 10:07:24,828 - sqlalchemy.engine.base.Engine - INFO - ROLLBACK
第一部分的操作是用oSuperEngine
进行的,第二部分是用Bob的引擎进行的。错误信息看起来是:
ProgrammingError: (ProgrammingError) permission denied for relation MY_TABLE
'insert into "MY_TABLE" (stuff) values (stuff);' {}
2 个回答
我也遇到过完全一样的问题。通过这样做解决了:
sSQL = "GRANT ALL PRIVILEGES ON TABLE \"NICE_TABLE\" to bob;"
with oSuperEngine.begin() as conn:
conn.execute(sSQL)
如果你运行上面的脚本,你会遇到一个关于事务隔离的问题。我之前也遇到过类似的情况。这里要记住的是,Bob的事务看不到超级引擎所做的更改,因为这个事务还在进行中,而隔离性不允许读取未提交的数据。Postgres有很多关于这个的文档,简单来说就是:在Postgres中,无法读取未提交的数据。
所以,你的解决办法显然是先从超级引擎提交你的更改,然后再在另一个事务中使用这些更改。我没有尝试过,但我猜测你必须保持默认的已提交读取隔离级别(因为更高的隔离级别会让你无法检测到自从事务开始(在这个例子中是Bob的事务)以来的更改)。
因此,在执行Bob的查询之前:
oSuperEngine.execute("COMMIT")
不过,这引出了一个副问题,实际上破坏了事务带来的好处:你无法轻松地回滚,因为更改已经被提交。你在这里基本上想要的是与保存点相反的东西:
保存点不会立即将更改存入数据库,而是确保回滚只会回到某个特定的点,而不是回到最开始。你想要的是:将更改存入数据库,但又能回滚并将其移除。我不知道有没有这样的东西,我非常怀疑它的存在,因为这会违反事务隔离的原则。
我解决这个问题的方法是编写自定义的回滚程序,当出现异常时调用它,这样我就可以手动撤销更改。不过,这需要不少工作,而且从长远来看维护起来也比较麻烦。最后,在我的案例中,我重新考虑了我的解决方案,放弃了多引擎的方法(但这个决定你得自己做)。