提交 987315a9 authored 作者: noelgrandin's avatar noelgrandin

Issue 477: PgServer binary transmission of query params is unimplemented, patch…

Issue 477: PgServer binary transmission of query params is unimplemented, patch from Andrew Franklin
上级 a3367698
......@@ -38,6 +38,7 @@ Change Log
</li><li>Issue 475: PgServer: add support for CancelRequest, patch from Andrew Franklin
</li><li>Issue 473: PgServer missing -key option, patch from Andrew Franklin
</li><li>Issue 471: CREATE VIEW does not check user rights, patch from Andrew Franklin
</li><li>Issue 477: PgServer binary transmission of query params is unimplemented, patch from Andrew Franklin
</li></ul>
<h2>Version 1.3.172 (2013-05-25)</h2>
......
......@@ -55,22 +55,22 @@ public class PgServer implements Service {
*/
public static final int PG_TYPE_INT2VECTOR = 22;
private static final int PG_TYPE_BOOL = 16;
private static final int PG_TYPE_BYTEA = 17;
private static final int PG_TYPE_BPCHAR = 1042;
private static final int PG_TYPE_INT8 = 20;
private static final int PG_TYPE_INT2 = 21;
private static final int PG_TYPE_INT4 = 23;
private static final int PG_TYPE_TEXT = 25;
private static final int PG_TYPE_OID = 26;
private static final int PG_TYPE_FLOAT4 = 700;
private static final int PG_TYPE_FLOAT8 = 701;
private static final int PG_TYPE_UNKNOWN = 705;
private static final int PG_TYPE_TEXTARRAY = 1009;
private static final int PG_TYPE_DATE = 1082;
private static final int PG_TYPE_TIME = 1083;
private static final int PG_TYPE_TIMESTAMP_NO_TMZONE = 1114;
private static final int PG_TYPE_NUMERIC = 1700;
public static final int PG_TYPE_BOOL = 16;
public static final int PG_TYPE_BYTEA = 17;
public static final int PG_TYPE_BPCHAR = 1042;
public static final int PG_TYPE_INT8 = 20;
public static final int PG_TYPE_INT2 = 21;
public static final int PG_TYPE_INT4 = 23;
public static final int PG_TYPE_TEXT = 25;
public static final int PG_TYPE_OID = 26;
public static final int PG_TYPE_FLOAT4 = 700;
public static final int PG_TYPE_FLOAT8 = 701;
public static final int PG_TYPE_UNKNOWN = 705;
public static final int PG_TYPE_TEXTARRAY = 1009;
public static final int PG_TYPE_DATE = 1082;
public static final int PG_TYPE_TIME = 1083;
public static final int PG_TYPE_TIMESTAMP_NO_TMZONE = 1114;
public static final int PG_TYPE_NUMERIC = 1700;
private final HashSet<Integer> typeSet = New.hashSet();
......
......@@ -257,16 +257,13 @@ public class PgServerThread implements Runnable {
formatCodes[i] = readShort();
}
int paramCount = readShort();
for (int i = 0; i < paramCount; i++) {
int paramLen = readInt();
byte[] d2 = DataUtils.newBytes(paramLen);
readFully(d2);
try {
setParameter(prep.prep, i, d2, formatCodes);
} catch (Exception e) {
sendErrorResponse(e);
break switchBlock;
try {
for (int i = 0; i < paramCount; i++) {
setParameter(prep.prep, prep.paramType[i], i, formatCodes);
}
} catch (Exception e) {
sendErrorResponse(e);
break switchBlock;
}
int resultCodeCount = readShort();
portal.resultColumnFormat = new int[resultCodeCount];
......@@ -474,24 +471,62 @@ public class PgServerThread implements Runnable {
}
private void sendDataRow(ResultSet rs) throws Exception {
int columns = rs.getMetaData().getColumnCount();
String[] values = new String[columns];
for (int i = 0; i < columns; i++) {
values[i] = rs.getString(i + 1);
}
ResultSetMetaData metaData = rs.getMetaData();
int columns = metaData.getColumnCount();
startMessage('D');
writeShort(columns);
for (String s : values) {
if (s == null) {
for (int i = 1; i <= columns; i++) {
writeDataColumn(rs, i, PgServer.convertType(metaData.getColumnType(i)));
}
sendMessage();
}
private void writeDataColumn(ResultSet rs, int column, int pgType) throws Exception {
if (formatAsText(pgType)) {
String s = rs.getString(column);
if (s==null) {
writeInt(-1);
} else {
// TODO write Binary data
byte[] d2 = s.getBytes(getEncoding());
writeInt(d2.length);
write(d2);
byte[] data = s.getBytes(getEncoding());
writeInt(data.length);
write(data);
}
} else {
// binary
switch (pgType) {
case PgServer.PG_TYPE_INT2:
writeInt(2);
dataOut.writeShort(rs.getShort(column));
break;
case PgServer.PG_TYPE_INT4:
writeInt(4);
dataOut.writeInt(rs.getInt(column));
break;
case PgServer.PG_TYPE_INT8:
writeInt(8);
dataOut.writeLong(rs.getLong(column));
break;
case PgServer.PG_TYPE_FLOAT4:
writeInt(4);
dataOut.writeFloat(rs.getFloat(column));
break;
case PgServer.PG_TYPE_FLOAT8:
writeInt(8);
dataOut.writeDouble(rs.getDouble(column));
break;
case PgServer.PG_TYPE_BYTEA:
byte[] data = rs.getBytes(column);
if (data==null) {
writeInt(-1);
} else {
writeInt(data.length);
write(data);
}
break;
default: throw new IllegalStateException("output binary format is undefined");
}
}
sendMessage();
}
private String getEncoding() {
......@@ -501,24 +536,60 @@ public class PgServerThread implements Runnable {
return clientEncoding;
}
private void setParameter(PreparedStatement prep, int i, byte[] d2, int[] formatCodes) throws SQLException {
private void setParameter(PreparedStatement prep, int pgType, int i, int[] formatCodes) throws SQLException, IOException {
boolean text = (i >= formatCodes.length) || (formatCodes[i] == 0);
String s;
try {
if (text) {
s = new String(d2, getEncoding());
} else {
server.trace("Binary format not supported");
s = new String(d2, getEncoding());
int col = i + 1;
int paramLen = readInt();
if (text) {
// plain text
byte[] data = DataUtils.newBytes(paramLen);
readFully(data);
prep.setString(col, new String(data, getEncoding()));
} else {
// binary
switch (pgType) {
case PgServer.PG_TYPE_INT2:
if (paramLen != 4) {
throw DbException.getInvalidValueException("paramLen", paramLen);
}
prep.setShort(col, dataIn.readShort());
break;
case PgServer.PG_TYPE_INT4:
if (paramLen != 4) {
throw DbException.getInvalidValueException("paramLen", paramLen);
}
prep.setInt(col, dataIn.readInt());
break;
case PgServer.PG_TYPE_INT8:
if (paramLen != 8) {
throw DbException.getInvalidValueException("paramLen", paramLen);
}
prep.setLong(col, dataIn.readLong());
break;
case PgServer.PG_TYPE_FLOAT4:
if (paramLen != 4) {
throw DbException.getInvalidValueException("paramLen", paramLen);
}
prep.setFloat(col, dataIn.readFloat());
break;
case PgServer.PG_TYPE_FLOAT8:
if (paramLen != 8) {
throw DbException.getInvalidValueException("paramLen", paramLen);
}
prep.setDouble(col, dataIn.readDouble());
break;
case PgServer.PG_TYPE_BYTEA:
byte[] d1 = DataUtils.newBytes(paramLen);
readFully(d1);
prep.setBytes(col, d1);
break;
default:
server.trace("Binary format for type: "+pgType+" is unsupported");
byte[] d2 = DataUtils.newBytes(paramLen);
readFully(d2);
prep.setString(col, new String(d2, getEncoding()));
}
} catch (Exception e) {
server.traceError(e);
s = null;
}
// if(server.getLog()) {
// server.log(" " + i + ": " + s);
// }
prep.setString(i + 1, s);
}
private void sendErrorResponse(Exception re) throws IOException {
......@@ -615,13 +686,23 @@ public class PgServerThread implements Runnable {
writeShort(getTypeSize(types[i], precision[i]));
// pg_attribute.atttypmod
writeInt(-1);
// text
writeShort(0);
// the format type: text = 0, binary = 1
writeShort(formatAsText(types[i]) ? 0 : 1);
}
sendMessage();
}
}
/** @return should the given type be formatted as binary or plain text? */
private boolean formatAsText(int pgType) {
switch (pgType) {
// TODO: add more types to send as binary once compatibility is confirmed
case PgServer.PG_TYPE_BYTEA:
return false;
}
return true;
}
private static int getTypeSize(int pgType, int precision) {
switch (pgType) {
case PgServer.PG_TYPE_VARCHAR:
......
......@@ -6,16 +6,7 @@
*/
package org.h2.test.unit;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.sql.*;
import java.util.concurrent.*;
import org.h2.test.TestBase;
......@@ -286,4 +277,50 @@ public class TestPgServer extends TestBase {
server.stop();
}
}
private void testBinaryTypes() throws SQLException {
Server server = Server.createPgServer("-pgPort", "5535", "-pgDaemon", "-key", "test", "mem:test");
server.start();
try {
Class.forName("org.postgresql.Driver");
Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5535/test", "sa", "sa");
Statement stat = conn.createStatement();
stat.execute("create table test(x1 varchar, x2 int, x3 smallint, x4 bigint, x5 double, x6 float, " +
"x7 real, x8 boolean, x9 char, x10 bytea)");
PreparedStatement ps = conn.prepareStatement("insert into test values (?,?,?,?,?,?,?,?,?,?)");
ps.setString(1, "test");
ps.setInt(2, 12345678);
ps.setShort(3, (short)12345);
ps.setLong(4, 1234567890123L);
ps.setDouble(5, 123.456);
ps.setFloat(6, 123.456f);
ps.setDouble(7, 123.456);
ps.setBoolean(8, true);
ps.setByte(9, (byte)0xfe);
ps.setBytes(10, new byte[] {'a',(byte)0xfe,'\127'});
ps.execute();
ResultSet rs = stat.executeQuery("select * from test");
assertTrue(rs.next());
assertEquals("test", rs.getString(1));
assertEquals(12345678, rs.getInt(2));
assertEquals((short)12345, rs.getShort(3));
assertEquals(1234567890123L, rs.getLong(4));
assertEquals(123.456, rs.getDouble(5));
assertEquals(123.456f, rs.getFloat(6));
assertEquals(123.456, rs.getDouble(7));
assertEquals(true, rs.getBoolean(8));
assertEquals((byte)0xfe, rs.getByte(9));
assertEquals(new byte[]{'a',(byte)0xfe,'\127'}, rs.getBytes(10));
conn.close();
} catch (ClassNotFoundException e) {
println("PostgreSQL JDBC driver not found - PgServer not tested");
} finally {
server.stop();
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论