No rows returned by fetch() when for DELETE rewritten to UPDATE using rule
还没有人认领这个 Issue。
评估
调研方向
首先,针对 PostgreSQL 16.2 运行 issue 中的 Python 复现代码,比较经过规则重写的带 RETURNING 的 DELETE、CTE 变通方案和普通 DELETE。跟踪直接重写查询的 asyncpg fetch 结果;完成标准是它返回 psql 显示的行,同时保持其他情况的现有行为。
由索引模型根据 Issue 内容生成。
描述
- asyncpg version: 0.29.0
- PostgreSQL version: 16.2 (
postgres:latestDocker image) - Python version: 3.12.3
- Platform: Tested on macOS and Linux
- Do you use pgbouncer?: No
- Did you install asyncpg with pip?: Yes
- Can the issue be reproduced under both asyncio and uvloop?: Yes
Unexpected behaviour: It seems asyncpg doesn't return the rows returned by a DELETE query rewritten to an UPDATE query by a rule. Perhaps because it's optimizing (the query status is DELETE 0, so perhaps it thinks it doesn't need to return any rows) or something like that? I didn't dive in any further to check if that is indeed what is happening.
Reproduction
import asyncio
import asyncpg
async def main():
connection = await asyncpg.connect('postgresql://postgres:password@localhost/test')
async def fetch_print(query, *params):
result = await connection.fetch(query, *params)
i = 0
for row in result:
print(f' - {row}')
i += 1
if i == 0:
print(' (no rows returned)')
print('')
# Create table with rule for deletion
await connection.execute('''
CREATE TABLE items (
id serial PRIMARY KEY,
name text UNIQUE,
deleted boolean DEFAULT false
);
CREATE RULE softdelete AS
ON DELETE
TO items
DO INSTEAD
UPDATE items SET deleted = true WHERE id = OLD.id RETURNING OLD.*
;
INSERT INTO items (name) VALUES
('foo'),
('bar')
;
''')
print('Our table has a rule that updates the "deleted" column instead of deleting the row.\n')
# Show contents
print('We start with 2 rows which are not soft-deleted:')
await fetch_print('SELECT * FROM items')
# Try delete (the unexpected case)
print('Deleting with RETURNING should give us a row (it does in psql), but we do not get any in asyncpg:')
await fetch_print('''
DELETE FROM items WHERE name = $1 RETURNING id
''', 'foo')
# Confirm above query worked
print('But the row is now soft-deleted:')
await fetch_print('SELECT * FROM items')
# Workaround
print('If wrapped in a CTE it does work:')
await fetch_print('''
WITH x AS (
DELETE FROM items WHERE name = $1 RETURNING id
) SELECT * FROM x
''', 'bar')
# Confirm above query worked
print('And it is again properly softdeleted:')
await fetch_print('SELECT * FROM items')
print('We now delete the rule.\n')
await connection.execute('DROP RULE softdelete ON items')
# Confirm normal delete without rule returns rows
print('Normal deletion (without the rule) does return rows correctly:')
await fetch_print('''
DELETE FROM items RETURNING id
''')
# Confirm above query worked
print('And now both rows are indeed gone:')
await fetch_print('SELECT * FROM items')
# Clean up table after we are done
await connection.execute('DROP TABLE items')
await connection.close()
asyncio.run(main())
Output of reproduction
Our table has a rule that updates the "deleted" column instead of deleting the row.
We start with 2 rows which are not soft-deleted:
- <Record id=1 name='foo' deleted=False>
- <Record id=2 name='bar' deleted=False>
Deleting with RETURNING should give us a row (it does in psql), but we do not get any in asyncpg:
(no rows returned)
But the row is now soft-deleted:
- <Record id=2 name='bar' deleted=False>
- <Record id=1 name='foo' deleted=True>
If wrapped in a CTE it does work:
- <Record id=2>
And it is again properly softdeleted:
- <Record id=1 name='foo' deleted=True>
- <Record id=2 name='bar' deleted=True>
We now delete the rule.
Normal deletion (without the rule) does return rows correctly:
- <Record id=1>
- <Record id=2>
And now both rows are indeed gone:
(no rows returned)
In contrast the psql command line tool does show me the resulting rows when the result code is DELETED 0.
Output of psql
test=# DELETE FROM items WHERE name = 'foo' RETURNING id;
id
----
1
(1 row)
DELETE 0
So it is a bit unexpected that asyncpg doesn't return any rows.
- 主要语言
- Python
- 星标
- 8.1k
- 派生
- 469
- 平均合并
- 4 天 1 小时
- 30 天内合并 PR
- 14
贡献指南
这个仓库没有索引到贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
MagicStack/asyncpg 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 76/100
MagicStack/asyncpg#1371 ·
-
难度 3/5 1-2 天 新手友好度 72/100
MagicStack/asyncpg#1342 ·
-
难度 3/5 1-2 天 新手友好度 56/100
MagicStack/asyncpg#1340 · 1 条评论 ·
-
难度 2/5 1-3 小时 新手友好度 28/100
MagicStack/asyncpg#1337 ·
-
难度 4/5 3-5 天 新手友好度 42/100
MagicStack/asyncpg#1330 · 1 条评论 ·
查看 MagicStack/asyncpg 的全部 Issue
相似的 Issue
-
agent-ready documentation needs-triage
难度 1/5 1-3 小时 新手友好度 88/100
-
documentation
难度 1/5 1 小时以内 新手友好度 91/100
-
workflow-status page template still says reusable workflows are "triggered only by workflow_call:" 未关闭
难度 1/5 1 小时以内 新手友好度 92/100
-
instance instance add
难度 1/5 1 小时以内 新手友好度 72/100
searxng/searx-instances#939 · 1 条评论 ·
-
area-deployment area-integrations triage:bot-seen
难度 2/5 半天 新手友好度 86/100