一尘不染

这是PostgreSQL SQL引擎的错误,如何避免(解决方法)呢?

sql

我正在解析文本文档并将其插入PostgreSQL
DB。我的代码是用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(引用)中的单词ID 。

虽然迭代此循环一段时间(例如2,000或15,000次迭代-这是不可预测的),但由于SQL错误而失败-无法将null
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。所有组合都会导致上述错误。为了确保这不是我的机器上的东西,我已经在其他机器上的类似环境中进行了测试。


更新: 正如Depesz所建议的,我已经启用了Postgres日志记录。以下是最新执行的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: 如果我膮膰臋艂艅贸艣藕偶从生成的单词中删除波兰国家字符(),则没有错误-
代码执行了所有50,000次迭代。我已经测试了几次。因此,对于这一行:

    private static final String letters = "abcdefghijklmnopqrstuvwxyz";

没有错误,一切似乎都很好,但是有了这一行(或上面完整源代码中的原始行):

    private static final String letters = "膮膰臋艂艅贸艣藕偶jklmnopqrstuvwxyz";

我收到上述错误。


UPDATE3: 我刚刚发布了类似的问题,但没有使用 Java-
完全移植到了纯plpgsql,请看这里:为什么该代码在PostgreSQL中失败,以及如何解决(解决方法)?它是Postgres
SQL引擎缺陷吗?
。现在我知道它与Java不相关-
这是Postgres本身的问题。


阅读 139

收藏
2021-03-08

共1个答案

一尘不染

我对该问题的进一步调查显示,该问题与纯Postgres
SQL有关,我开发了纯plpgsql版本,该版本是上述代码的一对一端口。对纯plpgsql的重新提出的问题在这里:为什么此代码在PostgreSQL中失败,以及如何解决(变通方法)?它是Postgres
SQL引擎缺陷吗?

所以-这不是Java / JDBC相关的问题。

此外,我设法简化了测试代码-现在它使用一个表。简化的问题发布在pgsql-
bugs邮件列表上:http : //archives.postgresql.org/pgsql-
bugs/2010-01/msg00182.php。确认发生在其他机器上(不仅是我的机器)。

解决方法是:将数据库排序规则从波兰语更改为标准“ C”。使用“
C”排序规则时,没有错误。但是,如果不使用波兰语归类,则波兰语单词的排序不正确(相对于波兰语国家字符),因此问题应在Postgres本身中解决。

2021-03-08