我正在解析文本文档并将其插入PostgreSQL DB。我的代码是用Java编写的,并且使用JDBC进行数据库连接。将数据添加到数据库时,我遇到了一个非常奇怪的错误- 似乎在不可预测的时刻(主循环的迭代次数不同),Postgres看不到刚刚添加到表中的行,并且无法正确执行更新。
也许我做错了什么,所以也许有一种方法可以纠正我的代码? 还是PostgreSQL的严重错误,我应该将它发布在PostgreSQL主页上(作为错误报告)?
以下是 我正在做的事情和出了什么问题 的详细信息 。我已经简化了代码以隔离错误- 简化版不会解析任何文本,但会使用生成的单词进行模拟。我的问题结尾处 包含源文件 (java和sql)。
在我的问题的简化示例中,我有一个单线程代码,一个JDBC连接,3个表和很少的SQL语句(完整的Java源代码少于90行)。
主循环适用于“文档”-20个单词,后接doc_id(整数)。
spb_word4obj
spb_word
spb_obj_word
虽然迭代此循环一段时间(例如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方法)。
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语句(包含在上面)。
'gal', 'zxo'
因此-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本身的问题。
我对该问题的进一步调查显示,该问题与纯Postgres SQL有关,我开发了纯plpgsql版本,该版本是上述代码的一对一端口。对纯plpgsql的重新提出的问题在这里:为什么此代码在PostgreSQL中失败,以及如何解决(变通方法)?它是Postgres SQL引擎缺陷吗?。
所以-这不是Java / JDBC相关的问题。
此外,我设法简化了测试代码-现在它使用一个表。简化的问题发布在pgsql- bugs邮件列表上:http : //archives.postgresql.org/pgsql- bugs/2010-01/msg00182.php。确认发生在其他机器上(不仅是我的机器)。
解决方法是:将数据库排序规则从波兰语更改为标准“ C”。使用“ C”排序规则时,没有错误。但是,如果不使用波兰语归类,则波兰语单词的排序不正确(相对于波兰语国家字符),因此问题应在Postgres本身中解决。