Unverified 提交 8b2f4739 authored 作者: Noel Grandin's avatar Noel Grandin 提交者: GitHub

Merge pull request #798 from katzyn/PgServer

Add partial support of DATE, TIME, and TIMESTAMP data types to PgServer
...@@ -26,10 +26,14 @@ import java.sql.ResultSet; ...@@ -26,10 +26,14 @@ import java.sql.ResultSet;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Properties; import java.util.Properties;
import java.util.TimeZone;
import org.h2.command.CommandInterface; import org.h2.command.CommandInterface;
import org.h2.engine.ConnectionInfo; import org.h2.engine.ConnectionInfo;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
...@@ -520,6 +524,11 @@ public class PgServerThread implements Runnable { ...@@ -520,6 +524,11 @@ public class PgServerThread implements Runnable {
sendMessage(); sendMessage();
} }
private static long toPostgreSeconds(long millis) {
// TODO handle Julian/Gregorian transitions
return millis / 1000 - 946684800L;
}
private void writeDataColumn(ResultSet rs, int column, int pgType, boolean text) private void writeDataColumn(ResultSet rs, int column, int pgType, boolean text)
throws Exception { throws Exception {
if (text) { if (text) {
...@@ -562,7 +571,7 @@ public class PgServerThread implements Runnable { ...@@ -562,7 +571,7 @@ public class PgServerThread implements Runnable {
writeInt(8); writeInt(8);
dataOut.writeDouble(rs.getDouble(column)); dataOut.writeDouble(rs.getDouble(column));
break; break;
case PgServer.PG_TYPE_BYTEA: case PgServer.PG_TYPE_BYTEA: {
byte[] data = rs.getBytes(column); byte[] data = rs.getBytes(column);
if (data == null) { if (data == null) {
writeInt(-1); writeInt(-1);
...@@ -571,7 +580,40 @@ public class PgServerThread implements Runnable { ...@@ -571,7 +580,40 @@ public class PgServerThread implements Runnable {
write(data); write(data);
} }
break; break;
}
case PgServer.PG_TYPE_DATE: {
writeInt(4);
long millis = rs.getDate(column).getTime();
millis += TimeZone.getDefault().getOffset(millis);
writeInt((int) (toPostgreSeconds(millis) / 86400));
break;
}
case PgServer.PG_TYPE_TIME: {
writeInt(8);
Time t = rs.getTime(column);
long m = t.getTime();
m += TimeZone.getDefault().getOffset(m);
// double format
m /= 1000;
m = Double.doubleToLongBits(m);
// long format
// m *= 1000;
writeInt((int) (m >>> 32));
writeInt((int) m);
break;
}
case PgServer.PG_TYPE_TIMESTAMP_NO_TMZONE: {
writeInt(8);
Timestamp t = rs.getTimestamp(column);
long m = t.getTime();
m += TimeZone.getDefault().getOffset(m);
// double format
m = toPostgreSeconds(m);
m = Double.doubleToLongBits(m + t.getNanos() * 0.000000001);
writeInt((int) (m >>> 32));
writeInt((int) m);
break;
}
default: throw new IllegalStateException("output binary format is undefined"); default: throw new IllegalStateException("output binary format is undefined");
} }
} }
...@@ -595,7 +637,28 @@ public class PgServerThread implements Runnable { ...@@ -595,7 +637,28 @@ public class PgServerThread implements Runnable {
// plain text // plain text
byte[] data = DataUtils.newBytes(paramLen); byte[] data = DataUtils.newBytes(paramLen);
readFully(data); readFully(data);
prep.setString(col, new String(data, getEncoding())); String str = new String(data, getEncoding());
switch (pgType) {
case PgServer.PG_TYPE_DATE: {
// Strip timezone offset
int idx = str.indexOf(' ');
if (idx > 0) {
str = str.substring(0, idx);
}
break;
}
case PgServer.PG_TYPE_TIME: {
// Strip timezone offset
int idx = str.indexOf('+');
if (idx <= 0)
idx = str.indexOf('-');
if (idx > 0) {
str = str.substring(0, idx);
}
break;
}
}
prep.setString(col, str);
} else { } else {
// binary // binary
switch (pgType) { switch (pgType) {
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
package org.h2.test; package org.h2.test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
......
...@@ -7,6 +7,7 @@ package org.h2.test.unit; ...@@ -7,6 +7,7 @@ package org.h2.test.unit;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.Date;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.ParameterMetaData; import java.sql.ParameterMetaData;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
...@@ -14,6 +15,7 @@ import java.sql.ResultSet; ...@@ -14,6 +15,7 @@ import java.sql.ResultSet;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.util.Properties; import java.util.Properties;
...@@ -22,6 +24,8 @@ import java.util.concurrent.ExecutionException; ...@@ -22,6 +24,8 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import org.h2.api.ErrorCode;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.Server; import org.h2.tools.Server;
...@@ -45,19 +49,13 @@ public class TestPgServer extends TestBase { ...@@ -45,19 +49,13 @@ public class TestPgServer extends TestBase {
config.memory = true; config.memory = true;
config.mvStore = true; config.mvStore = true;
config.mvcc = true; config.mvcc = true;
// the sleeps are too mitigate "port in use" exceptions on Jenkins // testPgAdapter() starts server by itself without a wait so run it first
testLowerCaseIdentifiers();
Thread.sleep(100);
testPgAdapter(); testPgAdapter();
Thread.sleep(100); testLowerCaseIdentifiers();
testKeyAlias(); testKeyAlias();
Thread.sleep(100);
testKeyAlias(); testKeyAlias();
Thread.sleep(100);
testCancelQuery(); testCancelQuery();
Thread.sleep(100);
testBinaryTypes(); testBinaryTypes();
Thread.sleep(100);
testPrepareWithUnspecifiedType(); testPrepareWithUnspecifiedType();
} }
...@@ -70,10 +68,9 @@ public class TestPgServer extends TestBase { ...@@ -70,10 +68,9 @@ public class TestPgServer extends TestBase {
"mem:pgserver;DATABASE_TO_UPPER=false", "sa", "sa"); "mem:pgserver;DATABASE_TO_UPPER=false", "sa", "sa");
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("create table test(id int, name varchar(255))"); stat.execute("create table test(id int, name varchar(255))");
Server server = Server.createPgServer("-baseDir", getBaseDir(), Server server = createPgServer("-baseDir", getBaseDir(),
"-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "-pgPort", "5535", "-pgDaemon", "-key", "pgserver",
"mem:pgserver"); "mem:pgserver");
server.start();
try { try {
Connection conn2; Connection conn2;
conn2 = DriverManager.getConnection( conn2 = DriverManager.getConnection(
...@@ -98,6 +95,28 @@ public class TestPgServer extends TestBase { ...@@ -98,6 +95,28 @@ public class TestPgServer extends TestBase {
} }
} }
private Server createPgServer(String... args) throws SQLException {
Server server = Server.createPgServer(args);
int failures = 0;
for (;;) {
try {
server.start();
return server;
} catch (SQLException e) {
// the sleeps are too mitigate "port in use" exceptions on Jenkins
if (e.getErrorCode() != ErrorCode.EXCEPTION_OPENING_PORT_2 || ++failures > 10) {
throw e;
}
println("Sleeping");
try {
Thread.sleep(100);
} catch (InterruptedException e2) {
throw new RuntimeException(e2);
}
}
}
}
private void testPgAdapter() throws SQLException { private void testPgAdapter() throws SQLException {
deleteDb("pgserver"); deleteDb("pgserver");
Server server = Server.createPgServer( Server server = Server.createPgServer(
...@@ -120,9 +139,8 @@ public class TestPgServer extends TestBase { ...@@ -120,9 +139,8 @@ public class TestPgServer extends TestBase {
return; return;
} }
Server server = Server.createPgServer( Server server = createPgServer(
"-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver"); "-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver");
server.start();
ExecutorService executor = Executors.newSingleThreadExecutor(); ExecutorService executor = Executors.newSingleThreadExecutor();
try { try {
...@@ -347,9 +365,8 @@ public class TestPgServer extends TestBase { ...@@ -347,9 +365,8 @@ public class TestPgServer extends TestBase {
if (!getPgJdbcDriver()) { if (!getPgJdbcDriver()) {
return; return;
} }
Server server = Server.createPgServer( Server server = createPgServer(
"-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver"); "-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver");
server.start();
try { try {
Connection conn = DriverManager.getConnection( Connection conn = DriverManager.getConnection(
"jdbc:postgresql://localhost:5535/pgserver", "sa", "sa"); "jdbc:postgresql://localhost:5535/pgserver", "sa", "sa");
...@@ -375,13 +392,8 @@ public class TestPgServer extends TestBase { ...@@ -375,13 +392,8 @@ public class TestPgServer extends TestBase {
return; return;
} }
// Sometimes the previous pg server has not finished shutting and we get Server server = createPgServer(
// "port in use", so sleep for a bit.
Thread.sleep(100);
Server server = Server.createPgServer(
"-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver"); "-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver");
server.start();
try { try {
Properties props = new Properties(); Properties props = new Properties();
props.setProperty("user", "sa"); props.setProperty("user", "sa");
...@@ -396,10 +408,11 @@ public class TestPgServer extends TestBase { ...@@ -396,10 +408,11 @@ public class TestPgServer extends TestBase {
stat.execute( stat.execute(
"create table test(x1 varchar, x2 int, " + "create table test(x1 varchar, x2 int, " +
"x3 smallint, x4 bigint, x5 double, x6 float, " + "x3 smallint, x4 bigint, x5 double, x6 float, " +
"x7 real, x8 boolean, x9 char, x10 bytea)"); "x7 real, x8 boolean, x9 char, x10 bytea, " +
"x11 date, x12 time, x13 timestamp)");
PreparedStatement ps = conn.prepareStatement( PreparedStatement ps = conn.prepareStatement(
"insert into test values (?,?,?,?,?,?,?,?,?,?)"); "insert into test values (?,?,?,?,?,?,?,?,?,?,?,?,?)");
ps.setString(1, "test"); ps.setString(1, "test");
ps.setInt(2, 12345678); ps.setInt(2, 12345678);
ps.setShort(3, (short) 12345); ps.setShort(3, (short) 12345);
...@@ -410,6 +423,9 @@ public class TestPgServer extends TestBase { ...@@ -410,6 +423,9 @@ public class TestPgServer extends TestBase {
ps.setBoolean(8, true); ps.setBoolean(8, true);
ps.setByte(9, (byte) 0xfe); ps.setByte(9, (byte) 0xfe);
ps.setBytes(10, new byte[] { 'a', (byte) 0xfe, '\127' }); ps.setBytes(10, new byte[] { 'a', (byte) 0xfe, '\127' });
ps.setDate(11, Date.valueOf("2015-01-31"));
ps.setTime(12, Time.valueOf("20:11:15"));
ps.setTimestamp(13, Timestamp.valueOf("2001-10-30 14:16:10.111"));
ps.execute(); ps.execute();
ResultSet rs = stat.executeQuery("select * from test"); ResultSet rs = stat.executeQuery("select * from test");
...@@ -425,6 +441,9 @@ public class TestPgServer extends TestBase { ...@@ -425,6 +441,9 @@ public class TestPgServer extends TestBase {
assertEquals((byte) 0xfe, rs.getByte(9)); assertEquals((byte) 0xfe, rs.getByte(9));
assertEquals(new byte[] { 'a', (byte) 0xfe, '\127' }, assertEquals(new byte[] { 'a', (byte) 0xfe, '\127' },
rs.getBytes(10)); rs.getBytes(10));
assertEquals(Date.valueOf("2015-01-31"), rs.getDate(11));
assertEquals(Time.valueOf("20:11:15"), rs.getTime(12));
assertEquals(Timestamp.valueOf("2001-10-30 14:16:10.111"), rs.getTimestamp(13));
conn.close(); conn.close();
} finally { } finally {
...@@ -437,9 +456,8 @@ public class TestPgServer extends TestBase { ...@@ -437,9 +456,8 @@ public class TestPgServer extends TestBase {
return; return;
} }
Server server = Server.createPgServer( Server server = createPgServer(
"-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver"); "-pgPort", "5535", "-pgDaemon", "-key", "pgserver", "mem:pgserver");
server.start();
try { try {
Properties props = new Properties(); Properties props = new Properties();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论