psycopg2 UniqueViolation ошибка при вставке в пустую таблицу - PullRequest
0 голосов
/ 07 июня 2019

Предполагая, что у меня есть пользовательская таблица, определенная так:

CREATE TABLE user (
    id SERIAL UNIQUE,
    email character varying(254) NOT NULL UNIQUE,
    username character varying(254) NOT NULL UNIQUE
)

И триггер для нее определен следующим образом:

CREATE OR REPLACE FUNCTION insert_username() RETURNS                       
TRIGGER AS $insert_username$                                               
    BEGIN                                                                  
        NEW.username := SPLIT_PART(NEW.email, '@', 1);                     
        RETURN NEW;                                                        
    END;                                                                   
$insert_username$ LANGUAGE plpgsql;                                        
CREATE TRIGGER insert_username BEFORE INSERT OR UPDATE                     
ON user FOR EACH ROW EXECUTE                           
PROCEDURE insert_username();

Затем, используя psycopg2, я пытаюсь заполнить эту таблицуданные из другой базы данных.Вот фрагмент кода из модуля миграции, который я сделал, и строка запроса, ответственная за это:

from psycopg2.extras import execute_values
from data_migration.database import cursor

query = 'INSERT INTO user("id", "email") VALUES %s'
values = [[1, 'example1@email.com'], [2, 'example2@email.com']]

execute_values(cursor, query, values)

Он работал как ожидалось, пока я не реализовал этот триггер.Теперь он все еще работает хорошо, когда я вставляю одну строку из оболочки postgres или использую psycopg2, но он не работает на execute_values ​​и выдает ошибку UniqueViolation, даже если таблица полностью пуста:

UniqueViolation: duplicate key value violates unique constraint "user_username_key"
DETAIL:  Key (username)=(kjhmgfd) already exists.

Там намного больше полейи код вокруг него, и, конечно, я могу избавиться от этого триггера и сделать то же самое на стороне Python.Но я бы очень хотел оставить это.Может быть, я упускаю что-то очевидное, но я потратил несколько часов на это и до сих пор не знаю, почему это происходит.Было бы очень полезно, если бы кто-то мог помочь мне выяснить это.Спасибо!

1 Ответ

0 голосов
/ 07 июня 2019

Как мне показалось, это было мое невнимание к исходным данным. Там у меня есть несколько пользователей с той же локальной частью, но с другим доменом в электронных письмах. Спасибо Джереми за то, что указал на это. Закончилось модификацией триггера следующим образом:

CREATE OR REPLACE FUNCTION insert_username() RETURNS                            
TRIGGER AS $insert_username$                                                    
    DECLARE                                                                     
        counter INTEGER := 0;                                                   
        new_username VARCHAR(256);                                              
        attempt_username VARCHAR(256);                                          
        is_exists BOOLEAN;                                                      
    BEGIN                                                                       
        CREATE OR REPLACE FUNCTION is_username_exists(un VARCHAR(256))          
            RETURNS BOOLEAN AS $$                                               
            DECLARE                                                             
                is_row_exists BOOLEAN;                                          
            BEGIN                                                               
                SELECT EXISTS(SELECT * FROM user WHERE username=un) INTO is_row_exists;                          
                RETURN is_row_exists;                                           
            END;                                                                
        $$ LANGUAGE plpgsql;                                                    

        new_username := SPLIT_PART(NEW.email, '@', 1);                          
        attempt_username := new_username;                                       

        LOOP                                                                    
            SELECT is_username_exists(attempt_username) INTO is_exists;         
            EXIT WHEN is_exists IS FALSE;                                       
            counter := counter + 1;                                             
            attempt_username := new_username || counter;                        
        END LOOP;                                                               

        NEW.username := attempt_username;                                       

        RETURN NEW;
    END;                                                                        
$insert_username$ LANGUAGE plpgsql;                                             
CREATE TRIGGER insert_username BEFORE INSERT OR UPDATE                          
ON user FOR EACH ROW EXECUTE PROCEDURE insert_username();
...