【发布时间】:2021-09-03 18:30:07
【问题描述】:
我遇到了一个很奇怪的行为,希望这里的专家能帮我解释一下为什么会出现这种现象。
我在 PostgreSQL 中有如下表和函数定义:
CREATE TABLE test_table (
"Id" text PRIMARY KEY,
"Counter" int NOT NULL
);
CREATE UNIQUE INDEX idx_test_table_id ON test_table("Id");
CREATE OR REPLACE FUNCTION public.test_func(id text)
RETURNS int
AS $$
DECLARE counter int;
BEGIN
INSERT INTO public.test_table
VALUES (id, 2)
ON CONFLICT ("Id")
DO UPDATE SET "Counter" = public.test_table."Counter" + 1
RETURNING "Counter" - 1
INTO counter;
RETURN counter;
END
$$
LANGUAGE plpgsql;
我有一个测试客户端,它在循环中异步调用函数,并使用相同的 ID。
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Npgsql;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
namespace Sandbox
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public async Task TestMethod1()
{
int id = new Random().Next();
IList<Task> tasks = new List<Task>();
for (int i = 0; i < 80; i++)
{
tasks.Add(ExecutePgFunctionAsync(id.ToString()));
}
await Task.WhenAll(tasks.ToArray());
}
private async Task ExecutePgFunctionAsync(string id)
{
NpgsqlConnection conn = new NpgsqlConnection("Database=sandbox;Host=localhost;Password=runsmarter;Pooling=True;Port=12000;Timeout=15;Username=postgres;Include Error Detail=True");
await conn.OpenAsync();
using (NpgsqlCommand command = new NpgsqlCommand("test_func", conn))
{
try
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("id", id);
await command.ExecuteNonQueryAsync();
}
finally
{
await command.Connection.CloseAsync();
}
}
}
}
}
使用上述定义,一切都很好。但是,如果我将唯一索引更改为:
CREATE UNIQUE INDEX idx_test_table_id ON test_table(LOWER("Id"));
我会开始偶尔收到以下错误:
Npgsql.PostgresException: 23505: 重复键值违反唯一约束“idx_test_table_id”
如果我在冲突条件中进一步添加LOWER(),即:
ON CONFLICT (LOWER("Id"))
错误变为:
Npgsql.PostgresException: 23505: 重复键值违反唯一约束“test_table_pkey”
为什么会出现这些错误?
附录
我在全新安装的 Visual Studio 和 PostgreSQL 上几乎按原样重新运行了原始代码。我在 ExecutePgFunctionAsync() 中添加了一个 catch 子句,希望能提供更多的诊断数据。
与:
我得到以下异常:
并且表在异常时处于以下状态(请注意Counter的值会因运行而异):
与:
我得到以下异常:
并且该表在异常时处于以下状态:
【问题讨论】:
-
要么你错了,数据库中有一些大写字母,要么你有一个损坏的索引。找到它抱怨的行!看看
REINDEX是否解决了问题。 -
这不可能是你调用的函数。你会得到
ERROR: invalid reference to FROM-clause entry for table "test_table" LINE 15: SET "Counter" = public.test_table."Counter" + 1(你不能在那里对表名进行模式限定。)请提供你实际执行的函数。也许你搞砸了你的search_path设置并且在另一个模式中有另一个函数test_func()的实例?或者你搞砸了手动编辑来隐藏真实姓名? -
你帖子的最后几行没有加起来。如果您实际上将函数更改为使用
ON CONFLICT (LOWER("Id")),则无法从PK 中获取唯一违规。任何此类重复也会被LOWER("Id")捕获。不可能像你报告的那样。 -
感谢您删除 DROP SCHEMA 的编辑。这是我的疏忽。
-
我添加了一些截图来捕捉我的观察结果。
标签: postgresql unique-constraint