【问题标题】:Unique constraint violation for unique index on LOWER(col)LOWER(col) 上唯一索引的唯一约束违规
【发布时间】: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


【解决方案1】:

不是真正的答案,只是一些太长的想法,无法在评论中显示:

  1. 我在本地设置了一个测试用例,即使在运行多达 10,000 次插入时也不会出现重复错误。当然,我没有使用 Npgsql,只是 psql 中的一个匿名函数。我的猜测是这可能是我成功的原因,没有异步。更多内容如下。

  2. 从这里On CONFLICT:

带有 ON CONFLICT DO UPDATE 子句的 INSERT 是“确定性”语句。这意味着该命令将不允许多次影响任何单个现有行;出现这种情况时将引发基数违规错误。建议插入的行不应在受仲裁索引或约束约束的属性方面相互重复。

  1. 也来自上述链接:

conflict_target 可以执行唯一索引推断。执行推理时,它包含一个或多个 index_column_name 列和/或 index_expression 表达式,以及一个可选的 index_predicate。所有 table_name 唯一索引,不考虑顺序,准确包含冲突目标指定的列/表达式,都被推断(选择)为仲裁索引。

  1. 我怀疑插入的异步特性会导致 2) 和/或 3) 跳闸。特别是因为“Id”上有两个唯一索引。虽然这并不能解释为什么 LOWER("Id") 似乎是一个问题。

  2. 我仍然不确定将文本字段用于整数值的目的是什么?也不为什么如果这些值都是整数,你会使用 LOWER("Id")?

  3. 为了有更好的机会获得明确的答案,我建议您发布到pgsql-general 邮件列表。虽然准备好回答关于 5) 中的问题。

【讨论】:

  • 感谢您的建议,我回家后会更仔细地检查链接部分。为5)提供响应——字段类型为文本,以便于LOWER()的使用,首先产生错误;我使用整数值来进一步说明这种情况的奇怪之处,即使LOWER() 对字段值没有影响,错误仍然存​​在。我同意该字段类型在其他情况下并不理想。
猜你喜欢
  • 2022-01-23
  • 2018-06-28
  • 2015-11-03
  • 2018-01-10
  • 2013-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多