Это ошибка SQL-движка PostgreSQL и как ее избежать (обойти)? - PullRequest
2 голосов
/ 18 января 2010

Я разбираю текстовые документы и вставляю их в базу данных PostgreSQL. Мой код написан на Java, и я использую JDBC для подключения к БД. Я столкнулся с очень странной ошибкой при добавлении данных в БД - кажется, что в непредсказуемый момент (разное количество итераций основного цикла) Postgres не видит строки, только что добавленные в таблицы, и не может правильно выполнить обновления.

Может быть, я что-то не так делаю, поэтому, возможно, есть способ исправить мой код? Или это серьезная ошибка PostgreSQL, и я должен опубликовать ее на домашней странице PostgreSQL (как отчет об ошибке)?

Вот подробности того, что я делаю и что не так. Я упростил свой код, чтобы изолировать ошибку - упрощенная версия не разбирает текст, но я имитирую его сгенерированными словами. Исходные файлы включены (java и sql) в конце моего вопроса.

В упрощенном примере моей проблемы у меня есть однопоточный код, одно JDBC-соединение, 3 таблицы и несколько операторов SQL (полные исходные коды Java содержат менее 90 строк).

Основной цикл работает для «документов» - 20 слов с последующим doc_id (целое число).

  1. Буферная таблица spb_word4obj очищается для только что вставленного doc_id.
  2. Слова вставляются в буферную таблицу (spb_word4obj),
  3. Затем в таблицу вставляются уникальные новые слова spb_word
  4. И наконец - слова документа вставляются в spb_obj_word - с заменой тел слов на идентификаторы слов из spb_word (ссылки).

При повторении этого цикла в течение некоторого времени (например, 2000 или 15000 повторений - это непредсказуемо) он завершается ошибкой SQL - не может вставить нулевой word_id в spb_word. Это становится более странным, поскольку повторение этой самой последней итерации вручную не дает ошибок. Кажется, что у PostgreSQL есть некоторая проблема с вставкой записей и скоростью выполнения операторов - он теряет некоторые данные или делает их видимыми для последующих операторов после небольшой задержки.

Последовательность сгенерированных слов повторяется - каждый раз, когда выполняется код, он генерирует одну и ту же последовательность слов, но номер итерации при сбое кода каждый раз отличается.

Вот мой sql код для создания таблиц:

create sequence spb_word_seq;

create table spb_word (
  id bigint not null primary key default nextval('spb_word_seq'),
  word varchar(410) not null unique
);

create sequence spb_obj_word_seq;

create table spb_obj_word (
  id int not null primary key default nextval('spb_obj_word_seq'),
  doc_id int not null,
  idx int not null,
  word_id bigint not null references spb_word (id),
  constraint spb_ak_obj_word unique (doc_id, word_id, idx)
);

create sequence spb_word4obj_seq;

create table spb_word4obj (
  id int not null primary key default nextval('spb_word4obj_seq'),
  doc_id int not null,
  idx int not null,
  word varchar(410) not null,
  word_id bigint null references spb_word (id),
  constraint spb_ak_word4obj unique (doc_id, word_id, idx),
  constraint spb_ak_word4obj2 unique (doc_id, word, idx)
);

И здесь идет код Java - он может быть просто выполнен (у него есть статический метод main).

package WildWezyrIsAstonished;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class StrangePostgresBehavior {

    private static final String letters = "abcdefghijklmnopqrstuvwxyząćęłńóśźż";
    private static final int llen = letters.length();
    private Connection conn;
    private Statement st;
    private int wordNum = 0;

    public void runMe() throws Exception {
        Class.forName("org.postgresql.Driver");

        conn = DriverManager.getConnection("jdbc:postgresql://localhost:5433/spb",
                "wwspb", "*****");
        conn.setAutoCommit(true);
        st = conn.createStatement();

        st.executeUpdate("truncate table spb_word4obj, spb_word, spb_obj_word");

        for (int j = 0; j < 50000; j++) {

            try {
                if (j % 100 == 0) {
                    System.out.println("j == " + j);
                }

                StringBuilder sb = new StringBuilder();

                for (int i = 0; i < 20; i++) {
                    sb.append("insert into spb_word4obj (word, idx, doc_id) values ('"
                            + getWord() + "'," + i + "," + j + ");\n");
                }
                st.executeUpdate("delete from spb_word4obj where doc_id = " + j);

                st.executeUpdate(sb.toString());

                st.executeUpdate("update spb_word4obj set word_id = w.id "
                        + "from spb_word w "
                        + "where w.word = spb_word4obj.word and doc_id = " + j);

                st.executeUpdate("insert into spb_word (word) "
                        + "select distinct word from spb_word4obj "
                        + "where word_id is null and doc_id = " + j);

                st.executeUpdate("update spb_word4obj set word_id = w.id "
                        + "from spb_word w "
                        + "where w.word = spb_word4obj.word and "
                        + "word_id is null and doc_id = " + j);

                st.executeUpdate("insert into spb_obj_word (word_id, idx, doc_id) "
                        + "select word_id, idx, doc_id from spb_word4obj "
                        + "where doc_id = " + j);
            } catch (Exception ex) {
                System.out.println("error for j == " + j);
                throw ex;
            }
        }
    }

    private String getWord() {
        int rn = 3 * (++wordNum + llen * llen * llen);
        rn = (rn + llen) / (rn % llen + 1);
        rn = rn % (rn / 2 + 10);

        StringBuilder sb = new StringBuilder();
        while (true) {
            char c = letters.charAt(rn % llen);
            sb.append(c);
            rn /= llen;
            if (rn == 0) {
                break;
            }
        }

        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
        new StrangePostgresBehavior().runMe();
    }
}

Итак, еще раз: я что-то делаю неправильно (что именно?) Или это серьезный недостаток в PosgreSQL SQL Engine (чем - есть ли способ для обхода)?

Я тестировал выше на Windows Vista с: Java 1.6 / PostgreSQL 8.3.3 и 8.4.2 / JDBC PostgreSQL драйверы postgresql-8.2-505.jdbc3 и postgresql-8.4-701.jdbc4. Все комбинации приводят к ошибке, описанной выше. Чтобы убедиться, что на моей машине это не то, что я тестировал в аналогичной среде на другой машине.


ОБНОВЛЕНИЕ: Я включил ведение журнала Postgres - как это было предложено Depesz. Вот последние SQL-операторы, которые были выполнены:

2010-01-18 16:18:51 CETLOG:  execute <unnamed>: delete from spb_word4obj where doc_id = 1453
2010-01-18 16:18:51 CETLOG:  execute <unnamed>: insert into spb_word4obj (word, idx, doc_id) values ('ouc',0,1453)
2010-01-18 16:18:51 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('rbjb',1,1453)
2010-01-18 16:18:51 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('pvr',2,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('gal',3,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('cai',4,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('żjg',5,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('egf',6,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('śne',7,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('ęęd',8,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('lnd',9,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('cbd',10,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('dąc',11,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('łrc',12,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('zmł',13,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('zxo',14,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('oćj',15,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('zlh',16,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('lńf',17,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('cóe',18,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('uge',19,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: update spb_word4obj set word_id = w.id from spb_word w where w.word = spb_word4obj.word and doc_id = 1453
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: insert into spb_word (word) select distinct word from spb_word4obj where word_id is null and doc_id = 1453
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: update spb_word4obj set word_id = w.id from spb_word w where w.word = spb_word4obj.word and word_id is null and doc_id = 1453
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: insert into spb_obj_word (word_id, idx, doc_id) select word_id, idx, doc_id from spb_word4obj where doc_id = 1453
2010-01-18 16:18:52 CETERROR:  null value in column "word_id" violates not-null constraint
2010-01-18 16:18:52 CETSTATEMENT:  insert into spb_obj_word (word_id, idx, doc_id) select word_id, idx, doc_id from spb_word4obj where doc_id = 1453

Теперь - код для проверки того, что не так в таблице spb_word4obj:

select * 
from spb_word4obj w4o left join spb_word w on w4o.word = w.word
where w4o.word_id is null

и это показывает, что два слова: 'gal', 'zxo' вызвали проблему. Но ... они находятся в таблице spb_word - только что добавлены операторы sql из журнала (см. Выше).

Итак, проблема не в драйвере JDBC, а в самом Postgres?


UPDATE2: Если я исключу польские национальные символы (ąćęłńóśźż) из сгенерированных слов, ошибки не будет - код выполняет все 50000 итераций. Я проверял это несколько раз. Итак, для этой строки:

    private static final String letters = "abcdefghijklmnopqrstuvwxyz";

ошибки нет, все вроде бы нормально, но с этой строкой (или с оригинальной строкой в ​​полном источнике выше):

    private static final String letters = "ąćęłńóśźżjklmnopqrstuvwxyz";

Я получаю ошибку, описанную выше.


UPDATE3: Я только что опубликовал аналогичный вопрос без использования Java - полностью перенесен на чистый plpgsql, посмотрите здесь: Почему этот код не работает в PostgreSQL и как его исправить (обходной путь) ? Это недостаток движка Postgres SQL? . Теперь я знаю, что это не связано с Java - это проблема только Postgres.

Ответы [ 3 ]

3 голосов
/ 22 января 2010

Мое дальнейшее исследование проблемы показало, что проблема связана с чистым Postgres SQL, я разработал чистую версию plpgsql, которая является портом кода один к одному выше. Пересмотренный вопрос для чистого plpgsql здесь: Почему этот код не работает в PostgreSQL и как это исправить (обходной путь)? Это недостаток движка Postgres SQL? .

Итак, это не проблема Java / JDBC.

Кроме того, мне удалось упростить тестовый код - теперь он использует одну таблицу. Упрощенная проблема была опубликована в списке рассылки pgsql-bugs: http://archives.postgresql.org/pgsql-bugs/2010-01/msg00182.php. Подтверждено, что это происходит на других машинах (не только на моем).

Вот обходной путь: измените сопоставление базы данных с польского на стандартное «C». При сопоставлении 'C' ошибки нет. Но без польской сортировки слова на польском языке сортируются неправильно (относительно польских национальных символов), поэтому проблема должна быть решена в самом Postgres.

2 голосов
/ 18 января 2010

Включите ведение журнала запросов (log_statement = all) в postgresql.conf и проверьте запросы.

Могу поспорить, что проблема в драйвере (JDBC).

1 голос
/ 19 января 2010

Если вы пытаетесь проиндексировать документы на естественном языке с помощью Postgres (насколько я вижу, вы пытаетесь создать перевернутый индекс для слов документов), я бы порекомендовал вам взглянуть на Полный текствместо этого ищите в Postgres .


Если это не вариант, проверьте настройки кодировки:

  • database (В Postgres вы не можете изменить кодировку базы данных- вам придется воссоздать базу данных с нуля.)
  • Настройка драйвера JDBC / клиента Postgres (извините, я не могу вспомнить подробности),
  • и ваш источник Java (редактор)

Я предлагаю установить их все в UTF-8.

Если это по-прежнему не помогло, то я подозреваю, что существует какая-то проблема экранирования / кодирования между источником данных (ваш файл исходного кода Java) и местом назначения данных (база данных).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...