com.mysql.cj.protocol.a.NativeProtocol Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mysql-connector-java Show documentation
Show all versions of mysql-connector-java Show documentation
Fixed bugs repo for official MySQL JDBC connector
The newest version!
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
* Free Software Foundation.
*
* This program is also distributed with certain software (including but not
* limited to OpenSSL) that is licensed under separate terms, as designated in a
* particular file or component or in included license documentation. The
* authors of MySQL hereby grant you an additional permission to link the
* program and your derivative works with the separately licensed software that
* they have included with MySQL.
*
* Without limiting anything contained in the foregoing, this file, which is
* part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
* version 1.0, a copy of which can be found at
* http://oss.oracle.com/licenses/universal-foss-exception.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
* for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.mysql.cj.protocol.a;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.lang.ref.SoftReference;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.DataTruncation;
import java.sql.Date;
import java.sql.SQLWarning;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;
import java.util.function.Supplier;
import com.mysql.cj.BindValue;
import com.mysql.cj.CharsetMapping;
import com.mysql.cj.Constants;
import com.mysql.cj.MessageBuilder;
import com.mysql.cj.Messages;
import com.mysql.cj.MysqlType;
import com.mysql.cj.NativeCharsetSettings;
import com.mysql.cj.NativeSession;
import com.mysql.cj.Query;
import com.mysql.cj.QueryAttributesBindings;
import com.mysql.cj.QueryResult;
import com.mysql.cj.ServerPreparedQuery;
import com.mysql.cj.ServerVersion;
import com.mysql.cj.Session;
import com.mysql.cj.TransactionEventHandler;
import com.mysql.cj.conf.PropertyKey;
import com.mysql.cj.conf.PropertySet;
import com.mysql.cj.conf.RuntimeProperty;
import com.mysql.cj.conf.RuntimeProperty.RuntimePropertyListener;
import com.mysql.cj.exceptions.CJCommunicationsException;
import com.mysql.cj.exceptions.CJConnectionFeatureNotAvailableException;
import com.mysql.cj.exceptions.CJException;
import com.mysql.cj.exceptions.CJOperationNotSupportedException;
import com.mysql.cj.exceptions.CJPacketTooBigException;
import com.mysql.cj.exceptions.ClosedOnExpiredPasswordException;
import com.mysql.cj.exceptions.DataTruncationException;
import com.mysql.cj.exceptions.ExceptionFactory;
import com.mysql.cj.exceptions.FeatureNotAvailableException;
import com.mysql.cj.exceptions.MysqlErrorNumbers;
import com.mysql.cj.exceptions.PasswordExpiredException;
import com.mysql.cj.exceptions.WrongArgumentException;
import com.mysql.cj.interceptors.QueryInterceptor;
import com.mysql.cj.jdbc.exceptions.MysqlDataTruncation;
import com.mysql.cj.log.BaseMetricsHolder;
import com.mysql.cj.log.Log;
import com.mysql.cj.log.ProfilerEvent;
import com.mysql.cj.log.ProfilerEventHandler;
import com.mysql.cj.protocol.AbstractProtocol;
import com.mysql.cj.protocol.ColumnDefinition;
import com.mysql.cj.protocol.ExportControlled;
import com.mysql.cj.protocol.FullReadInputStream;
import com.mysql.cj.protocol.Message;
import com.mysql.cj.protocol.MessageReader;
import com.mysql.cj.protocol.MessageSender;
import com.mysql.cj.protocol.PacketReceivedTimeHolder;
import com.mysql.cj.protocol.PacketSentTimeHolder;
import com.mysql.cj.protocol.Protocol;
import com.mysql.cj.protocol.ProtocolEntity;
import com.mysql.cj.protocol.ProtocolEntityFactory;
import com.mysql.cj.protocol.ProtocolEntityReader;
import com.mysql.cj.protocol.ResultBuilder;
import com.mysql.cj.protocol.Resultset;
import com.mysql.cj.protocol.Resultset.Concurrency;
import com.mysql.cj.protocol.Resultset.Type;
import com.mysql.cj.protocol.ResultsetRow;
import com.mysql.cj.protocol.ResultsetRows;
import com.mysql.cj.protocol.ServerSession;
import com.mysql.cj.protocol.SocketConnection;
import com.mysql.cj.protocol.ValueEncoder;
import com.mysql.cj.protocol.a.NativeConstants.IntegerDataType;
import com.mysql.cj.protocol.a.NativeConstants.StringLengthDataType;
import com.mysql.cj.protocol.a.NativeConstants.StringSelfDataType;
import com.mysql.cj.protocol.a.result.OkPacket;
import com.mysql.cj.result.Field;
import com.mysql.cj.result.IntegerValueFactory;
import com.mysql.cj.result.Row;
import com.mysql.cj.result.StringValueFactory;
import com.mysql.cj.result.ValueFactory;
import com.mysql.cj.util.LazyString;
import com.mysql.cj.util.StringUtils;
import com.mysql.cj.util.TestUtils;
import com.mysql.cj.util.TimeUtil;
import com.mysql.cj.util.Util;
public class NativeProtocol extends AbstractProtocol implements Protocol, RuntimePropertyListener {
protected static final int INITIAL_PACKET_SIZE = 1024;
protected static final int COMP_HEADER_LENGTH = 3;
protected static final int MAX_QUERY_SIZE_TO_EXPLAIN = 1024 * 1024; // don't explain queries above 1MB
protected static final int SSL_REQUEST_LENGTH = 32;
private static final String EXPLAINABLE_STATEMENT = "SELECT";
private static final String[] EXPLAINABLE_STATEMENT_EXTENSION = new String[] { "INSERT", "UPDATE", "REPLACE", "DELETE" };
protected MessageSender packetSender;
protected MessageReader packetReader;
protected NativeServerSession serverSession;
/** Track this to manually shut down. */
protected CompressedPacketSender compressedPacketSender;
//private PacketPayload sendPacket = null;
protected NativePacketPayload sharedSendPacket = null;
/** Use this when reading in rows to avoid thousands of new() calls, because the byte arrays just get copied out of the packet anyway */
protected NativePacketPayload reusablePacket = null;
/**
* Packet used for 'LOAD DATA LOCAL INFILE'
*
* We use a SoftReference, so that we don't penalize intermittent use of this feature
*/
private SoftReference loadFileBufRef;
protected byte packetSequence = 0;
protected boolean useCompression = false;
private RuntimeProperty maxAllowedPacket;
private RuntimeProperty useServerPrepStmts;
private boolean autoGenerateTestcaseScript;
/** Does the server support long column info? */
private boolean logSlowQueries = false;
private boolean useAutoSlowLog;
private boolean profileSQL = false;
private long slowQueryThreshold;
private int commandCount = 0;
protected boolean hadWarnings = false;
private int warningCount = 0;
protected Map, ProtocolEntityReader extends ProtocolEntity, ? extends Message>> PROTOCOL_ENTITY_CLASS_TO_TEXT_READER;
protected Map, ProtocolEntityReader extends ProtocolEntity, ? extends Message>> PROTOCOL_ENTITY_CLASS_TO_BINARY_READER;
private int statementExecutionDepth = 0;
private List queryInterceptors;
private RuntimeProperty maintainTimeStats;
private RuntimeProperty maxQuerySizeToLog;
private InputStream localInfileInputStream;
private BaseMetricsHolder metricsHolder;
static Map, Supplier> DEFAULT_ENCODERS = new HashMap<>();
static {
DEFAULT_ENCODERS.put(BigDecimal.class, NumberValueEncoder::new);
DEFAULT_ENCODERS.put(BigInteger.class, NumberValueEncoder::new);
DEFAULT_ENCODERS.put(Blob.class, BlobValueEncoder::new);
DEFAULT_ENCODERS.put(Boolean.class, BooleanValueEncoder::new);
DEFAULT_ENCODERS.put(Byte.class, NumberValueEncoder::new);
DEFAULT_ENCODERS.put(byte[].class, ByteArrayValueEncoder::new);
DEFAULT_ENCODERS.put(Calendar.class, UtilCalendarValueEncoder::new);
DEFAULT_ENCODERS.put(Clob.class, ClobValueEncoder::new);
DEFAULT_ENCODERS.put(Date.class, SqlDateValueEncoder::new);
DEFAULT_ENCODERS.put(java.util.Date.class, UtilDateValueEncoder::new);
DEFAULT_ENCODERS.put(Double.class, NumberValueEncoder::new);
DEFAULT_ENCODERS.put(Duration.class, DurationValueEncoder::new);
DEFAULT_ENCODERS.put(Float.class, NumberValueEncoder::new);
DEFAULT_ENCODERS.put(InputStream.class, InputStreamValueEncoder::new);
DEFAULT_ENCODERS.put(Instant.class, InstantValueEncoder::new);
DEFAULT_ENCODERS.put(Integer.class, NumberValueEncoder::new);
DEFAULT_ENCODERS.put(LocalDate.class, LocalDateValueEncoder::new);
DEFAULT_ENCODERS.put(LocalDateTime.class, LocalDateTimeValueEncoder::new);
DEFAULT_ENCODERS.put(LocalTime.class, LocalTimeValueEncoder::new);
DEFAULT_ENCODERS.put(Long.class, NumberValueEncoder::new);
DEFAULT_ENCODERS.put(OffsetDateTime.class, OffsetDateTimeValueEncoder::new);
DEFAULT_ENCODERS.put(OffsetTime.class, OffsetTimeValueEncoder::new);
DEFAULT_ENCODERS.put(Reader.class, ReaderValueEncoder::new);
DEFAULT_ENCODERS.put(Short.class, NumberValueEncoder::new);
DEFAULT_ENCODERS.put(String.class, StringValueEncoder::new);
DEFAULT_ENCODERS.put(Time.class, SqlTimeValueEncoder::new);
DEFAULT_ENCODERS.put(Timestamp.class, SqlTimestampValueEncoder::new);
DEFAULT_ENCODERS.put(ZonedDateTime.class, ZonedDateTimeValueEncoder::new);
}
/**
* The comment (if any) that we'll prepend to all queries
* sent to the server (to show up in "SHOW PROCESSLIST")
*/
private String queryComment = null;
private NativeMessageBuilder commandBuilder = null;
public static NativeProtocol getInstance(Session session, SocketConnection socketConnection, PropertySet propertySet, Log log,
TransactionEventHandler transactionManager) {
NativeProtocol protocol = new NativeProtocol(log);
protocol.init(session, socketConnection, propertySet, transactionManager);
return protocol;
}
public NativeProtocol(Log logger) {
this.log = logger;
this.metricsHolder = new BaseMetricsHolder();
}
@Override
public void init(Session sess, SocketConnection phConnection, PropertySet propSet, TransactionEventHandler trManager) {
super.init(sess, phConnection, propSet, trManager);
this.maintainTimeStats = this.propertySet.getBooleanProperty(PropertyKey.maintainTimeStats);
this.maxQuerySizeToLog = this.propertySet.getIntegerProperty(PropertyKey.maxQuerySizeToLog);
this.useAutoSlowLog = this.propertySet.getBooleanProperty(PropertyKey.autoSlowLog).getValue();
this.logSlowQueries = this.propertySet.getBooleanProperty(PropertyKey.logSlowQueries).getValue();
this.maxAllowedPacket = this.propertySet.getIntegerProperty(PropertyKey.maxAllowedPacket);
this.profileSQL = this.propertySet.getBooleanProperty(PropertyKey.profileSQL).getValue();
this.autoGenerateTestcaseScript = this.propertySet.getBooleanProperty(PropertyKey.autoGenerateTestcaseScript).getValue();
this.useServerPrepStmts = this.propertySet.getBooleanProperty(PropertyKey.useServerPrepStmts);
this.reusablePacket = new NativePacketPayload(INITIAL_PACKET_SIZE);
try {
this.packetSender = new SimplePacketSender(this.socketConnection.getMysqlOutput());
this.packetReader = new SimplePacketReader(this.socketConnection, this.maxAllowedPacket);
} catch (IOException ioEx) {
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), ioEx, getExceptionInterceptor());
}
if (this.propertySet.getBooleanProperty(PropertyKey.logSlowQueries).getValue()) {
calculateSlowQueryThreshold();
}
this.authProvider = new NativeAuthenticationProvider();
this.authProvider.init(this, this.getPropertySet(), this.socketConnection.getExceptionInterceptor());
Map, ProtocolEntityReader extends ProtocolEntity, NativePacketPayload>> protocolEntityClassToTextReader = new HashMap<>();
protocolEntityClassToTextReader.put(ColumnDefinition.class, new ColumnDefinitionReader(this));
protocolEntityClassToTextReader.put(ResultsetRow.class, new ResultsetRowReader(this));
protocolEntityClassToTextReader.put(Resultset.class, new TextResultsetReader(this));
this.PROTOCOL_ENTITY_CLASS_TO_TEXT_READER = Collections.unmodifiableMap(protocolEntityClassToTextReader);
Map, ProtocolEntityReader extends ProtocolEntity, NativePacketPayload>> protocolEntityClassToBinaryReader = new HashMap<>();
protocolEntityClassToBinaryReader.put(ColumnDefinition.class, new ColumnDefinitionReader(this));
protocolEntityClassToBinaryReader.put(Resultset.class, new BinaryResultsetReader(this));
this.PROTOCOL_ENTITY_CLASS_TO_BINARY_READER = Collections.unmodifiableMap(protocolEntityClassToBinaryReader);
}
@Override
public MessageBuilder getMessageBuilder() {
return getCommandBuilder();
}
public MessageSender getPacketSender() {
return this.packetSender;
}
public MessageReader getPacketReader() {
return this.packetReader;
}
private NativeMessageBuilder getCommandBuilder() {
if (this.commandBuilder != null) {
return this.commandBuilder;
}
return this.commandBuilder = new NativeMessageBuilder(this.serverSession.supportsQueryAttributes());
}
public Supplier getValueEncoderSupplier(Object obj) {
if (obj == null) {
return ByteArrayValueEncoder::new;
}
Supplier res = DEFAULT_ENCODERS.get(obj.getClass());
if (res == null) {
Optional> mysqlType = DEFAULT_ENCODERS.entrySet().stream().filter(m -> m.getKey().isAssignableFrom(obj.getClass()))
.map(m -> m.getValue()).findFirst();
if (mysqlType.isPresent()) {
res = mysqlType.get();
}
}
return res;
}
/**
* Negotiates the SSL communications channel used when connecting
* to a MySQL server that understands SSL.
*/
@Override
public void negotiateSSLConnection() {
if (!ExportControlled.enabled()) {
throw new CJConnectionFeatureNotAvailableException(this.getPropertySet(), this.serverSession, this.getPacketSentTimeHolder(), null);
}
long clientParam = this.serverSession.getClientParam();
NativePacketPayload packet = new NativePacketPayload(SSL_REQUEST_LENGTH);
packet.writeInteger(IntegerDataType.INT4, clientParam);
packet.writeInteger(IntegerDataType.INT4, NativeConstants.MAX_PACKET_SIZE);
packet.writeInteger(IntegerDataType.INT1, this.serverSession.getCharsetSettings().configurePreHandshake(false));
packet.writeBytes(StringLengthDataType.STRING_FIXED, new byte[23]); // Set of bytes reserved for future use.
send(packet, packet.getPosition());
try {
this.socketConnection.performTlsHandshake(this.serverSession, this.log);
// i/o streams were replaced, build new packet sender/reader
this.packetSender = new SimplePacketSender(this.socketConnection.getMysqlOutput());
this.packetReader = new SimplePacketReader(this.socketConnection, this.maxAllowedPacket);
} catch (FeatureNotAvailableException nae) {
throw new CJConnectionFeatureNotAvailableException(this.getPropertySet(), this.serverSession, this.getPacketSentTimeHolder(), nae);
} catch (IOException ioEx) {
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), ioEx, getExceptionInterceptor());
}
}
public void rejectProtocol(NativePacketPayload msg) {
try {
this.socketConnection.getMysqlSocket().close();
} catch (Exception e) {
// ignore
}
int errno = 2000;
NativePacketPayload buf = msg;
buf.setPosition(1); // skip the packet type
errno = (int) buf.readInteger(IntegerDataType.INT2);
String serverErrorMessage = "";
try {
serverErrorMessage = buf.readString(StringSelfDataType.STRING_TERM, "ASCII");
} catch (Exception e) {
//
}
StringBuilder errorBuf = new StringBuilder(Messages.getString("Protocol.0"));
errorBuf.append(serverErrorMessage);
errorBuf.append("\"");
String xOpen = MysqlErrorNumbers.mysqlToSqlState(errno);
throw ExceptionFactory.createException(MysqlErrorNumbers.get(xOpen) + ", " + errorBuf.toString(), xOpen, errno, false, null, getExceptionInterceptor());
}
@Override
public void beforeHandshake() {
// Reset packet sequences
this.packetReader.resetMessageSequence();
// Create session state
this.serverSession = new NativeServerSession(this.propertySet);
this.serverSession.setCharsetSettings(new NativeCharsetSettings((NativeSession) this.session));
// Read the first packet
this.serverSession.setCapabilities(readServerCapabilities());
}
@Override
public void afterHandshake() {
checkTransactionState();
try {
//
// Can't enable compression until after handshake
//
if (((this.serverSession.getCapabilities().getCapabilityFlags() & NativeServerSession.CLIENT_COMPRESS) != 0)
&& this.propertySet.getBooleanProperty(PropertyKey.useCompression).getValue()
&& !(this.socketConnection.getMysqlInput().getUnderlyingStream() instanceof CompressedInputStream)) {
this.useCompression = true;
this.socketConnection.setMysqlInput(new FullReadInputStream(new CompressedInputStream(this.socketConnection.getMysqlInput(),
this.propertySet.getBooleanProperty(PropertyKey.traceProtocol), this.log)));
this.compressedPacketSender = new CompressedPacketSender(this.socketConnection.getMysqlOutput());
this.packetSender = this.compressedPacketSender;
}
applyPacketDecorators(this.packetSender, this.packetReader);
this.socketConnection.getSocketFactory().afterHandshake();
} catch (IOException ioEx) {
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), ioEx, getExceptionInterceptor());
}
// Changing defaults for 8.0.3+ server: PNAME_useInformationSchema=true
RuntimeProperty useInformationSchema = this.propertySet.getProperty(PropertyKey.useInformationSchema);
if (versionMeetsMinimum(8, 0, 3) && !useInformationSchema.getValue() && !useInformationSchema.isExplicitlySet()) {
useInformationSchema.setValue(true);
}
// listen for properties changes to allow decorators reconfiguration
this.maintainTimeStats.addListener(this);
this.propertySet.getBooleanProperty(PropertyKey.traceProtocol).addListener(this);
this.propertySet.getBooleanProperty(PropertyKey.enablePacketDebug).addListener(this);
}
@Override
public void handlePropertyChange(RuntimeProperty> prop) {
switch (prop.getPropertyDefinition().getPropertyKey()) {
case maintainTimeStats:
case traceProtocol:
case enablePacketDebug:
applyPacketDecorators(this.packetSender.undecorateAll(), this.packetReader.undecorateAll());
break;
default:
break;
}
}
/**
* Apply optional decorators to configured PacketSender and PacketReader.
*
* @param sender
* {@link MessageSender}
* @param messageReader
* {@link MessageReader}
*/
public void applyPacketDecorators(MessageSender sender, MessageReader messageReader) {
TimeTrackingPacketSender ttSender = null;
TimeTrackingPacketReader ttReader = null;
LinkedList debugRingBuffer = null;
if (this.maintainTimeStats.getValue()) {
ttSender = new TimeTrackingPacketSender(sender);
sender = ttSender;
ttReader = new TimeTrackingPacketReader(messageReader);
messageReader = ttReader;
}
if (this.propertySet.getBooleanProperty(PropertyKey.traceProtocol).getValue()) {
sender = new TracingPacketSender(sender, this.log, this.socketConnection.getHost(), getServerSession().getCapabilities().getThreadId());
messageReader = new TracingPacketReader(messageReader, this.log);
}
if (this.getPropertySet().getBooleanProperty(PropertyKey.enablePacketDebug).getValue()) {
debugRingBuffer = new LinkedList<>();
sender = new DebugBufferingPacketSender(sender, debugRingBuffer, this.propertySet.getIntegerProperty(PropertyKey.packetDebugBufferSize));
messageReader = new DebugBufferingPacketReader(messageReader, debugRingBuffer,
this.propertySet.getIntegerProperty(PropertyKey.packetDebugBufferSize));
}
// do it after other decorators to have trace and debug applied to individual packets
messageReader = new MultiPacketReader(messageReader);
// atomic replacement of currently used objects
synchronized (this.packetReader) {
this.packetReader = messageReader;
this.packetDebugRingBuffer = debugRingBuffer;
this.setPacketSentTimeHolder(ttSender != null ? ttSender : new PacketSentTimeHolder() {
});
}
synchronized (this.packetSender) {
this.packetSender = sender;
this.setPacketReceivedTimeHolder(ttReader != null ? ttReader : new PacketReceivedTimeHolder() {
});
}
}
public NativeCapabilities readServerCapabilities() {
// Read the first packet
NativePacketPayload buf = readMessage(null);
// Server Greeting Error packet instead of Server Greeting
if (buf.isErrorPacket()) {
rejectProtocol(buf);
}
return new NativeCapabilities(buf);
}
@Override
public NativeServerSession getServerSession() {
return this.serverSession;
}
@Override
public void changeDatabase(String database) {
if (database == null || database.length() == 0) {
return;
}
try {
sendCommand(getCommandBuilder().buildComInitDb(getSharedSendPacket(), database), false, 0);
} catch (CJException ex) {
if (this.getPropertySet().getBooleanProperty(PropertyKey.createDatabaseIfNotExist).getValue()) {
sendCommand(getCommandBuilder().buildComQuery(getSharedSendPacket(),
"CREATE DATABASE IF NOT EXISTS " + StringUtils.quoteIdentifier(database, true)), false, 0);
sendCommand(getCommandBuilder().buildComInitDb(getSharedSendPacket(), database), false, 0);
} else {
throw ExceptionFactory.createCommunicationsException(this.getPropertySet(), this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), ex, getExceptionInterceptor());
}
}
}
@Override
public final NativePacketPayload readMessage(NativePacketPayload reuse) {
try {
NativePacketHeader header = this.packetReader.readHeader();
NativePacketPayload buf = this.packetReader.readMessage(Optional.ofNullable(reuse), header);
this.packetSequence = header.getMessageSequence();
return buf;
} catch (IOException ioEx) {
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), ioEx, getExceptionInterceptor());
} catch (OutOfMemoryError oom) {
throw ExceptionFactory.createException(oom.getMessage(), MysqlErrorNumbers.SQL_STATE_MEMORY_ALLOCATION_ERROR, 0, false, oom,
this.exceptionInterceptor);
}
}
public final NativePacketPayload probeMessage(NativePacketPayload reuse) {
try {
NativePacketHeader header = this.packetReader.probeHeader();
NativePacketPayload buf = this.packetReader.probeMessage(Optional.ofNullable(reuse), header);
this.packetSequence = header.getMessageSequence();
return buf;
} catch (IOException ioEx) {
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), ioEx, getExceptionInterceptor());
} catch (OutOfMemoryError oom) {
throw ExceptionFactory.createException(oom.getMessage(), MysqlErrorNumbers.SQL_STATE_MEMORY_ALLOCATION_ERROR, 0, false, oom,
this.exceptionInterceptor);
}
}
/**
* @param packet
* {@link Message}
* @param packetLen
* length of header + payload
*/
@Override
public final void send(Message packet, int packetLen) {
try {
if (this.maxAllowedPacket.getValue() > 0 && packetLen > this.maxAllowedPacket.getValue()) {
throw new CJPacketTooBigException(packetLen, this.maxAllowedPacket.getValue());
}
this.packetSequence++;
this.packetSender.send(packet.getByteBuffer(), packetLen, this.packetSequence);
//
// Don't hold on to large packets
//
if (packet == this.sharedSendPacket) {
reclaimLargeSharedSendPacket();
}
} catch (IOException ioEx) {
throw ExceptionFactory.createCommunicationsException(this.getPropertySet(), this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), ioEx, getExceptionInterceptor());
}
}
@Override
public final NativePacketPayload sendCommand(Message queryPacket, boolean skipCheck, int timeoutMillis) {
int command = queryPacket.getByteBuffer()[0];
this.commandCount++;
if (this.queryInterceptors != null) {
NativePacketPayload interceptedPacketPayload = (NativePacketPayload) invokeQueryInterceptorsPre(queryPacket, false);
if (interceptedPacketPayload != null) {
return interceptedPacketPayload;
}
}
this.packetReader.resetMessageSequence();
int oldTimeout = 0;
if (timeoutMillis != 0) {
try {
oldTimeout = this.socketConnection.getMysqlSocket().getSoTimeout();
this.socketConnection.getMysqlSocket().setSoTimeout(timeoutMillis);
} catch (IOException e) {
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), e, getExceptionInterceptor());
}
}
try {
checkForOutstandingStreamingData();
// Clear serverStatus...this value is guarded by an external mutex, as you can only ever be processing one command at a time
this.serverSession.setStatusFlags(0, true);
this.hadWarnings = false;
this.setWarningCount(0);
//
// Compressed input stream needs cleared at beginning of each command execution...
//
if (this.useCompression) {
int bytesLeft = this.socketConnection.getMysqlInput().available();
if (bytesLeft > 0) {
this.socketConnection.getMysqlInput().skip(bytesLeft);
}
}
try {
clearInputStream();
this.packetSequence = -1;
send(queryPacket, queryPacket.getPosition());
} catch (CJException ex) {
// don't wrap CJExceptions
throw ex;
} catch (Exception ex) {
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), ex, getExceptionInterceptor());
}
NativePacketPayload returnPacket = null;
if (!skipCheck) {
if ((command == NativeConstants.COM_STMT_EXECUTE) || (command == NativeConstants.COM_STMT_RESET)) {
this.packetReader.resetMessageSequence();
}
returnPacket = checkErrorMessage(command);
if (this.queryInterceptors != null) {
returnPacket = (NativePacketPayload) invokeQueryInterceptorsPost(queryPacket, returnPacket, false);
}
}
return returnPacket;
} catch (IOException ioEx) {
this.serverSession.preserveOldTransactionState();
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), ioEx, getExceptionInterceptor());
} catch (CJException e) {
this.serverSession.preserveOldTransactionState();
throw e;
} finally {
if (timeoutMillis != 0) {
try {
this.socketConnection.getMysqlSocket().setSoTimeout(oldTimeout);
} catch (IOException e) {
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), e, getExceptionInterceptor());
}
}
}
}
public void checkTransactionState() {
int transState = this.serverSession.getTransactionState();
if (transState == ServerSession.TRANSACTION_COMPLETED) {
this.transactionManager.transactionCompleted();
} else if (transState == ServerSession.TRANSACTION_STARTED) {
this.transactionManager.transactionBegun();
}
}
public NativePacketPayload checkErrorMessage() {
return checkErrorMessage(-1);
}
/**
* Checks for errors in the reply packet, and if none, returns the reply
* packet, ready for reading
*
* @param command
* the command being issued (if used)
* @return NativePacketPayload
* @throws CJException
* if an error packet was received
* @throws CJCommunicationsException
* if a database error occurs
*/
private NativePacketPayload checkErrorMessage(int command) {
NativePacketPayload resultPacket = null;
this.serverSession.setStatusFlags(0);
try {
// Check return value, if we get a java.io.EOFException, the server has gone away. We'll pass it on up the exception chain and let someone higher up
// decide what to do (barf, reconnect, etc).
resultPacket = readMessage(this.reusablePacket);
} catch (CJException ex) {
// Don't wrap CJExceptions
throw ex;
} catch (Exception fallThru) {
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), fallThru, getExceptionInterceptor());
}
checkErrorMessage(resultPacket);
return resultPacket;
}
public void checkErrorMessage(NativePacketPayload resultPacket) {
resultPacket.setPosition(0);
byte statusCode = (byte) resultPacket.readInteger(IntegerDataType.INT1);
// Error handling
if (statusCode == (byte) 0xff) {
String serverErrorMessage;
int errno = 2000;
errno = (int) resultPacket.readInteger(IntegerDataType.INT2);
String xOpen = null;
serverErrorMessage = resultPacket.readString(StringSelfDataType.STRING_TERM, this.serverSession.getCharsetSettings().getErrorMessageEncoding());
if (serverErrorMessage.charAt(0) == '#') {
// we have an SQLState
if (serverErrorMessage.length() > 6) {
xOpen = serverErrorMessage.substring(1, 6);
serverErrorMessage = serverErrorMessage.substring(6);
if (xOpen.equals("HY000")) {
xOpen = MysqlErrorNumbers.mysqlToSqlState(errno);
}
} else {
xOpen = MysqlErrorNumbers.mysqlToSqlState(errno);
}
} else {
xOpen = MysqlErrorNumbers.mysqlToSqlState(errno);
}
clearInputStream();
StringBuilder errorBuf = new StringBuilder();
String xOpenErrorMessage = MysqlErrorNumbers.get(xOpen);
boolean useOnlyServerErrorMessages = this.propertySet.getBooleanProperty(PropertyKey.useOnlyServerErrorMessages).getValue();
if (!useOnlyServerErrorMessages) {
if (xOpenErrorMessage != null) {
errorBuf.append(xOpenErrorMessage);
errorBuf.append(Messages.getString("Protocol.0"));
}
}
errorBuf.append(serverErrorMessage);
if (!useOnlyServerErrorMessages) {
if (xOpenErrorMessage != null) {
errorBuf.append("\"");
}
}
appendDeadlockStatusInformation(this.session, xOpen, errorBuf);
if (xOpen != null) {
if (xOpen.startsWith("22")) {
throw new DataTruncationException(errorBuf.toString(), 0, true, false, 0, 0, errno);
}
if (errno == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD) {
throw ExceptionFactory.createException(PasswordExpiredException.class, errorBuf.toString(), getExceptionInterceptor());
} else if (errno == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN) {
throw ExceptionFactory.createException(ClosedOnExpiredPasswordException.class, errorBuf.toString(), getExceptionInterceptor());
} else if (errno == MysqlErrorNumbers.ER_CLIENT_INTERACTION_TIMEOUT) {
throw ExceptionFactory.createException(CJCommunicationsException.class, errorBuf.toString(), null, getExceptionInterceptor());
}
}
throw ExceptionFactory.createException(errorBuf.toString(), xOpen, errno, false, null, getExceptionInterceptor());
}
}
private void reclaimLargeSharedSendPacket() {
if ((this.sharedSendPacket != null) && (this.sharedSendPacket.getCapacity() > 1048576)) {
this.sharedSendPacket = new NativePacketPayload(INITIAL_PACKET_SIZE);
}
}
public void clearInputStream() {
try {
int len;
// Due to a bug in some older Linux kernels (fixed after the patch "tcp: fix FIONREAD/SIOCINQ"), our SocketInputStream.available() may return 1 even
// if there is no data in the Stream, so, we need to check if InputStream.skip() actually skipped anything.
while ((len = this.socketConnection.getMysqlInput().available()) > 0 && this.socketConnection.getMysqlInput().skip(len) > 0) {
continue;
}
} catch (IOException ioEx) {
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), ioEx, getExceptionInterceptor());
}
}
/**
* Don't hold on to overly-large packets
*/
public void reclaimLargeReusablePacket() {
if ((this.reusablePacket != null) && (this.reusablePacket.getCapacity() > 1048576)) {
this.reusablePacket = new NativePacketPayload(INITIAL_PACKET_SIZE);
}
}
/**
* Build a query packet from the given string and send it to the server.
*
* @param
* extends {@link Resultset}
* @param callingQuery
* {@link Query}
* @param query
* query string
* @param characterEncoding
* Java encoding name
* @param maxRows
* rows limit
* @param streamResults
* whether a stream result should be created
* @param cachedMetadata
* use this metadata instead of the one provided on wire
* @param resultSetFactory
* {@link ProtocolEntityFactory}
* @return T instance
* @throws IOException
* if an i/o error occurs
*/
public final T sendQueryString(Query callingQuery, String query, String characterEncoding, int maxRows, boolean streamResults,
ColumnDefinition cachedMetadata, ProtocolEntityFactory resultSetFactory) throws IOException {
String statementComment = this.queryComment;
if (this.propertySet.getBooleanProperty(PropertyKey.includeThreadNamesAsStatementComment).getValue()) {
statementComment = (statementComment != null ? statementComment + ", " : "") + "java thread: " + Thread.currentThread().getName();
}
// We don't know exactly how many bytes we're going to get from the query. Since we're dealing with UTF-8, the max is 4, so pad it
// (4 * query) + space for headers
int packLength = 1 /* com_query */ + (query.length() * 4) + 2;
byte[] commentAsBytes = null;
if (statementComment != null) {
commentAsBytes = StringUtils.getBytes(statementComment, characterEncoding);
packLength += commentAsBytes.length;
packLength += 6; // for /*[space] [space]*/
}
boolean supportsQueryAttributes = this.serverSession.supportsQueryAttributes();
QueryAttributesBindings queryAttributes = null;
if (!supportsQueryAttributes && callingQuery != null && callingQuery.getQueryAttributesBindings().getCount() > 0) {
this.log.logWarn(Messages.getString("QueryAttributes.SetButNotSupported"));
}
if (supportsQueryAttributes) {
if (callingQuery != null) {
queryAttributes = callingQuery.getQueryAttributesBindings();
}
if (queryAttributes != null && queryAttributes.getCount() > 0) {
packLength += 9 /* parameter_count */ + 1 /* parameter_set_count */;
packLength += (queryAttributes.getCount() + 7) / 8 /* null_bitmap */ + 1 /* new_params_bind_flag */;
for (int i = 0; i < queryAttributes.getCount(); i++) {
BindValue queryAttribute = queryAttributes.getAttributeValue(i);
packLength += 2 /* parameter_type */ + queryAttribute.getName().length() /* parameter_name */ + queryAttribute.getBinaryLength();
}
} else {
packLength += 1 /* parameter_count */ + 1 /* parameter_set_count */;
}
}
// TODO decide how to safely use the shared this.sendPacket
NativePacketPayload sendPacket = new NativePacketPayload(packLength);
sendPacket.setPosition(0);
sendPacket.writeInteger(IntegerDataType.INT1, NativeConstants.COM_QUERY);
if (supportsQueryAttributes) {
if (queryAttributes != null && queryAttributes.getCount() > 0) {
sendPacket.writeInteger(IntegerDataType.INT_LENENC, queryAttributes.getCount());
sendPacket.writeInteger(IntegerDataType.INT_LENENC, 1); // parameter_set_count (always 1)
byte[] nullBitsBuffer = new byte[(queryAttributes.getCount() + 7) / 8];
for (int i = 0; i < queryAttributes.getCount(); i++) {
if (queryAttributes.getAttributeValue(i).isNull()) {
nullBitsBuffer[i >>> 3] |= 1 << (i & 7);
}
}
sendPacket.writeBytes(StringLengthDataType.STRING_VAR, nullBitsBuffer);
sendPacket.writeInteger(IntegerDataType.INT1, 1); // new_params_bind_flag (always 1)
queryAttributes.runThroughAll(a -> {
sendPacket.writeInteger(IntegerDataType.INT2, a.getFieldType());
sendPacket.writeBytes(StringSelfDataType.STRING_LENENC, a.getName().getBytes());
});
queryAttributes.runThroughAll(a -> {
if (!a.isNull()) {
a.writeAsQueryAttribute(sendPacket);
}
});
} else {
sendPacket.writeInteger(IntegerDataType.INT_LENENC, 0);
sendPacket.writeInteger(IntegerDataType.INT_LENENC, 1); // parameter_set_count (always 1)
}
}
sendPacket.setTag("QUERY");
if (commentAsBytes != null) {
sendPacket.writeBytes(StringLengthDataType.STRING_FIXED, Constants.SLASH_STAR_SPACE_AS_BYTES);
sendPacket.writeBytes(StringLengthDataType.STRING_FIXED, commentAsBytes);
sendPacket.writeBytes(StringLengthDataType.STRING_FIXED, Constants.SPACE_STAR_SLASH_SPACE_AS_BYTES);
}
if (!this.session.getServerSession().getCharsetSettings().doesPlatformDbCharsetMatches() && StringUtils.startsWithIgnoreCaseAndWs(query, "LOAD DATA")) {
sendPacket.writeBytes(StringLengthDataType.STRING_FIXED, StringUtils.getBytes(query));
} else {
sendPacket.writeBytes(StringLengthDataType.STRING_FIXED, StringUtils.getBytes(query, characterEncoding));
}
return sendQueryPacket(callingQuery, sendPacket, maxRows, streamResults, cachedMetadata, resultSetFactory);
}
/**
* Send a query stored in a packet to the server.
*
* @param
* extends {@link Resultset}
* @param callingQuery
* {@link Query}
* @param queryPacket
* {@link NativePacketPayload} containing query
* @param maxRows
* rows limit
* @param streamResults
* whether a stream result should be created
* @param cachedMetadata
* use this metadata instead of the one provided on wire
* @param resultSetFactory
* {@link ProtocolEntityFactory}
* @return T instance
* @throws IOException
* if an i/o error occurs
*/
public final T sendQueryPacket(Query callingQuery, NativePacketPayload queryPacket, int maxRows, boolean streamResults,
ColumnDefinition cachedMetadata, ProtocolEntityFactory resultSetFactory) throws IOException {
final long queryStartTime = getCurrentTimeNanosOrMillis();
this.statementExecutionDepth++;
byte[] queryBuf = queryPacket.getByteBuffer();
int oldPacketPosition = queryPacket.getPosition(); // save the packet position
int queryPosition = queryPacket.getTag("QUERY");
LazyString query = new LazyString(queryBuf, queryPosition, (oldPacketPosition - queryPosition));
try {
if (this.queryInterceptors != null) {
T interceptedResults = invokeQueryInterceptorsPre(query, callingQuery, false);
if (interceptedResults != null) {
return interceptedResults;
}
}
if (this.autoGenerateTestcaseScript) {
StringBuilder debugBuf = new StringBuilder(query.length() + 32);
generateQueryCommentBlock(debugBuf);
debugBuf.append(query);
debugBuf.append(';');
TestUtils.dumpTestcaseQuery(debugBuf.toString());
}
// Send query command and sql query string
NativePacketPayload resultPacket = sendCommand(queryPacket, false, 0);
final long queryEndTime = getCurrentTimeNanosOrMillis();
final long queryDuration = queryEndTime - queryStartTime;
if (callingQuery != null) {
callingQuery.setExecuteTime(queryDuration);
}
boolean queryWasSlow = this.logSlowQueries && (this.useAutoSlowLog ? this.metricsHolder.checkAbonormallyLongQuery(queryDuration)
: queryDuration > this.propertySet.getIntegerProperty(PropertyKey.slowQueryThresholdMillis).getValue());
long fetchBeginTime = this.profileSQL ? getCurrentTimeNanosOrMillis() : 0L;
T rs = readAllResults(maxRows, streamResults, resultPacket, false, cachedMetadata, resultSetFactory);
if (this.profileSQL || queryWasSlow) {
long fetchEndTime = this.profileSQL ? getCurrentTimeNanosOrMillis() : 0L;
// Extract the actual query from the network packet
boolean truncated = oldPacketPosition - queryPosition > this.maxQuerySizeToLog.getValue();
int extractPosition = truncated ? this.maxQuerySizeToLog.getValue() + queryPosition : oldPacketPosition;
String extractedQuery = StringUtils.toString(queryBuf, queryPosition, (extractPosition - queryPosition));
if (truncated) {
extractedQuery += Messages.getString("Protocol.2");
}
ProfilerEventHandler eventSink = this.session.getProfilerEventHandler();
if (this.logSlowQueries) {
if (queryWasSlow) {
eventSink.processEvent(ProfilerEvent.TYPE_SLOW_QUERY, this.session, callingQuery, rs, queryDuration, new Throwable(),
Messages.getString("Protocol.SlowQuery",
new Object[] { this.useAutoSlowLog ? " 95% of all queries " : String.valueOf(this.slowQueryThreshold),
this.queryTimingUnits, Long.valueOf(queryDuration), extractedQuery }));
if (this.propertySet.getBooleanProperty(PropertyKey.explainSlowQueries).getValue()) {
if (oldPacketPosition - queryPosition < MAX_QUERY_SIZE_TO_EXPLAIN) {
queryPacket.setPosition(queryPosition); // skip until the query is located in the packet
explainSlowQuery(query.toString(), extractedQuery);
} else {
this.log.logWarn(Messages.getString("Protocol.3", new Object[] { MAX_QUERY_SIZE_TO_EXPLAIN }));
}
}
}
if (this.serverSession.noGoodIndexUsed()) {
eventSink.processEvent(ProfilerEvent.TYPE_SLOW_QUERY, this.session, callingQuery, rs, queryDuration, new Throwable(),
Messages.getString("Protocol.4") + extractedQuery);
}
if (this.serverSession.noIndexUsed()) {
eventSink.processEvent(ProfilerEvent.TYPE_SLOW_QUERY, this.session, callingQuery, rs, queryDuration, new Throwable(),
Messages.getString("Protocol.5") + extractedQuery);
}
if (this.serverSession.queryWasSlow()) {
eventSink.processEvent(ProfilerEvent.TYPE_SLOW_QUERY, this.session, callingQuery, rs, queryDuration, new Throwable(),
Messages.getString("Protocol.ServerSlowQuery") + extractedQuery);
}
}
if (this.profileSQL) {
eventSink.processEvent(ProfilerEvent.TYPE_QUERY, this.session, callingQuery, rs, queryDuration, new Throwable(), extractedQuery);
eventSink.processEvent(ProfilerEvent.TYPE_FETCH, this.session, callingQuery, rs, (fetchEndTime - fetchBeginTime), new Throwable(), null);
}
}
if (this.hadWarnings) {
scanForAndThrowDataTruncation();
}
if (this.queryInterceptors != null) {
rs = invokeQueryInterceptorsPost(query, callingQuery, rs, false);
}
return rs;
} catch (CJException sqlEx) {
if (this.queryInterceptors != null) {
// TODO why doing this?
invokeQueryInterceptorsPost(query, callingQuery, null, false); // we don't do anything with the result set in this case
}
if (callingQuery != null) {
callingQuery.checkCancelTimeout();
}
throw sqlEx;
} finally {
this.statementExecutionDepth--;
}
}
public T invokeQueryInterceptorsPre(Supplier sql, Query interceptedQuery, boolean forceExecute) {
T previousResultSet = null;
for (int i = 0, s = this.queryInterceptors.size(); i < s; i++) {
QueryInterceptor interceptor = this.queryInterceptors.get(i);
boolean executeTopLevelOnly = interceptor.executeTopLevelOnly();
boolean shouldExecute = (executeTopLevelOnly && (this.statementExecutionDepth == 1 || forceExecute)) || (!executeTopLevelOnly);
if (shouldExecute) {
T interceptedResultSet = interceptor.preProcess(sql, interceptedQuery);
if (interceptedResultSet != null) {
previousResultSet = interceptedResultSet;
}
}
}
return previousResultSet;
}
/**
*
* @param
* extends {@link Message}
* @param queryPacket
* {@link NativePacketPayload} containing query
* @param forceExecute
* currently ignored
* @return M instance
*/
public M invokeQueryInterceptorsPre(M queryPacket, boolean forceExecute) {
M previousPacketPayload = null;
for (int i = 0, s = this.queryInterceptors.size(); i < s; i++) {
QueryInterceptor interceptor = this.queryInterceptors.get(i);
// TODO how to handle executeTopLevelOnly in such case ?
// boolean executeTopLevelOnly = interceptor.executeTopLevelOnly();
// boolean shouldExecute = (executeTopLevelOnly && (this.statementExecutionDepth == 1 || forceExecute)) || (!executeTopLevelOnly);
// if (shouldExecute) {
M interceptedPacketPayload = interceptor.preProcess(queryPacket);
if (interceptedPacketPayload != null) {
previousPacketPayload = interceptedPacketPayload;
}
// }
}
return previousPacketPayload;
}
public T invokeQueryInterceptorsPost(Supplier sql, Query interceptedQuery, T originalResultSet, boolean forceExecute) {
for (int i = 0, s = this.queryInterceptors.size(); i < s; i++) {
QueryInterceptor interceptor = this.queryInterceptors.get(i);
boolean executeTopLevelOnly = interceptor.executeTopLevelOnly();
boolean shouldExecute = (executeTopLevelOnly && (this.statementExecutionDepth == 1 || forceExecute)) || (!executeTopLevelOnly);
if (shouldExecute) {
T interceptedResultSet = interceptor.postProcess(sql, interceptedQuery, originalResultSet, this.serverSession);
if (interceptedResultSet != null) {
originalResultSet = interceptedResultSet;
}
}
}
return originalResultSet;
}
/**
*
* @param
* extends {@link Message}
* @param queryPacket
* {@link NativePacketPayload} containing query
* @param originalResponsePacket
* {@link NativePacketPayload} containing response
* @param forceExecute
* currently ignored
* @return T instance
*/
public M invokeQueryInterceptorsPost(M queryPacket, M originalResponsePacket, boolean forceExecute) {
for (int i = 0, s = this.queryInterceptors.size(); i < s; i++) {
QueryInterceptor interceptor = this.queryInterceptors.get(i);
// TODO how to handle executeTopLevelOnly in such case ?
// boolean executeTopLevelOnly = interceptor.executeTopLevelOnly();
// boolean shouldExecute = (executeTopLevelOnly && (this.statementExecutionDepth == 1 || forceExecute)) || (!executeTopLevelOnly);
// if (shouldExecute) {
M interceptedPacketPayload = interceptor.postProcess(queryPacket, originalResponsePacket);
if (interceptedPacketPayload != null) {
originalResponsePacket = interceptedPacketPayload;
}
// }
}
return originalResponsePacket;
}
public long getCurrentTimeNanosOrMillis() {
return this.useNanosForElapsedTime ? TimeUtil.getCurrentTimeNanosOrMillis() : System.currentTimeMillis();
}
public boolean hadWarnings() {
return this.hadWarnings;
}
public void setHadWarnings(boolean hadWarnings) {
this.hadWarnings = hadWarnings;
}
/**
* Runs an 'EXPLAIN' on the given query and dumps the results to the log
*
* @param query
* full query string
* @param truncatedQuery
* query string truncated for profiling
*
*/
public void explainSlowQuery(String query, String truncatedQuery) {
if (StringUtils.startsWithIgnoreCaseAndWs(truncatedQuery, EXPLAINABLE_STATEMENT)
|| (versionMeetsMinimum(5, 6, 3) && StringUtils.startsWithIgnoreCaseAndWs(truncatedQuery, EXPLAINABLE_STATEMENT_EXTENSION) != -1)) {
try {
NativePacketPayload resultPacket = sendCommand(getCommandBuilder().buildComQuery(getSharedSendPacket(), "EXPLAIN " + query), false, 0);
Resultset rs = readAllResults(-1, false, resultPacket, false, null, new ResultsetFactory(Type.FORWARD_ONLY, null));
StringBuilder explainResults = new StringBuilder(Messages.getString("Protocol.6"));
explainResults.append(truncatedQuery);
explainResults.append(Messages.getString("Protocol.7"));
appendResultSetSlashGStyle(explainResults, rs);
this.log.logWarn(explainResults.toString());
} catch (CJException sqlEx) {
throw sqlEx;
} catch (Exception ex) {
throw ExceptionFactory.createException(ex.getMessage(), ex, getExceptionInterceptor());
}
}
}
/**
* Reads and discards a single MySQL packet from the input stream.
*
* @throws CJException
* if the network fails while skipping the
* packet.
*/
public final void skipPacket() {
try {
int packetLength = this.packetReader.readHeader().getMessageSize();
this.socketConnection.getMysqlInput().skipFully(packetLength);
} catch (IOException ioEx) {
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), ioEx, getExceptionInterceptor());
}
}
/**
* Log-off of the MySQL server and close the socket.
*
*/
public final void quit() {
try {
try {
if (!ExportControlled.isSSLEstablished(this.socketConnection.getMysqlSocket())) { // Fix for Bug#56979 does not apply to secure sockets.
if (!this.socketConnection.getMysqlSocket().isClosed()) {
try {
// The response won't be read, this fixes BUG#56979 [Improper connection closing logic leads to TIME_WAIT sockets on server].
this.socketConnection.getMysqlSocket().shutdownInput();
} catch (UnsupportedOperationException e) {
// Ignore, some sockets do not support this method.
}
}
}
} catch (IOException e) {
// Can't do anything constructive about this.
}
this.packetSequence = -1;
NativePacketPayload packet = new NativePacketPayload(1);
send(getCommandBuilder().buildComQuit(packet), packet.getPosition());
} finally {
this.socketConnection.forceClose();
this.localInfileInputStream = null;
}
}
/**
* Returns the packet used for sending data (used by PreparedStatement) with position set to 0.
* Guarded by external synchronization on a mutex.
*
* @return A packet to send data with
*/
public NativePacketPayload getSharedSendPacket() {
if (this.sharedSendPacket == null) {
this.sharedSendPacket = new NativePacketPayload(INITIAL_PACKET_SIZE);
}
this.sharedSendPacket.setPosition(0);
return this.sharedSendPacket;
}
private void calculateSlowQueryThreshold() {
this.slowQueryThreshold = this.propertySet.getIntegerProperty(PropertyKey.slowQueryThresholdMillis).getValue();
if (this.propertySet.getBooleanProperty(PropertyKey.useNanosForElapsedTime).getValue()) {
long nanosThreshold = this.propertySet.getLongProperty(PropertyKey.slowQueryThresholdNanos).getValue();
if (nanosThreshold != 0) {
this.slowQueryThreshold = nanosThreshold;
} else {
this.slowQueryThreshold *= 1000000; // 1 million millis in a nano
}
}
}
/**
* Re-authenticates as the given user and password
*
* @param user
* user name
* @param password
* password
* @param database
* database name
*
*/
public void changeUser(String user, String password, String database) {
this.packetSequence = -1;
this.packetSender = this.packetSender.undecorateAll();
this.packetReader = this.packetReader.undecorateAll();
this.authProvider.changeUser(user, password, database);
}
protected boolean useNanosForElapsedTime() {
return this.useNanosForElapsedTime;
}
public long getSlowQueryThreshold() {
return this.slowQueryThreshold;
}
public int getCommandCount() {
return this.commandCount;
}
public void setQueryInterceptors(List queryInterceptors) {
this.queryInterceptors = queryInterceptors.isEmpty() ? null : queryInterceptors;
}
public List getQueryInterceptors() {
return this.queryInterceptors;
}
public void setSocketTimeout(int milliseconds) {
try {
Socket soc = this.socketConnection.getMysqlSocket();
if (soc != null) {
soc.setSoTimeout(milliseconds);
}
} catch (IOException e) {
throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("Protocol.8"), e, getExceptionInterceptor());
}
}
public void releaseResources() {
if (this.compressedPacketSender != null) {
this.compressedPacketSender.stop();
}
}
public void connect(String user, String password, String database) {
// session creation & initialization happens here
beforeHandshake();
this.authProvider.connect(user, password, database);
}
protected boolean isDataAvailable() {
try {
return this.socketConnection.getMysqlInput().available() > 0;
} catch (IOException ioEx) {
throw ExceptionFactory.createCommunicationsException(this.propertySet, this.serverSession, this.getPacketSentTimeHolder(),
this.getPacketReceivedTimeHolder(), ioEx, getExceptionInterceptor());
}
}
public NativePacketPayload getReusablePacket() {
return this.reusablePacket;
}
public int getWarningCount() {
return this.warningCount;
}
public void setWarningCount(int warningCount) {
this.warningCount = warningCount;
}
public void dumpPacketRingBuffer() {
// use local variable to allow unsynchronized usage of the buffer
LinkedList localPacketDebugRingBuffer = this.packetDebugRingBuffer;
if (localPacketDebugRingBuffer != null) {
StringBuilder dumpBuffer = new StringBuilder();
dumpBuffer.append("Last " + localPacketDebugRingBuffer.size() + " packets received from server, from oldest->newest:\n");
dumpBuffer.append("\n");
for (Iterator ringBufIter = localPacketDebugRingBuffer.iterator(); ringBufIter.hasNext();) {
dumpBuffer.append(ringBufIter.next());
dumpBuffer.append("\n");
}
this.log.logTrace(dumpBuffer.toString());
}
}
public boolean versionMeetsMinimum(int major, int minor, int subminor) {
return this.serverSession.getServerVersion().meetsMinimum(new ServerVersion(major, minor, subminor));
}
public static MysqlType findMysqlType(PropertySet propertySet, int mysqlTypeId, short colFlag, long length, LazyString tableName,
LazyString originalTableName, int collationIndex, String encoding) {
boolean isUnsigned = ((colFlag & MysqlType.FIELD_FLAG_UNSIGNED) > 0);
boolean isFromFunction = originalTableName.length() == 0;
boolean isBinary = ((colFlag & MysqlType.FIELD_FLAG_BINARY) > 0);
/**
* Is this field owned by a server-created temporary table?
*/
boolean isImplicitTemporaryTable = tableName.length() > 0 && tableName.toString().startsWith("#sql_");
boolean isOpaqueBinary = (isBinary && collationIndex == CharsetMapping.MYSQL_COLLATION_INDEX_binary && (mysqlTypeId == MysqlType.FIELD_TYPE_STRING
|| mysqlTypeId == MysqlType.FIELD_TYPE_VAR_STRING || mysqlTypeId == MysqlType.FIELD_TYPE_VARCHAR)) ?
// queries resolved by temp tables also have this 'signature', check for that
!isImplicitTemporaryTable : "binary".equalsIgnoreCase(encoding);
switch (mysqlTypeId) {
case MysqlType.FIELD_TYPE_DECIMAL:
case MysqlType.FIELD_TYPE_NEWDECIMAL:
return isUnsigned ? MysqlType.DECIMAL_UNSIGNED : MysqlType.DECIMAL;
case MysqlType.FIELD_TYPE_TINY:
// Adjust for pseudo-boolean
if (!isUnsigned && length == 1 && propertySet.getBooleanProperty(PropertyKey.tinyInt1isBit).getValue()) {
if (propertySet.getBooleanProperty(PropertyKey.transformedBitIsBoolean).getValue()) {
return MysqlType.BOOLEAN;
}
return MysqlType.BIT;
}
return isUnsigned ? MysqlType.TINYINT_UNSIGNED : MysqlType.TINYINT;
case MysqlType.FIELD_TYPE_SHORT:
return isUnsigned ? MysqlType.SMALLINT_UNSIGNED : MysqlType.SMALLINT;
case MysqlType.FIELD_TYPE_LONG:
return isUnsigned ? MysqlType.INT_UNSIGNED : MysqlType.INT;
case MysqlType.FIELD_TYPE_FLOAT:
return isUnsigned ? MysqlType.FLOAT_UNSIGNED : MysqlType.FLOAT;
case MysqlType.FIELD_TYPE_DOUBLE:
return isUnsigned ? MysqlType.DOUBLE_UNSIGNED : MysqlType.DOUBLE;
case MysqlType.FIELD_TYPE_NULL:
return MysqlType.NULL;
case MysqlType.FIELD_TYPE_TIMESTAMP:
return MysqlType.TIMESTAMP;
case MysqlType.FIELD_TYPE_LONGLONG:
return isUnsigned ? MysqlType.BIGINT_UNSIGNED : MysqlType.BIGINT;
case MysqlType.FIELD_TYPE_INT24:
return isUnsigned ? MysqlType.MEDIUMINT_UNSIGNED : MysqlType.MEDIUMINT;
case MysqlType.FIELD_TYPE_DATE:
return MysqlType.DATE;
case MysqlType.FIELD_TYPE_TIME:
return MysqlType.TIME;
case MysqlType.FIELD_TYPE_DATETIME:
return MysqlType.DATETIME;
case MysqlType.FIELD_TYPE_YEAR:
return MysqlType.YEAR;
case MysqlType.FIELD_TYPE_VARCHAR:
case MysqlType.FIELD_TYPE_VAR_STRING:
if (isOpaqueBinary && !(isFromFunction && propertySet.getBooleanProperty(PropertyKey.functionsNeverReturnBlobs).getValue())) {
return MysqlType.VARBINARY;
}
return MysqlType.VARCHAR;
case MysqlType.FIELD_TYPE_BIT:
//if (length > 1) {
// we need to pretend this is a full binary blob
//this.colFlag |= MysqlType.FIELD_FLAG_BINARY;
//this.colFlag |= MysqlType.FIELD_FLAG_BLOB;
//return MysqlType.VARBINARY;
//}
return MysqlType.BIT;
case MysqlType.FIELD_TYPE_JSON:
return MysqlType.JSON;
case MysqlType.FIELD_TYPE_ENUM:
return MysqlType.ENUM;
case MysqlType.FIELD_TYPE_SET:
return MysqlType.SET;
case MysqlType.FIELD_TYPE_TINY_BLOB:
if (!isBinary || collationIndex != CharsetMapping.MYSQL_COLLATION_INDEX_binary
|| propertySet.getBooleanProperty(PropertyKey.blobsAreStrings).getValue()
|| isFromFunction && (propertySet.getBooleanProperty(PropertyKey.functionsNeverReturnBlobs).getValue())) {
return MysqlType.TINYTEXT;
}
return MysqlType.TINYBLOB;
case MysqlType.FIELD_TYPE_MEDIUM_BLOB:
if (!isBinary || collationIndex != CharsetMapping.MYSQL_COLLATION_INDEX_binary
|| propertySet.getBooleanProperty(PropertyKey.blobsAreStrings).getValue()
|| isFromFunction && (propertySet.getBooleanProperty(PropertyKey.functionsNeverReturnBlobs).getValue())) {
return MysqlType.MEDIUMTEXT;
}
return MysqlType.MEDIUMBLOB;
case MysqlType.FIELD_TYPE_LONG_BLOB:
if (!isBinary || collationIndex != CharsetMapping.MYSQL_COLLATION_INDEX_binary
|| propertySet.getBooleanProperty(PropertyKey.blobsAreStrings).getValue()
|| isFromFunction && (propertySet.getBooleanProperty(PropertyKey.functionsNeverReturnBlobs).getValue())) {
return MysqlType.LONGTEXT;
}
return MysqlType.LONGBLOB;
case MysqlType.FIELD_TYPE_BLOB:
// Sometimes MySQL uses this protocol-level type for all possible BLOB variants,
// we can divine what the actual type is by the length reported
int newMysqlTypeId = mysqlTypeId;
// fixing initial type according to length
if (length <= MysqlType.TINYBLOB.getPrecision()) {
newMysqlTypeId = MysqlType.FIELD_TYPE_TINY_BLOB;
} else if (length <= MysqlType.BLOB.getPrecision()) {
if (!isBinary || collationIndex != CharsetMapping.MYSQL_COLLATION_INDEX_binary
|| propertySet.getBooleanProperty(PropertyKey.blobsAreStrings).getValue()
|| isFromFunction && (propertySet.getBooleanProperty(PropertyKey.functionsNeverReturnBlobs).getValue())) {
newMysqlTypeId = MysqlType.FIELD_TYPE_VARCHAR;
return MysqlType.TEXT;
}
return MysqlType.BLOB;
} else if (length <= MysqlType.MEDIUMBLOB.getPrecision()) {
newMysqlTypeId = MysqlType.FIELD_TYPE_MEDIUM_BLOB;
} else {
newMysqlTypeId = MysqlType.FIELD_TYPE_LONG_BLOB;
}
// call this method again with correct this.mysqlType set
return findMysqlType(propertySet, newMysqlTypeId, colFlag, length, tableName, originalTableName, collationIndex, encoding);
case MysqlType.FIELD_TYPE_STRING:
if (isOpaqueBinary && !propertySet.getBooleanProperty(PropertyKey.blobsAreStrings).getValue()) {
return MysqlType.BINARY;
}
return MysqlType.CHAR;
case MysqlType.FIELD_TYPE_GEOMETRY:
return MysqlType.GEOMETRY;
default:
return MysqlType.UNKNOWN;
}
}
/*
* Reading results
*/
@Override
public T read(Class requiredClass, ProtocolEntityFactory protocolEntityFactory) throws IOException {
@SuppressWarnings("unchecked")
ProtocolEntityReader sr = (ProtocolEntityReader) this.PROTOCOL_ENTITY_CLASS_TO_TEXT_READER
.get(requiredClass);
if (sr == null) {
throw ExceptionFactory.createException(FeatureNotAvailableException.class, "ProtocolEntityReader isn't available for class " + requiredClass);
}
return sr.read(protocolEntityFactory);
}
@Override
public T read(Class requiredClass, int maxRows, boolean streamResults, NativePacketPayload resultPacket,
boolean isBinaryEncoded, ColumnDefinition metadata, ProtocolEntityFactory protocolEntityFactory) throws IOException {
@SuppressWarnings("unchecked")
ProtocolEntityReader sr = isBinaryEncoded
? (ProtocolEntityReader) this.PROTOCOL_ENTITY_CLASS_TO_BINARY_READER.get(requiredClass)
: (ProtocolEntityReader) this.PROTOCOL_ENTITY_CLASS_TO_TEXT_READER.get(requiredClass);
if (sr == null) {
throw ExceptionFactory.createException(FeatureNotAvailableException.class, "ProtocolEntityReader isn't available for class " + requiredClass);
}
return sr.read(maxRows, streamResults, resultPacket, metadata, protocolEntityFactory);
}
/**
* Read next result set from multi-result chain.
*
* @param
* extends {@link ProtocolEntity}
* @param currentProtocolEntity
* T instance
* @param maxRows
* rows limit
* @param streamResults
* whether a stream result should be created
* @param isBinaryEncoded
* true for binary protocol
* @param resultSetFactory
* {@link ProtocolEntityFactory}
* @return T instance
* @throws IOException
* if an i/o error occurs
*/
public T readNextResultset(T currentProtocolEntity, int maxRows, boolean streamResults, boolean isBinaryEncoded,
ProtocolEntityFactory resultSetFactory) throws IOException {
T result = null;
if (Resultset.class.isAssignableFrom(currentProtocolEntity.getClass()) && this.serverSession.useMultiResults()) {
if (this.serverSession.hasMoreResults()) {
T currentResultSet = currentProtocolEntity;
T newResultSet;
do {
NativePacketPayload fieldPacket = checkErrorMessage();
fieldPacket.setPosition(0);
newResultSet = read(Resultset.class, maxRows, streamResults, fieldPacket, isBinaryEncoded, null, resultSetFactory);
((Resultset) currentResultSet).setNextResultset((Resultset) newResultSet);
currentResultSet = newResultSet;
if (result == null) {
// we should return the first result set in chain
result = currentResultSet;
}
} while (streamResults && this.serverSession.hasMoreResults() // we need to consume all result sets which don't contain rows from streamer right now,
&& !((Resultset) currentResultSet).hasRows()); // because next data portion from streamer is available only via ResultsetRows.next()
}
}
return result;
}
public T readAllResults(int maxRows, boolean streamResults, NativePacketPayload resultPacket, boolean isBinaryEncoded,
ColumnDefinition metadata, ProtocolEntityFactory resultSetFactory) throws IOException {
resultPacket.setPosition(0);
T topLevelResultSet = read(Resultset.class, maxRows, streamResults, resultPacket, isBinaryEncoded, metadata, resultSetFactory);
if (this.serverSession.hasMoreResults()) {
T currentResultSet = topLevelResultSet;
if (streamResults) {
currentResultSet = readNextResultset(currentResultSet, maxRows, true, isBinaryEncoded, resultSetFactory);
} else {
while (this.serverSession.hasMoreResults()) {
currentResultSet = readNextResultset(currentResultSet, maxRows, false, isBinaryEncoded, resultSetFactory);
}
clearInputStream();
}
}
if (this.hadWarnings) {
scanForAndThrowDataTruncation();
}
reclaimLargeReusablePacket();
return topLevelResultSet;
}
@SuppressWarnings("unchecked")
public final T readServerStatusForResultSets(NativePacketPayload rowPacket, boolean saveOldStatus) {
T result = null;
if (rowPacket.isEOFPacket()) {
// read EOF packet
rowPacket.setPosition(1); // skip the packet signature header
this.warningCount = (int) rowPacket.readInteger(IntegerDataType.INT2);
if (this.warningCount > 0) {
this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand()
}
this.serverSession.setStatusFlags((int) rowPacket.readInteger(IntegerDataType.INT2), saveOldStatus);
checkTransactionState();
} else {
// read OK packet
OkPacket ok = OkPacket.parse(rowPacket, this.serverSession.getCharsetSettings().getErrorMessageEncoding());
result = (T) ok;
this.serverSession.setStatusFlags(ok.getStatusFlags(), saveOldStatus);
this.serverSession.getServerSessionStateController().setSessionStateChanges(ok.getSessionStateChanges());
checkTransactionState();
this.warningCount = ok.getWarningCount();
if (this.warningCount > 0) {
this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand()
}
}
return result;
}
@Override
public T readQueryResult(ResultBuilder resultBuilder) {
throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported");
}
public InputStream getLocalInfileInputStream() {
return this.localInfileInputStream;
}
public void setLocalInfileInputStream(InputStream stream) {
this.localInfileInputStream = stream;
}
/**
* Reads and sends a file to the server for LOAD DATA LOCAL INFILE
*
* @param fileName
* the file name to send.
* @return NativePacketPayload
*/
public final NativePacketPayload sendFileToServer(String fileName) {
NativePacketPayload filePacket = (this.loadFileBufRef == null) ? null : this.loadFileBufRef.get();
int bigPacketLength = Math.min(this.maxAllowedPacket.getValue() - (NativeConstants.HEADER_LENGTH * 3),
alignPacketSize(this.maxAllowedPacket.getValue() - 16, 4096) - (NativeConstants.HEADER_LENGTH * 3));
int oneMeg = 1024 * 1024;
int smallerPacketSizeAligned = Math.min(oneMeg - (NativeConstants.HEADER_LENGTH * 3),
alignPacketSize(oneMeg - 16, 4096) - (NativeConstants.HEADER_LENGTH * 3));
int packetLength = Math.min(smallerPacketSizeAligned, bigPacketLength);
if (filePacket == null) {
try {
filePacket = new NativePacketPayload(packetLength);
this.loadFileBufRef = new SoftReference<>(filePacket);
} catch (OutOfMemoryError oom) {
throw ExceptionFactory.createException(Messages.getString("MysqlIO.111", new Object[] { packetLength }),
MysqlErrorNumbers.SQL_STATE_MEMORY_ALLOCATION_ERROR, 0, false, oom, this.exceptionInterceptor);
}
}
filePacket.setPosition(0);
byte[] fileBuf = new byte[packetLength];
BufferedInputStream fileIn = null;
try {
fileIn = getFileStream(fileName);
int bytesRead = 0;
while ((bytesRead = fileIn.read(fileBuf)) != -1) {
filePacket.setPosition(0);
filePacket.writeBytes(StringLengthDataType.STRING_FIXED, fileBuf, 0, bytesRead);
send(filePacket, filePacket.getPosition());
}
} catch (IOException ioEx) {
boolean isParanoid = this.propertySet.getBooleanProperty(PropertyKey.paranoid).getValue();
StringBuilder messageBuf = new StringBuilder(Messages.getString("MysqlIO.62"));
if (fileName != null && !isParanoid) {
messageBuf.append("'");
messageBuf.append(fileName);
messageBuf.append("'");
}
messageBuf.append(Messages.getString("MysqlIO.63"));
if (!isParanoid) {
messageBuf.append(Messages.getString("MysqlIO.64"));
messageBuf.append(Util.stackTraceToString(ioEx));
}
throw ExceptionFactory.createException(messageBuf.toString(), ioEx, this.exceptionInterceptor);
} finally {
if (fileIn != null) {
try {
fileIn.close();
} catch (Exception ex) {
throw ExceptionFactory.createException(Messages.getString("MysqlIO.65"), ex, this.exceptionInterceptor);
}
fileIn = null;
} else {
// File open failed, but server needs one packet.
filePacket.setPosition(0);
send(filePacket, filePacket.getPosition());
checkErrorMessage(); // To clear response off of queue.
}
}
// send empty packet to mark EOF
filePacket.setPosition(0);
send(filePacket, filePacket.getPosition());
return checkErrorMessage();
}
private BufferedInputStream getFileStream(String fileName) throws IOException {
RuntimeProperty allowLoadLocalInfile = this.propertySet.getBooleanProperty(PropertyKey.allowLoadLocalInfile);
RuntimeProperty allowLoadLocaInfileInPath = this.propertySet.getStringProperty(PropertyKey.allowLoadLocalInfileInPath);
RuntimeProperty allowUrlInLocalInfile = this.propertySet.getBooleanProperty(PropertyKey.allowUrlInLocalInfile);
if (!allowLoadLocalInfile.getValue() && !allowLoadLocaInfileInPath.isExplicitlySet()) {
throw ExceptionFactory.createException(Messages.getString("MysqlIO.LoadDataLocalNotAllowed"), this.exceptionInterceptor);
}
if (allowLoadLocalInfile.getValue()) {
// "LOAD DATA LOCAL INFILE" is enabled without restrictions.
InputStream hookedStream = getLocalInfileInputStream();
if (hookedStream != null) {
return new BufferedInputStream(hookedStream);
} else if (allowUrlInLocalInfile.getValue()) {
// Look for ':'.
if (fileName.indexOf(':') != -1) {
try {
URL urlFromFileName = new URL(fileName);
return new BufferedInputStream(urlFromFileName.openStream());
} catch (MalformedURLException e) {
// Ignore and fall back to trying this as a file input stream.
}
}
}
return new BufferedInputStream(new FileInputStream(fileName));
}
// Given the code paths above, allowLoadLocaInfileInPath.isExplicitlySet() must be true and restrictions to "LOAD DATA LOCAL INFILE" apply.
String safePathValue = allowLoadLocaInfileInPath.getValue();
Path safePath;
if (safePathValue.length() == 0) {
throw ExceptionFactory.createException(
Messages.getString("MysqlIO.60", new Object[] { safePathValue, PropertyKey.allowLoadLocalInfileInPath.getKeyName() }),
this.exceptionInterceptor);
}
try {
safePath = Paths.get(safePathValue).toRealPath();
} catch (IOException | InvalidPathException e) {
throw ExceptionFactory.createException(
Messages.getString("MysqlIO.60", new Object[] { safePathValue, PropertyKey.allowLoadLocalInfileInPath.getKeyName() }), e,
this.exceptionInterceptor);
}
if (allowUrlInLocalInfile.getValue()) {
try {
URL urlFromFileName = new URL(fileName);
if (!urlFromFileName.getProtocol().equalsIgnoreCase("file")) {
throw ExceptionFactory.createException(Messages.getString("MysqlIO.66", new Object[] { urlFromFileName.getProtocol() }),
this.exceptionInterceptor);
}
try {
InetAddress addr = InetAddress.getByName(urlFromFileName.getHost());
if (!addr.isLoopbackAddress()) {
throw ExceptionFactory.createException(Messages.getString("MysqlIO.67", new Object[] { urlFromFileName.getHost() }),
this.exceptionInterceptor);
}
} catch (UnknownHostException e) {
throw ExceptionFactory.createException(Messages.getString("MysqlIO.68", new Object[] { fileName }), e, this.exceptionInterceptor);
}
Path filePath = null;
try {
filePath = Paths.get(urlFromFileName.toURI()).toRealPath();
} catch (InvalidPathException e) {
// Windows paths often can't be extracted, but the URL is still valid.
String pathString = urlFromFileName.getPath();
if (pathString.indexOf(':') != -1 && (pathString.startsWith("/") || pathString.startsWith("\\"))) {
pathString = pathString.replaceFirst("^[/\\\\]*", "");
}
filePath = Paths.get(pathString).toRealPath();
} catch (IllegalArgumentException e) {
// Try the path directly.
filePath = Paths.get(urlFromFileName.getPath()).toRealPath();
}
if (!filePath.startsWith(safePath)) {
throw ExceptionFactory.createException(Messages.getString("MysqlIO.61", new Object[] { filePath, safePath }), this.exceptionInterceptor);
}
return new BufferedInputStream(urlFromFileName.openStream());
} catch (MalformedURLException | URISyntaxException e) {
// Fall back to trying this as a file input stream.
}
}
Path filePath = Paths.get(fileName).toRealPath();
if (!filePath.startsWith(safePath)) {
throw ExceptionFactory.createException(Messages.getString("MysqlIO.61", new Object[] { filePath, safePath }), this.exceptionInterceptor);
}
return new BufferedInputStream(new FileInputStream(filePath.toFile()));
}
private int alignPacketSize(int a, int l) {
return ((((a) + (l)) - 1) & ~((l) - 1));
}
private ResultsetRows streamingData = null;
public ResultsetRows getStreamingData() {
return this.streamingData;
}
public void setStreamingData(ResultsetRows streamingData) {
this.streamingData = streamingData;
}
public void checkForOutstandingStreamingData() {
if (this.streamingData != null) {
boolean shouldClobber = this.propertySet.getBooleanProperty(PropertyKey.clobberStreamingResults).getValue();
if (!shouldClobber) {
throw ExceptionFactory.createException(Messages.getString("MysqlIO.39") + this.streamingData + Messages.getString("MysqlIO.40")
+ Messages.getString("MysqlIO.41") + Messages.getString("MysqlIO.42"), this.exceptionInterceptor);
}
// Close the result set
this.streamingData.getOwner().closeOwner(false);
// clear any pending data....
clearInputStream();
}
}
public void unsetStreamingData(ResultsetRows streamer) {
if (this.streamingData == null) {
throw ExceptionFactory.createException(Messages.getString("MysqlIO.17") + streamer + Messages.getString("MysqlIO.18"), this.exceptionInterceptor);
}
// don't try to discard streamer other than passed in; it's probably already replaced by the next result in a multi-resultset
if (streamer == this.streamingData) {
this.streamingData = null;
}
}
public void scanForAndThrowDataTruncation() {
if (this.streamingData == null && this.propertySet.getBooleanProperty(PropertyKey.jdbcCompliantTruncation).getValue() && getWarningCount() > 0) {
int warningCountOld = getWarningCount();
convertShowWarningsToSQLWarnings(true);
setWarningCount(warningCountOld);
}
}
public StringBuilder generateQueryCommentBlock(StringBuilder buf) {
buf.append("/* conn id ");
buf.append(getServerSession().getCapabilities().getThreadId());
buf.append(" clock: ");
buf.append(System.currentTimeMillis());
buf.append(" */ ");
return buf;
}
public BaseMetricsHolder getMetricsHolder() {
return this.metricsHolder;
}
@Override
public String getQueryComment() {
return this.queryComment;
}
@Override
public void setQueryComment(String comment) {
this.queryComment = comment;
}
private void appendDeadlockStatusInformation(Session sess, String xOpen, StringBuilder errorBuf) {
if (sess.getPropertySet().getBooleanProperty(PropertyKey.includeInnodbStatusInDeadlockExceptions).getValue() && xOpen != null
&& (xOpen.startsWith("40") || xOpen.startsWith("41")) && getStreamingData() == null) {
try {
NativePacketPayload resultPacket = sendCommand(getCommandBuilder().buildComQuery(getSharedSendPacket(), "SHOW ENGINE INNODB STATUS"), false, 0);
Resultset rs = readAllResults(-1, false, resultPacket, false, null, new ResultsetFactory(Type.FORWARD_ONLY, null));
int colIndex = 0;
Field f = null;
for (int i = 0; i < rs.getColumnDefinition().getFields().length; i++) {
f = rs.getColumnDefinition().getFields()[i];
if ("Status".equals(f.getName())) {
colIndex = i;
break;
}
}
ValueFactory vf = new StringValueFactory(this.propertySet);
Row r;
if ((r = rs.getRows().next()) != null) {
errorBuf.append("\n\n").append(r.getValue(colIndex, vf));
} else {
errorBuf.append("\n\n").append(Messages.getString("MysqlIO.NoInnoDBStatusFound"));
}
} catch (IOException | CJException ex) {
errorBuf.append("\n\n").append(Messages.getString("MysqlIO.InnoDBStatusFailed")).append("\n\n").append(Util.stackTraceToString(ex));
}
}
if (sess.getPropertySet().getBooleanProperty(PropertyKey.includeThreadDumpInDeadlockExceptions).getValue()) {
errorBuf.append("\n\n*** Java threads running at time of deadlock ***\n\n");
ThreadMXBean threadMBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadMBean.getAllThreadIds();
ThreadInfo[] threads = threadMBean.getThreadInfo(threadIds, Integer.MAX_VALUE);
List activeThreads = new ArrayList<>();
for (ThreadInfo info : threads) {
if (info != null) {
activeThreads.add(info);
}
}
for (ThreadInfo threadInfo : activeThreads) {
// "Thread-60" daemon prio=1 tid=0x093569c0 nid=0x1b99 in Object.wait()
errorBuf.append('"').append(threadInfo.getThreadName()).append("\" tid=").append(threadInfo.getThreadId()).append(" ")
.append(threadInfo.getThreadState());
if (threadInfo.getLockName() != null) {
errorBuf.append(" on lock=").append(threadInfo.getLockName());
}
if (threadInfo.isSuspended()) {
errorBuf.append(" (suspended)");
}
if (threadInfo.isInNative()) {
errorBuf.append(" (running in native)");
}
StackTraceElement[] stackTrace = threadInfo.getStackTrace();
if (stackTrace.length > 0) {
errorBuf.append(" in ");
errorBuf.append(stackTrace[0].getClassName()).append(".");
errorBuf.append(stackTrace[0].getMethodName()).append("()");
}
errorBuf.append("\n");
if (threadInfo.getLockOwnerName() != null) {
errorBuf.append("\t owned by ").append(threadInfo.getLockOwnerName()).append(" Id=").append(threadInfo.getLockOwnerId()).append("\n");
}
for (int j = 0; j < stackTrace.length; j++) {
StackTraceElement ste = stackTrace[j];
errorBuf.append("\tat ").append(ste.toString()).append("\n");
}
}
}
}
private StringBuilder appendResultSetSlashGStyle(StringBuilder appendTo, Resultset rs) {
Field[] fields = rs.getColumnDefinition().getFields();
int maxWidth = 0;
for (int i = 0; i < fields.length; i++) {
if (fields[i].getColumnLabel().length() > maxWidth) {
maxWidth = fields[i].getColumnLabel().length();
}
}
int rowCount = 1;
Row r;
while ((r = rs.getRows().next()) != null) {
appendTo.append("*************************** ");
appendTo.append(rowCount++);
appendTo.append(". row ***************************\n");
for (int i = 0; i < fields.length; i++) {
int leftPad = maxWidth - fields[i].getColumnLabel().length();
for (int j = 0; j < leftPad; j++) {
appendTo.append(" ");
}
appendTo.append(fields[i].getColumnLabel()).append(": ");
String stringVal = r.getValue(i, new StringValueFactory(this.propertySet));
appendTo.append(stringVal != null ? stringVal : "NULL").append("\n");
}
appendTo.append("\n");
}
return appendTo;
}
/**
* Turns output of 'SHOW WARNINGS' into JDBC SQLWarning instances.
*
* If 'forTruncationOnly' is true, only looks for truncation warnings, and
* actually throws DataTruncation as an exception.
*
* @param forTruncationOnly
* if this method should only scan for data truncation warnings
*
* @return the SQLWarning chain (or null if no warnings)
*/
public SQLWarning convertShowWarningsToSQLWarnings(boolean forTruncationOnly) {
if (this.warningCount == 0) {
return null;
}
SQLWarning currentWarning = null;
ResultsetRows rows = null;
try {
/*
* +---------+------+---------------------------------------------+
* | Level ..| Code | Message ....................................|
* +---------+------+---------------------------------------------+
* | Warning | 1265 | Data truncated for column 'field1' at row 1 |
* +---------+------+---------------------------------------------+
*/
NativePacketPayload resultPacket = sendCommand(getCommandBuilder().buildComQuery(getSharedSendPacket(), "SHOW WARNINGS"), false, 0);
Resultset warnRs = readAllResults(-1, this.warningCount > 99 /* stream large warning counts */, resultPacket, false, null,
new ResultsetFactory(Type.FORWARD_ONLY, Concurrency.READ_ONLY));
int codeFieldIndex = warnRs.getColumnDefinition().findColumn("Code", false, 1) - 1;
int messageFieldIndex = warnRs.getColumnDefinition().findColumn("Message", false, 1) - 1;
ValueFactory svf = new StringValueFactory(this.propertySet);
ValueFactory ivf = new IntegerValueFactory(this.propertySet);
rows = warnRs.getRows();
Row r;
while ((r = rows.next()) != null) {
int code = r.getValue(codeFieldIndex, ivf);
if (forTruncationOnly) {
if (code == MysqlErrorNumbers.ER_WARN_DATA_TRUNCATED || code == MysqlErrorNumbers.ER_WARN_DATA_OUT_OF_RANGE) {
DataTruncation newTruncation = new MysqlDataTruncation(r.getValue(messageFieldIndex, svf), 0, false, false, 0, 0, code);
if (currentWarning == null) {
currentWarning = newTruncation;
} else {
currentWarning.setNextWarning(newTruncation);
}
}
} else {
//String level = warnRs.getString("Level");
String message = r.getValue(messageFieldIndex, svf);
SQLWarning newWarning = new SQLWarning(message, MysqlErrorNumbers.mysqlToSqlState(code), code);
if (currentWarning == null) {
currentWarning = newWarning;
} else {
currentWarning.setNextWarning(newWarning);
}
}
}
if (forTruncationOnly && (currentWarning != null)) {
throw ExceptionFactory.createException(currentWarning.getMessage(), currentWarning);
}
return currentWarning;
} catch (IOException ex) {
throw ExceptionFactory.createException(ex.getMessage(), ex);
} finally {
if (rows != null) {
rows.close();
}
}
}
@Override
public ColumnDefinition readMetadata() {
throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported");
}
@Override
public void close() throws IOException {
throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported");
}
/**
* Configures the client's timezone if required.
*
* @throws CJException
* if the timezone the server is configured to use can't be
* mapped to a Java timezone.
*/
public void configureTimeZone() {
String connectionTimeZone = getPropertySet().getStringProperty(PropertyKey.connectionTimeZone).getValue();
TimeZone selectedTz = null;
if (connectionTimeZone == null || StringUtils.isEmptyOrWhitespaceOnly(connectionTimeZone) || "LOCAL".equals(connectionTimeZone)) {
selectedTz = TimeZone.getDefault();
} else if ("SERVER".equals(connectionTimeZone)) {
// Session time zone will be detected after the first ServerSession.getSessionTimeZone() call.
return;
} else {
selectedTz = TimeZone.getTimeZone(ZoneId.of(connectionTimeZone)); // TODO use ZoneId.of(String zoneId, Map aliasMap) for custom abbreviations support
}
this.serverSession.setSessionTimeZone(selectedTz);
if (getPropertySet().getBooleanProperty(PropertyKey.forceConnectionTimeZoneToSession).getValue()) {
// TODO don't send 'SET SESSION time_zone' if time_zone is already equal to the selectedTz (but it requires time zone detection)
StringBuilder query = new StringBuilder("SET SESSION time_zone='");
ZoneId zid = selectedTz.toZoneId().normalized();
if (zid instanceof ZoneOffset) {
String offsetStr = ((ZoneOffset) zid).getId().replace("Z", "+00:00");
query.append(offsetStr);
this.serverSession.getServerVariables().put("time_zone", offsetStr);
} else {
query.append(selectedTz.getID());
this.serverSession.getServerVariables().put("time_zone", selectedTz.getID());
}
query.append("'");
sendCommand(getCommandBuilder().buildComQuery(null, query.toString()), false, 0);
}
}
@Override
public void initServerSession() {
configureTimeZone();
if (this.serverSession.getServerVariables().containsKey("max_allowed_packet")) {
int serverMaxAllowedPacket = this.serverSession.getServerVariable("max_allowed_packet", -1);
// use server value if maxAllowedPacket hasn't been given, or max_allowed_packet is smaller
if (serverMaxAllowedPacket != -1 && (!this.maxAllowedPacket.isExplicitlySet() || serverMaxAllowedPacket < this.maxAllowedPacket.getValue())) {
this.maxAllowedPacket.setValue(serverMaxAllowedPacket);
}
if (this.useServerPrepStmts.getValue()) {
RuntimeProperty blobSendChunkSize = this.propertySet.getProperty(PropertyKey.blobSendChunkSize);
int preferredBlobSendChunkSize = blobSendChunkSize.getValue();
// LONG_DATA and MySQLIO packet header size
int packetHeaderSize = ServerPreparedQuery.BLOB_STREAM_READ_BUF_SIZE + 11;
int allowedBlobSendChunkSize = Math.min(preferredBlobSendChunkSize, this.maxAllowedPacket.getValue()) - packetHeaderSize;
if (allowedBlobSendChunkSize <= 0) {
throw ExceptionFactory.createException(Messages.getString("Connection.15", new Object[] { packetHeaderSize }),
MysqlErrorNumbers.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, 0, false, null, this.exceptionInterceptor);
}
blobSendChunkSize.setValue(allowedBlobSendChunkSize);
}
}
this.serverSession.getCharsetSettings().configurePostHandshake(false);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy