【问题标题】:Executing multiple statements with Postgresql via SQLAlchemy does not persist changes通过 SQLAlchemy 使用 Postgresql 执行多个语句不会持久化更改
【发布时间】:2018-05-10 16:52:30
【问题描述】:

这不起作用——更新无效:

command = "select content from blog where slug = 'meow'; update account_balance set balance=200 where id=1; select 1 from blog;"
content = db.engine.scalar(command)

切换语句执行更新并成功选择:

command = "update account_balance set balance=200 where id=1; select content from blog where slug = 'meow';"
content = db.engine.scalar(command)

为什么第一个不起作用?它在 Pgadmin 中工作。我使用 Flask-Sqlalchemy 启用了自动提交。

我正在做一个关于SQL注入的研讨会,所以请不要重写解决方案!

【问题讨论】:

  • 定义“不起作用”?没错。
  • @CraigRinger account_balance 行当然不会更新
  • @IljaEverilä,由于数据库执行它们,我没有收到任何错误,但更新语句没有效果
  • @IljaEverilä 你是对的。我忘了包括一些东西以避免错误。我使用 1.1.12

标签: python postgresql sqlalchemy


【解决方案1】:

SQLAlchemy 的自动提交的工作方式是检查发出的语句,尝试detect whether or not data is modified:

...,SQLAlchemy 实现了自己的“自动提交”功能,该功能在所有后端完全一致。这是通过检测表示数据更改操作的语句(即 INSERT、UPDATE、DELETE)以及数据定义语言 (DDL) 语句(如 CREATE TABLE、ALTER TABLE)来实现的,然后如果没有事务正在进行,则自动发出 COMMIT .检测基于语句上是否存在autocommit=True 执行选项。如果语句是纯文本语句并且未设置标志,则使用正则表达式来检测特定后端的 INSERT、UPDATE、DELETE 以及各种其他命令

由于multiple result sets are not supported 在 SQLAlchemy 级别,在您的第一个示例中,检测只是省略了发出 COMMIT,因为 first 语句是一个 SELECT,而在您的第二个示例中它是一个 UPDATE。不会尝试从多个语句中检测数据修改语句。

如果您查看PGExecutionContext.should_autocommit_text(),您会发现它对AUTOCOMMIT_REGEXP 执行了regex match。换句话说,它只匹配文本的开头。

【讨论】:

  • 我附加了一个commit,现在它可以工作了。 SQLAlchemy 一定忘记自己发布它了。您是否猜测 SQLA 检查 last 语句是否应该发出提交?
  • 对不起,我修正了答案。重要的是 first 语句(例如在第二个示例中,第一个语句是 UPDATE)。还添加了一些关于原因的上下文。
  • 哇,认真的吗?这是一种疯狂的实现方式。不要使用该功能!
  • @craig-ringer 它适用于单个文本 SQL 语句,如果你真的想要多语句 SQL 字符串(SQLA 似乎没有正式支持,但保留 DBAPI 级别)和自动提交,有text(...).execution_options(autocommit=True),如"Understanding Autocommit" 中所述,尽管我猜这更常用于表示函数或SP 改变数据并应该提交的信号。另一方面,我个人一般不喜欢自动提交。
  • SELECT insert_something(...) 会不会失败?
【解决方案2】:

如果要使用SELECT INTO创建表:

如上所述,engine.execute('select * into a from b') 不起作用。相反,您可以执行以下操作:

conn = engine.raw_connection()
cursor = conn.cursor()

cursor.execute('select * into a from b')
conn.commit() 

【讨论】:

    【解决方案3】:

    如果您想执行所有操作并只获取第一行,您应该使用db.engine.execute(...).first()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-01
      • 2020-01-16
      • 1970-01-01
      相关资源
      最近更新 更多