@ -228,6 +228,19 @@ public class PGStream implements Closeable, Flushable {
|
||||
pg_output.write(_int4buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Long to the back end.
|
||||
*
|
||||
* @param val the long to be sent
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void sendLong(long val) throws IOException {
|
||||
int high = (int)(val >> 32);
|
||||
int low = (int)(val & 0xffffffff);
|
||||
sendInteger4(high);
|
||||
sendInteger4(low);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 2-byte integer (short) to the back end.
|
||||
*
|
||||
@ -327,6 +340,18 @@ public class PGStream implements Closeable, Flushable {
|
||||
| _int4buf[3] & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a Long from the backend.
|
||||
*
|
||||
* @return the 64bit integer received from the backend
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public long receiveLong() throws IOException {
|
||||
long high = receiveInteger4();
|
||||
long low = receiveInteger4();
|
||||
return (high << 32) | low;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a two byte integer from the backend.
|
||||
*
|
||||
|
||||
@ -90,8 +90,12 @@ public abstract class QueryExecutorBase implements QueryExecutor {
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract void sendSupportTrace() throws IOException;
|
||||
|
||||
protected abstract void sendCloseMessage() throws IOException;
|
||||
|
||||
protected abstract void sendTrace() throws IOException;
|
||||
|
||||
@Override
|
||||
public void setNetworkTimeout(int milliseconds) throws IOException {
|
||||
pgStream.setNetworkTimeout(milliseconds);
|
||||
@ -146,6 +150,7 @@ public abstract class QueryExecutorBase implements QueryExecutor {
|
||||
|
||||
try {
|
||||
LOGGER.trace(" FE=> Terminate");
|
||||
sendTrace();
|
||||
sendCloseMessage();
|
||||
pgStream.flush();
|
||||
pgStream.close();
|
||||
|
||||
@ -126,6 +126,16 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
|
||||
private String clientEncoding;
|
||||
|
||||
private long sendTime;
|
||||
|
||||
private long recvTime;
|
||||
|
||||
private long netTime;
|
||||
|
||||
private boolean enableTrace = false;
|
||||
|
||||
private boolean waitNexttime = false;
|
||||
|
||||
/**
|
||||
* {@code CommandComplete(B)} messages are quite common, so we reuse instance to parse those
|
||||
*/
|
||||
@ -157,6 +167,12 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
|
||||
private static final String OFF = "off";
|
||||
|
||||
private static final String SERVER_SUPPORT_TRACE = "server_support_trace";
|
||||
|
||||
private static final int PROTOCOL3_MSGLEN_OFFSET = 4;
|
||||
|
||||
private static final int TIME_NS_ONE_US = 1000;
|
||||
|
||||
public QueryExecutorImpl(PGStream pgStream, String user, String database,
|
||||
int cancelSignalTimeout, Properties info) throws SQLException, IOException {
|
||||
super(pgStream, user, database, cancelSignalTimeout, info);
|
||||
@ -222,6 +238,14 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
return this.clientEncoding;
|
||||
}
|
||||
|
||||
public boolean getWaitNexttime() {
|
||||
return waitNexttime;
|
||||
}
|
||||
|
||||
public void setWaitNexttime(boolean waitNexttime) {
|
||||
this.waitNexttime = waitNexttime;
|
||||
}
|
||||
|
||||
/**
|
||||
* When database compatibility mode is A database and the parameter overload function
|
||||
* is turned on, add out parameter description message.
|
||||
@ -294,6 +318,14 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
}
|
||||
}
|
||||
|
||||
private void recordSendTime() {
|
||||
sendTime = System.nanoTime();
|
||||
}
|
||||
|
||||
private void recordRecvTime() {
|
||||
recvTime = System.nanoTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param holder object assumed to hold the lock
|
||||
* @return whether given object actually holds the lock
|
||||
@ -363,6 +395,10 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSimpleQuery(int flags) {
|
||||
return (flags & QueryExecutor.QUERY_EXECUTE_AS_SIMPLE) != 0;
|
||||
}
|
||||
|
||||
public synchronized void execute(Query query, ParameterList parameters, ResultHandler handler,
|
||||
int maxRows, int fetchSize, int flags) throws SQLException {
|
||||
waitOnLock();
|
||||
@ -388,6 +424,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
boolean autosave = false;
|
||||
try {
|
||||
try {
|
||||
recordAndSendTrace(flags);
|
||||
handler = sendQueryPreamble(handler, flags);
|
||||
autosave = sendAutomaticSavepoint(query, flags);
|
||||
sendQuery(query, (V3ParameterList) parameters, maxRows, fetchSize, flags,
|
||||
@ -546,6 +583,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
boolean autosave = false;
|
||||
ResultHandler handler = batchHandler;
|
||||
try {
|
||||
recordAndSendTrace(flags);
|
||||
handler = sendQueryPreamble(batchHandler, flags);
|
||||
autosave = sendAutomaticSavepoint(queries[0], flags);
|
||||
estimatedReceiveBufferBytes = 0;
|
||||
@ -619,6 +657,8 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
boolean autosave = false;
|
||||
ResultHandler handler = batchHandler;
|
||||
try {
|
||||
// P->DS->S U->E->S
|
||||
recordAndSendTrace(flags);
|
||||
handler = sendQueryPreamble(batchHandler, flags);
|
||||
autosave = sendAutomaticSavepoint(queries[0], flags);
|
||||
estimatedReceiveBufferBytes = 0;
|
||||
@ -748,6 +788,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
};
|
||||
|
||||
try {
|
||||
recordAndSendTrace(QueryExecutor.QUERY_NO_METADATA);
|
||||
sendOneQuery(beginTransactionQuery, SimpleQuery.NO_PARAMETERS, 0, 0,
|
||||
QueryExecutor.QUERY_NO_METADATA);
|
||||
sendSync();
|
||||
@ -1564,6 +1605,28 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// record sendTime and send K message.
|
||||
//
|
||||
private void recordAndSendTrace(int flags) throws IOException {
|
||||
if (!isSimpleQuery(flags)) {
|
||||
recordSendTime();
|
||||
}
|
||||
sendTrace();
|
||||
}
|
||||
|
||||
//
|
||||
// send netTime to database
|
||||
//
|
||||
protected void sendTrace() throws IOException {
|
||||
if (!enableTrace) {
|
||||
return;
|
||||
}
|
||||
pgStream.sendChar('K'); // Sync
|
||||
pgStream.sendInteger4(8 + PROTOCOL3_MSGLEN_OFFSET); // Length
|
||||
pgStream.sendLong(netTime);
|
||||
}
|
||||
|
||||
//
|
||||
// Message sending
|
||||
//
|
||||
@ -2142,11 +2205,12 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
//
|
||||
private void sendOneQuery(SimpleQuery query, SimpleParameterList params, int maxRows,
|
||||
int fetchSize, int flags) throws IOException {
|
||||
boolean asSimple = (flags & QueryExecutor.QUERY_EXECUTE_AS_SIMPLE) != 0;
|
||||
boolean asSimple = isSimpleQuery(flags);
|
||||
if (asSimple) {
|
||||
assert (flags & QueryExecutor.QUERY_DESCRIBE_ONLY) == 0
|
||||
: "Simple mode does not support describe requests. sql = " + query.getNativeSql()
|
||||
+ ", flags = " + flags;
|
||||
recordSendTime();
|
||||
sendSimpleQuery(query, params);
|
||||
return;
|
||||
}
|
||||
@ -2445,10 +2509,25 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
// from there.
|
||||
boolean doneAfterRowDescNoData = false;
|
||||
try {
|
||||
boolean isRecvP = false;
|
||||
while (!endQuery) {
|
||||
c = pgStream.receiveChar();
|
||||
recievedPacketType=c;
|
||||
switch (c) {
|
||||
case 'K': //receive dbTime
|
||||
recordRecvTime();
|
||||
int msgLen = pgStream.receiveInteger4();
|
||||
// dbTime 8 bytes
|
||||
assert (msgLen == 8 + PROTOCOL3_MSGLEN_OFFSET);
|
||||
long dbTime = pgStream.receiveLong();
|
||||
if (getWaitNexttime()) {
|
||||
netTime += ((recvTime - sendTime) / TIME_NS_ONE_US) - dbTime;
|
||||
} else {
|
||||
netTime = ((recvTime - sendTime) / TIME_NS_ONE_US) - dbTime;
|
||||
}
|
||||
isRecvP = true;
|
||||
break;
|
||||
|
||||
case 'A': // Asynchronous Notify
|
||||
receiveAsyncNotify();
|
||||
break;
|
||||
@ -2836,7 +2915,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
throw new IOException("Unexpected packet type: " + c);
|
||||
}
|
||||
}
|
||||
|
||||
enableTrace = isRecvP;
|
||||
} catch(IOException e) {
|
||||
LOGGER.error("IO Exception.recieved packetType:" + recievedPacketType + "last PacketType:" + lastPacketType
|
||||
+ "connection info:" + secSocketAddress + "buffer :\n" + pgStream.getInputBufferByHex());
|
||||
@ -2877,6 +2956,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
processDeadParsedQueries();
|
||||
processDeadPortals();
|
||||
|
||||
recordAndSendTrace(0);
|
||||
sendExecute(portal.getQuery(), portal, fetchSize);
|
||||
sendSync();
|
||||
|
||||
@ -3059,6 +3139,12 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
}
|
||||
}
|
||||
|
||||
protected void sendSupportTrace() throws IOException {
|
||||
pgStream.sendChar('V');
|
||||
pgStream.sendInteger4(4);
|
||||
pgStream.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendCloseMessage() throws IOException {
|
||||
pgStream.sendChar('X');
|
||||
@ -3125,6 +3211,13 @@ public class QueryExecutorImpl extends QueryExecutorBase {
|
||||
String name = pgStream.receiveString();
|
||||
String value = pgStream.receiveString();
|
||||
|
||||
if (name.equals(SERVER_SUPPORT_TRACE)) {
|
||||
// SERVER_SUPPORT_TRACE equals "1" means server support trace
|
||||
if ("1".equals(value)) {
|
||||
sendSupportTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (name.equals(CLIENT_ENCODING)) {
|
||||
if (allowEncodingChanges) {
|
||||
if (!value.equalsIgnoreCase("UTF8") && !value.equalsIgnoreCase("GBK")) {
|
||||
|
||||
@ -7,6 +7,7 @@ package org.postgresql.jdbc;
|
||||
|
||||
import org.postgresql.Driver;
|
||||
import org.postgresql.core.*;
|
||||
import org.postgresql.core.v3.QueryExecutorImpl;
|
||||
import org.postgresql.quickautobalance.ConnectionManager;
|
||||
import org.postgresql.quickautobalance.LoadBalanceHeartBeating;
|
||||
import org.postgresql.util.GT;
|
||||
@ -991,6 +992,7 @@ public class PgStatement implements Statement, BaseStatement {
|
||||
BatchResultHandler handler;
|
||||
handler = createBatchHandler(queries, parameterLists);
|
||||
|
||||
QueryExecutorImpl executer = (QueryExecutorImpl) connection.getQueryExecutor();
|
||||
if ((preDescribe || forceBinaryTransfers)
|
||||
&& (flags & QueryExecutor.QUERY_EXECUTE_AS_SIMPLE) == 0) {
|
||||
// Do a client-server round trip, parsing and describing the query so we
|
||||
@ -1000,6 +1002,9 @@ public class PgStatement implements Statement, BaseStatement {
|
||||
StatementResultHandler handler2 = new StatementResultHandler();
|
||||
try {
|
||||
connection.getQueryExecutor().execute(queries[0], parameterLists[0], handler2, 0, 0, flags2);
|
||||
if (connection.getQueryExecutor() instanceof QueryExecutorImpl) {
|
||||
executer.setWaitNexttime(true);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
// Unable to parse the first statement -> throw BatchUpdateException
|
||||
handler.handleError(e);
|
||||
@ -1046,6 +1051,7 @@ public class PgStatement implements Statement, BaseStatement {
|
||||
}
|
||||
}
|
||||
}
|
||||
executer.setWaitNexttime(false);
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user