Index: 3rdParty_sources/mysql-connector/com/mysql/cj/AbstractPreparedQuery.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/AbstractPreparedQuery.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/AbstractPreparedQuery.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.ReadableProperty; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.protocol.Message; +import com.mysql.cj.protocol.a.NativeConstants; +import com.mysql.cj.protocol.a.NativeConstants.IntegerDataType; +import com.mysql.cj.protocol.a.NativeConstants.StringLengthDataType; +import com.mysql.cj.protocol.a.NativePacketPayload; +import com.mysql.cj.util.StringUtils; + +// TODO should not be protocol-specific +public abstract class AbstractPreparedQuery> extends AbstractQuery implements PreparedQuery { + + protected ParseInfo parseInfo; + + protected T queryBindings = null; + + /** The SQL that was passed in to 'prepare' */ + protected String originalSql = null; + + /** The number of parameters in this PreparedStatement */ + protected int parameterCount; + + protected ReadableProperty autoClosePStmtStreams; + + /** Command index of currently executing batch command. */ + protected int batchCommandIndex = -1; + + protected ReadableProperty useStreamLengthsInPrepStmts; + + private byte[] streamConvertBuf = null; + + private boolean usingAnsiMode; + + public AbstractPreparedQuery(NativeSession sess) { + super(sess); + + this.autoClosePStmtStreams = this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_autoClosePStmtStreams); + this.useStreamLengthsInPrepStmts = this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useStreamLengthsInPrepStmts); + this.usingAnsiMode = !this.session.getServerSession().useAnsiQuotedIdentifiers(); + } + + @Override + public void closeQuery() { + this.streamConvertBuf = null; + super.closeQuery(); + } + + public ParseInfo getParseInfo() { + return this.parseInfo; + } + + public void setParseInfo(ParseInfo parseInfo) { + this.parseInfo = parseInfo; + } + + public String getOriginalSql() { + return this.originalSql; + } + + public void setOriginalSql(String originalSql) { + this.originalSql = originalSql; + } + + public int getParameterCount() { + return this.parameterCount; + } + + public void setParameterCount(int parameterCount) { + this.parameterCount = parameterCount; + } + + @Override + public T getQueryBindings() { + return this.queryBindings; + } + + @Override + public void setQueryBindings(T queryBindings) { + this.queryBindings = queryBindings; + } + + public int getBatchCommandIndex() { + return this.batchCommandIndex; + } + + public void setBatchCommandIndex(int batchCommandIndex) { + this.batchCommandIndex = batchCommandIndex; + } + + /** + * Computes the optimum number of batched parameter lists to send + * without overflowing max_allowed_packet. + * + * @param numBatchedArgs + */ + public int computeBatchSize(int numBatchedArgs) { + long[] combinedValues = computeMaxParameterSetSizeAndBatchSize(numBatchedArgs); + + long maxSizeOfParameterSet = combinedValues[0]; + long sizeOfEntireBatch = combinedValues[1]; + + if (sizeOfEntireBatch < this.maxAllowedPacket.getValue() - this.originalSql.length()) { + return numBatchedArgs; + } + + return (int) Math.max(1, (this.maxAllowedPacket.getValue() - this.originalSql.length()) / maxSizeOfParameterSet); + } + + /** + * Method checkNullOrEmptyQuery. + * + * @param sql + * the SQL to check + * + * @throws WrongArgumentException + * if query is null or empty. + */ + public void checkNullOrEmptyQuery(String sql) { + if (sql == null) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedQuery.0"), this.session.getExceptionInterceptor()); + } + + if (sql.length() == 0) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedQuery.1"), this.session.getExceptionInterceptor()); + } + } + + public String asSql() { + return asSql(false); + } + + public String asSql(boolean quoteStreamsAndUnknowns) { + StringBuilder buf = new StringBuilder(); + + Object batchArg = null; + if (this.batchCommandIndex != -1) { + batchArg = this.batchedArgs.get(this.batchCommandIndex); + } + + byte[][] staticSqlStrings = this.parseInfo.getStaticSql(); + for (int i = 0; i < this.parameterCount; ++i) { + buf.append(this.charEncoding != null ? StringUtils.toString(staticSqlStrings[i], this.charEncoding) : StringUtils.toString(staticSqlStrings[i])); + + byte val[] = null; + if (batchArg != null && batchArg instanceof String) { + buf.append((String) batchArg); + continue; + } + val = this.batchCommandIndex == -1 ? (this.queryBindings == null ? null : this.queryBindings.getBindValues()[i].getByteValue()) + : ((QueryBindings) batchArg).getBindValues()[i].getByteValue(); + + boolean isStreamParam = this.batchCommandIndex == -1 ? (this.queryBindings == null ? false : this.queryBindings.getBindValues()[i].isStream()) + : ((QueryBindings) batchArg).getBindValues()[i].isStream(); + + if ((val == null) && !isStreamParam) { + buf.append(quoteStreamsAndUnknowns ? "'** NOT SPECIFIED **'" : "** NOT SPECIFIED **"); + } else if (isStreamParam) { + buf.append(quoteStreamsAndUnknowns ? "'** STREAM DATA **'" : "** STREAM DATA **"); + } else { + buf.append(StringUtils.toString(val, this.charEncoding)); + } + } + + buf.append(this.charEncoding != null ? StringUtils.toString(staticSqlStrings[this.parameterCount], this.charEncoding) + : StringUtils.toAsciiString(staticSqlStrings[this.parameterCount])); + + return buf.toString(); + } + + protected abstract long[] computeMaxParameterSetSizeAndBatchSize(int numBatchedArgs); + + /** + * Creates the packet that contains the query to be sent to the server. + * + * @return A Buffer filled with the query representing the + * PreparedStatement. + */ + @Override + public M fillSendPacket() { + synchronized (this) { + return fillSendPacket(this.queryBindings); + } + } + + /** + * Creates the packet that contains the query to be sent to the server. + * + * @param bindValues + * values + * + * @return a Buffer filled with the query that represents this statement + */ + @SuppressWarnings("unchecked") + @Override + public M fillSendPacket(QueryBindings bindings) { + synchronized (this) { + BindValue[] bindValues = bindings.getBindValues(); + + NativePacketPayload sendPacket = this.session.getSharedSendPacket(); + + sendPacket.writeInteger(IntegerDataType.INT1, NativeConstants.COM_QUERY); + + boolean useStreamLengths = this.useStreamLengthsInPrepStmts.getValue(); + + // + // Try and get this allocation as close as possible for BLOBs + // + int ensurePacketSize = 0; + + String statementComment = this.session.getProtocol().getQueryComment(); + + byte[] commentAsBytes = null; + + if (statementComment != null) { + commentAsBytes = StringUtils.getBytes(statementComment, this.charEncoding); + + ensurePacketSize += commentAsBytes.length; + ensurePacketSize += 6; // for /*[space] [space]*/ + } + + for (int i = 0; i < bindValues.length; i++) { + if (bindValues[i].isStream() && useStreamLengths) { + ensurePacketSize += bindValues[i].getStreamLength(); + } + } + + if (ensurePacketSize != 0) { + sendPacket.ensureCapacity(ensurePacketSize); + } + + 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); + } + + byte[][] staticSqlStrings = this.parseInfo.getStaticSql(); + for (int i = 0; i < bindValues.length; i++) { + bindings.checkParameterSet(i); + + sendPacket.writeBytes(StringLengthDataType.STRING_FIXED, staticSqlStrings[i]); + + if (bindValues[i].isStream()) { + streamToBytes(sendPacket, bindValues[i].getStreamValue(), true, bindValues[i].getStreamLength(), useStreamLengths); + } else { + sendPacket.writeBytes(StringLengthDataType.STRING_FIXED, bindValues[i].getByteValue()); + } + } + + sendPacket.writeBytes(StringLengthDataType.STRING_FIXED, staticSqlStrings[bindValues.length]); + + return (M) sendPacket; + } + } + + private final void streamToBytes(NativePacketPayload packet, InputStream in, boolean escape, int streamLength, boolean useLength) { + try { + if (this.streamConvertBuf == null) { + this.streamConvertBuf = new byte[4096]; + } + + boolean hexEscape = this.session.getServerSession().isNoBackslashEscapesSet(); + + if (streamLength == -1) { + useLength = false; + } + + int bc = useLength ? readblock(in, this.streamConvertBuf, streamLength) : readblock(in, this.streamConvertBuf); + + int lengthLeftToRead = streamLength - bc; + + packet.writeBytes(StringLengthDataType.STRING_FIXED, StringUtils.getBytes(hexEscape ? "x" : "_binary")); + + if (escape) { + packet.writeInteger(IntegerDataType.INT1, (byte) '\''); + } + + while (bc > 0) { + if (hexEscape) { + ((AbstractQueryBindings) this.queryBindings).hexEscapeBlock(this.streamConvertBuf, packet, bc); + } else if (escape) { + escapeblockFast(this.streamConvertBuf, packet, bc); + } else { + packet.writeBytes(StringLengthDataType.STRING_FIXED, this.streamConvertBuf, 0, bc); + } + + if (useLength) { + bc = readblock(in, this.streamConvertBuf, lengthLeftToRead); + + if (bc > 0) { + lengthLeftToRead -= bc; + } + } else { + bc = readblock(in, this.streamConvertBuf); + } + } + + if (escape) { + packet.writeInteger(IntegerDataType.INT1, (byte) '\''); + } + } finally { + if (this.autoClosePStmtStreams.getValue()) { + try { + in.close(); + } catch (IOException ioEx) { + } + + in = null; + } + } + } + + protected final byte[] streamToBytes(InputStream in, boolean escape, int streamLength, boolean useLength) { + in.mark(Integer.MAX_VALUE); // we may need to read this same stream several times, so we need to reset it at the end. + try { + if (this.streamConvertBuf == null) { + this.streamConvertBuf = new byte[4096]; + } + if (streamLength == -1) { + useLength = false; + } + + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + + int bc = useLength ? readblock(in, this.streamConvertBuf, streamLength) : readblock(in, this.streamConvertBuf); + + int lengthLeftToRead = streamLength - bc; + + if (escape) { + bytesOut.write('_'); + bytesOut.write('b'); + bytesOut.write('i'); + bytesOut.write('n'); + bytesOut.write('a'); + bytesOut.write('r'); + bytesOut.write('y'); + bytesOut.write('\''); + } + + while (bc > 0) { + if (escape) { + StringUtils.escapeblockFast(this.streamConvertBuf, bytesOut, bc, this.usingAnsiMode); + } else { + bytesOut.write(this.streamConvertBuf, 0, bc); + } + + if (useLength) { + bc = readblock(in, this.streamConvertBuf, lengthLeftToRead); + + if (bc > 0) { + lengthLeftToRead -= bc; + } + } else { + bc = readblock(in, this.streamConvertBuf); + } + } + + if (escape) { + bytesOut.write('\''); + } + + return bytesOut.toByteArray(); + } finally { + try { + in.reset(); + } catch (IOException e) { + } + if (this.autoClosePStmtStreams.getValue()) { + try { + in.close(); + } catch (IOException ioEx) { + } + + in = null; + } + } + } + + private final int readblock(InputStream i, byte[] b) { + try { + return i.read(b); + } catch (Throwable ex) { + throw ExceptionFactory.createException(Messages.getString("PreparedStatement.56") + ex.getClass().getName(), + this.session.getExceptionInterceptor()); + } + } + + private final int readblock(InputStream i, byte[] b, int length) { + try { + int lengthToRead = length; + + if (lengthToRead > b.length) { + lengthToRead = b.length; + } + + return i.read(b, 0, lengthToRead); + } catch (Throwable ex) { + throw ExceptionFactory.createException(Messages.getString("PreparedStatement.56") + ex.getClass().getName(), + this.session.getExceptionInterceptor()); + } + } + + private final void escapeblockFast(byte[] buf, NativePacketPayload packet, int size) { + int lastwritten = 0; + + for (int i = 0; i < size; i++) { + byte b = buf[i]; + + if (b == '\0') { + // write stuff not yet written + if (i > lastwritten) { + packet.writeBytes(StringLengthDataType.STRING_FIXED, buf, lastwritten, i - lastwritten); + } + + // write escape + packet.writeInteger(IntegerDataType.INT1, (byte) '\\'); + packet.writeInteger(IntegerDataType.INT1, (byte) '0'); + lastwritten = i + 1; + } else { + if ((b == '\\') || (b == '\'') || (!this.usingAnsiMode && b == '"')) { + // write stuff not yet written + if (i > lastwritten) { + packet.writeBytes(StringLengthDataType.STRING_FIXED, buf, lastwritten, i - lastwritten); + } + + // write escape + packet.writeInteger(IntegerDataType.INT1, (byte) '\\'); + lastwritten = i; // not i+1 as b wasn't written. + } + } + } + + // write out remaining stuff from buffer + if (lastwritten < size) { + packet.writeBytes(StringLengthDataType.STRING_FIXED, buf, lastwritten, size - lastwritten); + } + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/AbstractQuery.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/AbstractQuery.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/AbstractQuery.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.ReadableProperty; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.CJTimeoutException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.OperationCancelledException; +import com.mysql.cj.log.ProfilerEventHandler; +import com.mysql.cj.protocol.Message; +import com.mysql.cj.protocol.ProtocolEntityFactory; +import com.mysql.cj.protocol.Resultset; +import com.mysql.cj.protocol.Resultset.Type; + +//TODO should not be protocol-specific +public abstract class AbstractQuery implements Query { + + /** Used to generate IDs when profiling. */ + static int statementCounter = 1; + + public NativeSession session = null; + + /** Used to identify this statement when profiling. */ + protected int statementId; + + /** Should we profile? */ + protected boolean profileSQL = false; + + protected ReadableProperty maxAllowedPacket; + + /** The character encoding to use (if available) */ + protected String charEncoding = null; + + /** Mutex to prevent race between returning query results and noticing that query has been timed-out or cancelled. */ + protected Object cancelTimeoutMutex = new Object(); + + private CancelStatus cancelStatus = CancelStatus.NOT_CANCELED; + + /** The timeout for a query */ + protected int timeoutInMillis = 0; + + /** Holds batched commands */ + protected List batchedArgs; + + protected boolean useCursorFetch = false; + + /** The type of this result set (scroll sensitive or in-sensitive) */ + protected Resultset.Type resultSetType = Type.FORWARD_ONLY; + + /** The number of rows to fetch at a time (currently ignored) */ + protected int fetchSize = 0; + + /** If profiling, where should events go to? */ + protected ProfilerEventHandler eventSink = null; + + /** Currently executing a statement? */ + protected final AtomicBoolean statementExecuting = new AtomicBoolean(false); + + /** The catalog in use */ + protected String currentCatalog = null; + + /** Has clearWarnings() been called? */ + protected boolean clearWarningsCalled = false; + + public AbstractQuery(NativeSession sess) { + statementCounter++; + this.session = sess; + this.profileSQL = sess.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_profileSQL).getValue(); + this.maxAllowedPacket = sess.getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_maxAllowedPacket); + this.charEncoding = sess.getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_characterEncoding).getValue(); + this.useCursorFetch = sess.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useCursorFetch).getValue(); + } + + @Override + public int getId() { + return this.statementId; + } + + @Override + public void setCancelStatus(CancelStatus cs) { + this.cancelStatus = cs; + } + + @Override + public void checkCancelTimeout() { + synchronized (this.cancelTimeoutMutex) { + if (this.cancelStatus != CancelStatus.NOT_CANCELED) { + CJException cause = this.cancelStatus == CancelStatus.CANCELED_BY_TIMEOUT ? new CJTimeoutException() : new OperationCancelledException(); + resetCancelledState(); + throw cause; + } + } + } + + public void resetCancelledState() { + synchronized (this.cancelTimeoutMutex) { + this.cancelStatus = CancelStatus.NOT_CANCELED; + } + } + + @Override + public ProtocolEntityFactory getResultSetFactory() { + // TODO Auto-generated method stub + return null; + } + + @Override + public NativeSession getSession() { + return this.session; + } + + @Override + public Object getCancelTimeoutMutex() { + return this.cancelTimeoutMutex; + } + + public void closeQuery() { + this.session = null; + } + + public void addBatch(Object batch) { + if (this.batchedArgs == null) { + this.batchedArgs = new ArrayList<>(); + } + this.batchedArgs.add(batch); + } + + public List getBatchedArgs() { + return this.batchedArgs == null ? null : Collections.unmodifiableList(this.batchedArgs); + } + + @Override + public void clearBatchedArgs() { + if (this.batchedArgs != null) { + this.batchedArgs.clear(); + } + } + + @Override + public int getResultFetchSize() { + return this.fetchSize; + } + + @Override + public void setResultFetchSize(int fetchSize) { + this.fetchSize = fetchSize; + } + + public Resultset.Type getResultType() { + return this.resultSetType; + } + + public void setResultType(Resultset.Type resultSetType) { + this.resultSetType = resultSetType; + } + + public int getTimeoutInMillis() { + return this.timeoutInMillis; + } + + public void setTimeoutInMillis(int timeoutInMillis) { + this.timeoutInMillis = timeoutInMillis; + } + + public CancelQueryTask startQueryTimer(Query stmtToCancel, int timeout) { + if (this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_enableQueryTimeouts).getValue() && timeout != 0) { + CancelQueryTaskImpl timeoutTask = new CancelQueryTaskImpl(stmtToCancel); + this.session.getCancelTimer().schedule(timeoutTask, timeout); + return timeoutTask; + } + return null; + } + + public void stopQueryTimer(CancelQueryTask timeoutTask, boolean rethrowCancelReason, boolean checkCancelTimeout) { + if (timeoutTask != null) { + timeoutTask.cancel(); + + if (rethrowCancelReason && timeoutTask.getCaughtWhileCancelling() != null) { + Throwable t = timeoutTask.getCaughtWhileCancelling(); + throw ExceptionFactory.createException(t.getMessage(), t); + } + + this.session.getCancelTimer().purge(); + + if (checkCancelTimeout) { + checkCancelTimeout(); + } + } + } + + public ProfilerEventHandler getEventSink() { + return this.eventSink; + } + + public void setEventSink(ProfilerEventHandler eventSink) { + this.eventSink = eventSink; + } + + public AtomicBoolean getStatementExecuting() { + return this.statementExecuting; + } + + public String getCurrentCatalog() { + return this.currentCatalog; + } + + public void setCurrentCatalog(String currentCatalog) { + this.currentCatalog = currentCatalog; + } + + public boolean isClearWarningsCalled() { + return this.clearWarningsCalled; + } + + public void setClearWarningsCalled(boolean clearWarningsCalled) { + this.clearWarningsCalled = clearWarningsCalled; + } + + public void statementBegins() { + this.clearWarningsCalled = false; + this.statementExecuting.set(true); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/AbstractQueryBindings.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/AbstractQueryBindings.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/AbstractQueryBindings.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.ObjectOutputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.text.ParsePosition; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Calendar; +import java.util.Locale; + +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.ReadableProperty; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.protocol.a.NativeConstants.IntegerDataType; +import com.mysql.cj.protocol.a.NativePacketPayload; +import com.mysql.cj.util.StringUtils; +import com.mysql.cj.util.TimeUtil; + +//TODO should not be protocol-specific +public abstract class AbstractQueryBindings implements QueryBindings { + + protected final static byte[] HEX_DIGITS = new byte[] { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', + (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F' }; + + protected Session session; + + /** Bind values for individual fields */ + protected T[] bindValues; + + protected String charEncoding; + + protected int numberOfExecutions = 0; + + protected ReadableProperty useStreamLengthsInPrepStmts; + protected ReadableProperty sendFractionalSeconds; + private ReadableProperty treatUtilDateAsTimestamp; + + /** Is this query a LOAD DATA query? */ + protected boolean isLoadDataQuery = false; + + public AbstractQueryBindings(int parameterCount, Session sess) { + this.session = sess; + this.charEncoding = this.session.getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_characterEncoding).getValue(); + this.sendFractionalSeconds = this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_sendFractionalSeconds); + this.treatUtilDateAsTimestamp = this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_treatUtilDateAsTimestamp); + this.useStreamLengthsInPrepStmts = this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useStreamLengthsInPrepStmts); + + initBindValues(parameterCount); + } + + protected abstract void initBindValues(int parameterCount); + + @Override + public abstract AbstractQueryBindings clone(); + + @Override + public boolean isLoadDataQuery() { + return this.isLoadDataQuery; + } + + @Override + public void setLoadDataQuery(boolean isLoadDataQuery) { + this.isLoadDataQuery = isLoadDataQuery; + } + + @Override + public T[] getBindValues() { + return this.bindValues; + } + + @Override + public void setBindValues(T[] bindValues) { + this.bindValues = bindValues; + } + + @Override + public boolean clearBindValues() { + boolean hadLongData = false; + + if (this.bindValues != null) { + for (int i = 0; i < this.bindValues.length; i++) { + if ((this.bindValues[i] != null) && ((ServerPreparedQueryBindValue) this.bindValues[i]).isLongData) { // TODO ServerPreparedQueryBindValue should not be referred here + hadLongData = true; + } + this.bindValues[i].reset(); + } + } + + return hadLongData; + } + + public abstract void checkParameterSet(int columnIndex); + + public void checkAllParametersSet() { + for (int i = 0; i < this.bindValues.length; i++) { + checkParameterSet(i); + } + } + + public int getNumberOfExecutions() { + return this.numberOfExecutions; + } + + public void setNumberOfExecutions(int numberOfExecutions) { + this.numberOfExecutions = numberOfExecutions; + } + + public synchronized final void setValue(int paramIndex, byte[] val) { + this.bindValues[paramIndex].setByteValue(val); + } + + public synchronized final void setValue(int paramIndex, byte[] val, MysqlType type) { + this.bindValues[paramIndex].setByteValue(val); + this.bindValues[paramIndex].setMysqlType(type); + } + + public synchronized final void setValue(int paramIndex, String val) { + byte[] parameterAsBytes = StringUtils.getBytes(val, this.charEncoding); + setValue(paramIndex, parameterAsBytes); + } + + public synchronized final void setValue(int paramIndex, String val, MysqlType type) { + byte[] parameterAsBytes = StringUtils.getBytes(val, this.charEncoding); + setValue(paramIndex, parameterAsBytes, type); + } + + /** + * Used to escape binary data with hex for mb charsets + * + * @param buf + * @param packet + * @param size + */ + public final void hexEscapeBlock(byte[] buf, NativePacketPayload packet, int size) { + for (int i = 0; i < size; i++) { + byte b = buf[i]; + int lowBits = (b & 0xff) / 16; + int highBits = (b & 0xff) % 16; + + packet.writeInteger(IntegerDataType.INT1, HEX_DIGITS[lowBits]); + packet.writeInteger(IntegerDataType.INT1, HEX_DIGITS[highBits]); + } + } + + @Override + public void setObject(int parameterIndex, Object parameterObj) { + if (parameterObj == null) { + setNull(parameterIndex); + } else { + if (parameterObj instanceof Byte) { + setInt(parameterIndex, ((Byte) parameterObj).intValue()); + + } else if (parameterObj instanceof String) { + setString(parameterIndex, (String) parameterObj); + + } else if (parameterObj instanceof BigDecimal) { + setBigDecimal(parameterIndex, (BigDecimal) parameterObj); + + } else if (parameterObj instanceof Short) { + setShort(parameterIndex, ((Short) parameterObj).shortValue()); + + } else if (parameterObj instanceof Integer) { + setInt(parameterIndex, ((Integer) parameterObj).intValue()); + + } else if (parameterObj instanceof Long) { + setLong(parameterIndex, ((Long) parameterObj).longValue()); + + } else if (parameterObj instanceof Float) { + setFloat(parameterIndex, ((Float) parameterObj).floatValue()); + + } else if (parameterObj instanceof Double) { + setDouble(parameterIndex, ((Double) parameterObj).doubleValue()); + + } else if (parameterObj instanceof byte[]) { + setBytes(parameterIndex, (byte[]) parameterObj); + + } else if (parameterObj instanceof java.sql.Date) { + setDate(parameterIndex, (java.sql.Date) parameterObj); + + } else if (parameterObj instanceof Time) { + setTime(parameterIndex, (Time) parameterObj); + + } else if (parameterObj instanceof Timestamp) { + setTimestamp(parameterIndex, (Timestamp) parameterObj); + + } else if (parameterObj instanceof Boolean) { + setBoolean(parameterIndex, ((Boolean) parameterObj).booleanValue()); + + } else if (parameterObj instanceof InputStream) { + setBinaryStream(parameterIndex, (InputStream) parameterObj, -1); + + } else if (parameterObj instanceof java.sql.Blob) { + setBlob(parameterIndex, (java.sql.Blob) parameterObj); + + } else if (parameterObj instanceof java.sql.Clob) { + setClob(parameterIndex, (java.sql.Clob) parameterObj); + + } else if (this.treatUtilDateAsTimestamp.getValue() && parameterObj instanceof java.util.Date) { + setTimestamp(parameterIndex, new Timestamp(((java.util.Date) parameterObj).getTime())); + + } else if (parameterObj instanceof BigInteger) { + setString(parameterIndex, parameterObj.toString()); + + } else if (parameterObj instanceof LocalDate) { + setDate(parameterIndex, Date.valueOf((LocalDate) parameterObj)); + + } else if (parameterObj instanceof LocalDateTime) { + setTimestamp(parameterIndex, Timestamp.valueOf((LocalDateTime) parameterObj)); + + } else if (parameterObj instanceof LocalTime) { + setTime(parameterIndex, Time.valueOf((LocalTime) parameterObj)); + + } else { + setSerializableObject(parameterIndex, parameterObj); + } + } + } + + @Override + public void setObject(int parameterIndex, Object parameterObj, MysqlType targetMysqlType) { + setObject(parameterIndex, parameterObj, targetMysqlType, parameterObj instanceof BigDecimal ? ((BigDecimal) parameterObj).scale() : 0); + } + + /** + * Set the value of a parameter using an object; use the java.lang equivalent objects for integral values. + * + *

+ * The given Java object will be converted to the targetMysqlType before being sent to the database. + * + * @param parameterIndex + * the first parameter is 1... + * @param parameterObj + * the object containing the input parameter value + * @param targetMysqlType + * The MysqlType to be send to the database + * @param scaleOrLength + * For Types.DECIMAL or Types.NUMERIC types + * this is the number of digits after the decimal. For all other + * types this value will be ignored. + */ + public void setObject(int parameterIndex, Object parameterObj, MysqlType targetMysqlType, int scaleOrLength) { + if (parameterObj == null) { + setNull(parameterIndex); + } else { + if (parameterObj instanceof LocalDate) { + parameterObj = Date.valueOf((LocalDate) parameterObj); + } else if (parameterObj instanceof LocalDateTime) { + parameterObj = Timestamp.valueOf((LocalDateTime) parameterObj); + } else if (parameterObj instanceof LocalTime) { + parameterObj = Time.valueOf((LocalTime) parameterObj); + } + + try { + /* + * From Table-B5 in the JDBC Spec + */ + switch (targetMysqlType) { + case BOOLEAN: + if (parameterObj instanceof Boolean) { + setBoolean(parameterIndex, ((Boolean) parameterObj).booleanValue()); + break; + + } else if (parameterObj instanceof String) { + setBoolean(parameterIndex, "true".equalsIgnoreCase((String) parameterObj) || !"0".equalsIgnoreCase((String) parameterObj)); + break; + + } else if (parameterObj instanceof Number) { + int intValue = ((Number) parameterObj).intValue(); + setBoolean(parameterIndex, intValue != 0); + break; + + } else { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("PreparedStatement.66", new Object[] { parameterObj.getClass().getName() }), + this.session.getExceptionInterceptor()); + } + + case BIT: + case TINYINT: + case TINYINT_UNSIGNED: + case SMALLINT: + case SMALLINT_UNSIGNED: + case INT: + case INT_UNSIGNED: + case MEDIUMINT: + case MEDIUMINT_UNSIGNED: + case BIGINT: + case BIGINT_UNSIGNED: + case FLOAT: + case FLOAT_UNSIGNED: + case DOUBLE: + case DOUBLE_UNSIGNED: + case DECIMAL: + case DECIMAL_UNSIGNED: + setNumericObject(parameterIndex, parameterObj, targetMysqlType, scaleOrLength); + break; + + case CHAR: + case ENUM: + case SET: + case VARCHAR: + case TINYTEXT: + case TEXT: + case MEDIUMTEXT: + case LONGTEXT: + case JSON: + if (parameterObj instanceof BigDecimal) { + setString(parameterIndex, (StringUtils.fixDecimalExponent(((BigDecimal) parameterObj).toPlainString()))); + } else if (parameterObj instanceof java.sql.Clob) { + setClob(parameterIndex, (java.sql.Clob) parameterObj); + } else { + setString(parameterIndex, parameterObj.toString()); + } + break; + + case BINARY: + case GEOMETRY: + case VARBINARY: + case TINYBLOB: + case BLOB: + case MEDIUMBLOB: + case LONGBLOB: + if (parameterObj instanceof byte[]) { + setBytes(parameterIndex, (byte[]) parameterObj); + } else if (parameterObj instanceof java.sql.Blob) { + setBlob(parameterIndex, (java.sql.Blob) parameterObj); + } else { + setBytes(parameterIndex, StringUtils.getBytes(parameterObj.toString(), this.charEncoding)); + } + + break; + + case DATE: + case DATETIME: + case TIMESTAMP: + case YEAR: + + java.util.Date parameterAsDate; + + if (parameterObj instanceof String) { + ParsePosition pp = new ParsePosition(0); + java.text.DateFormat sdf = new java.text.SimpleDateFormat(TimeUtil.getDateTimePattern((String) parameterObj, false), Locale.US); + parameterAsDate = sdf.parse((String) parameterObj, pp); + } else { + parameterAsDate = (java.util.Date) parameterObj; + } + + switch (targetMysqlType) { + case DATE: + + if (parameterAsDate instanceof java.sql.Date) { + setDate(parameterIndex, (java.sql.Date) parameterAsDate); + } else { + setDate(parameterIndex, new java.sql.Date(parameterAsDate.getTime())); + } + + break; + + case DATETIME: + case TIMESTAMP: + + if (parameterAsDate instanceof java.sql.Timestamp) { + setTimestamp(parameterIndex, (java.sql.Timestamp) parameterAsDate); + } else { + setTimestamp(parameterIndex, new java.sql.Timestamp(parameterAsDate.getTime())); + } + + break; + + case YEAR: + Calendar cal = Calendar.getInstance(); + cal.setTime(parameterAsDate); + setNumericObject(parameterIndex, cal.get(Calendar.YEAR), targetMysqlType, scaleOrLength); + break; + + default: + break; + } + + break; + + case TIME: + if (parameterObj instanceof String) { + java.text.DateFormat sdf = new java.text.SimpleDateFormat(TimeUtil.getDateTimePattern((String) parameterObj, true), Locale.US); + setTime(parameterIndex, new java.sql.Time(sdf.parse((String) parameterObj).getTime())); + } else if (parameterObj instanceof Timestamp) { + Timestamp xT = (Timestamp) parameterObj; + setTime(parameterIndex, new java.sql.Time(xT.getTime())); + } else { + setTime(parameterIndex, (java.sql.Time) parameterObj); + } + + break; + + case UNKNOWN: + setSerializableObject(parameterIndex, parameterObj); + break; + + default: + throw ExceptionFactory.createException(Messages.getString("PreparedStatement.16"), this.session.getExceptionInterceptor()); + } + } catch (Exception ex) { + throw ExceptionFactory.createException( + Messages.getString("PreparedStatement.17") + parameterObj.getClass().toString() + Messages.getString("PreparedStatement.18") + + ex.getClass().getName() + Messages.getString("PreparedStatement.19") + ex.getMessage(), + ex, this.session.getExceptionInterceptor()); + } + } + } + + private void setNumericObject(int parameterIndex, Object parameterObj, MysqlType targetMysqlType, int scale) { + Number parameterAsNum; + + if (parameterObj instanceof Boolean) { + parameterAsNum = ((Boolean) parameterObj).booleanValue() ? Integer.valueOf(1) : Integer.valueOf(0); + } else if (parameterObj instanceof String) { + switch (targetMysqlType) { + case BIT: + if ("1".equals(parameterObj) || "0".equals(parameterObj)) { + parameterAsNum = Integer.valueOf((String) parameterObj); + } else { + boolean parameterAsBoolean = "true".equalsIgnoreCase((String) parameterObj); + + parameterAsNum = parameterAsBoolean ? Integer.valueOf(1) : Integer.valueOf(0); + } + break; + + case TINYINT: + case TINYINT_UNSIGNED: + case SMALLINT: + case SMALLINT_UNSIGNED: + case MEDIUMINT: + case MEDIUMINT_UNSIGNED: + case INT: + case INT_UNSIGNED: + case YEAR: + parameterAsNum = Integer.valueOf((String) parameterObj); + break; + + case BIGINT: + parameterAsNum = Long.valueOf((String) parameterObj); + break; + + case BIGINT_UNSIGNED: + parameterAsNum = new BigInteger((String) parameterObj); + break; + + case FLOAT: + case FLOAT_UNSIGNED: + parameterAsNum = Float.valueOf((String) parameterObj); + + break; + + case DOUBLE: + case DOUBLE_UNSIGNED: + parameterAsNum = Double.valueOf((String) parameterObj); + break; + + case DECIMAL: + case DECIMAL_UNSIGNED: + default: + parameterAsNum = new java.math.BigDecimal((String) parameterObj); + } + } else { + parameterAsNum = (Number) parameterObj; + } + + switch (targetMysqlType) { + case BIT: + case TINYINT: + case TINYINT_UNSIGNED: + case SMALLINT: + case SMALLINT_UNSIGNED: + case MEDIUMINT: + case MEDIUMINT_UNSIGNED: + case INT: + case INT_UNSIGNED: + case YEAR: + setInt(parameterIndex, parameterAsNum.intValue()); + break; + + case BIGINT: + case BIGINT_UNSIGNED: + setLong(parameterIndex, parameterAsNum.longValue()); + break; + + case FLOAT: + case FLOAT_UNSIGNED: + setFloat(parameterIndex, parameterAsNum.floatValue()); + break; + + case DOUBLE: + case DOUBLE_UNSIGNED: + setDouble(parameterIndex, parameterAsNum.doubleValue()); + break; + + case DECIMAL: + case DECIMAL_UNSIGNED: + if (parameterAsNum instanceof java.math.BigDecimal) { + BigDecimal scaledBigDecimal = null; + + try { + scaledBigDecimal = ((java.math.BigDecimal) parameterAsNum).setScale(scale); + } catch (ArithmeticException ex) { + try { + scaledBigDecimal = ((java.math.BigDecimal) parameterAsNum).setScale(scale, BigDecimal.ROUND_HALF_UP); + } catch (ArithmeticException arEx) { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("PreparedStatement.65", new Object[] { scale, parameterAsNum }), this.session.getExceptionInterceptor()); + } + } + + setBigDecimal(parameterIndex, scaledBigDecimal); + } else if (parameterAsNum instanceof java.math.BigInteger) { + setBigDecimal(parameterIndex, new java.math.BigDecimal((java.math.BigInteger) parameterAsNum, scale)); + } else { + setBigDecimal(parameterIndex, new java.math.BigDecimal(parameterAsNum.doubleValue())); + } + + break; + default: + break; + } + } + + /** + * Sets the value for the placeholder as a serialized Java object (used by various forms of setObject() + * + * @param parameterIndex + * @param parameterObj + */ + protected final void setSerializableObject(int parameterIndex, Object parameterObj) { + try { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + ObjectOutputStream objectOut = new ObjectOutputStream(bytesOut); + objectOut.writeObject(parameterObj); + objectOut.flush(); + objectOut.close(); + bytesOut.flush(); + bytesOut.close(); + + byte[] buf = bytesOut.toByteArray(); + ByteArrayInputStream bytesIn = new ByteArrayInputStream(buf); + setBinaryStream(parameterIndex, bytesIn, buf.length); + this.bindValues[parameterIndex].setMysqlType(MysqlType.BINARY); + } catch (Exception ex) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedStatement.54") + ex.getClass().getName(), ex, + this.session.getExceptionInterceptor()); + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/AppendingBatchVisitor.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/AppendingBatchVisitor.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/AppendingBatchVisitor.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.util.Iterator; +import java.util.LinkedList; + +import com.mysql.cj.util.StringUtils; + +public class AppendingBatchVisitor implements BatchVisitor { + LinkedList statementComponents = new LinkedList<>(); + + public BatchVisitor append(byte[] values) { + this.statementComponents.addLast(values); + + return this; + } + + public BatchVisitor increment() { + // no-op + return this; + } + + public BatchVisitor decrement() { + this.statementComponents.removeLast(); + + return this; + } + + public BatchVisitor merge(byte[] front, byte[] back) { + int mergedLength = front.length + back.length; + byte[] merged = new byte[mergedLength]; + System.arraycopy(front, 0, merged, 0, front.length); + System.arraycopy(back, 0, merged, front.length, back.length); + this.statementComponents.addLast(merged); + return this; + } + + public byte[][] getStaticSqlStrings() { + byte[][] asBytes = new byte[this.statementComponents.size()][]; + this.statementComponents.toArray(asBytes); + + return asBytes; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + Iterator iter = this.statementComponents.iterator(); + while (iter.hasNext()) { + buf.append(StringUtils.toString(iter.next())); + } + + return buf.toString(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/BatchVisitor.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/BatchVisitor.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/BatchVisitor.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +public interface BatchVisitor { + + abstract BatchVisitor increment(); + + abstract BatchVisitor decrement(); + + abstract BatchVisitor append(byte[] values); + + abstract BatchVisitor merge(byte[] begin, byte[] end); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/BindValue.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/BindValue.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/BindValue.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.io.InputStream; + +public interface BindValue { + + BindValue clone(); + + void reset(); + + boolean isNull(); + + void setNull(boolean isNull); + + boolean isStream(); + + void setIsStream(boolean isStream); + + MysqlType getMysqlType(); + + void setMysqlType(MysqlType type); + + byte[] getByteValue(); + + void setByteValue(byte[] parameterValue); + + InputStream getStreamValue(); + + void setStreamValue(InputStream parameterStream, int streamLength); + + int getStreamLength(); + + boolean isSet(); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/CacheAdapter.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/CacheAdapter.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/CacheAdapter.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.util.Set; + +public interface CacheAdapter { + V get(K key); + + void put(K key, V value); + + void invalidate(K key); + + void invalidateAll(Set keys); + + void invalidateAll(); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/CacheAdapterFactory.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/CacheAdapterFactory.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/CacheAdapterFactory.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +public interface CacheAdapterFactory { + + CacheAdapter getInstance(Object syncMutex, String url, int cacheMaxSize, int maxKeySize); + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/CancelQueryTask.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/CancelQueryTask.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/CancelQueryTask.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +public interface CancelQueryTask { + + boolean cancel(); + + Throwable getCaughtWhileCancelling(); + + void setCaughtWhileCancelling(Throwable caughtWhileCancelling); + + Query getQueryToCancel(); + + void setQueryToCancel(Query queryToCancel); + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/CancelQueryTaskImpl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/CancelQueryTaskImpl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/CancelQueryTaskImpl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.util.TimerTask; + +import com.mysql.cj.Query.CancelStatus; +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.OperationCancelledException; +import com.mysql.cj.protocol.a.NativeMessageBuilder; +import com.mysql.cj.util.StringUtils; + +//TODO should not be protocol-specific + +/** + * Thread used to implement query timeouts...Eventually we could be more + * efficient and have one thread with timers, but this is a straightforward + * and simple way to implement a feature that isn't used all that often. + */ +public class CancelQueryTaskImpl extends TimerTask implements CancelQueryTask { + + Query queryToCancel; + Throwable caughtWhileCancelling = null; + boolean queryTimeoutKillsConnection = false; + + public CancelQueryTaskImpl(Query cancellee) { + this.queryToCancel = cancellee; + NativeSession session = (NativeSession) cancellee.getSession(); + this.queryTimeoutKillsConnection = session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_queryTimeoutKillsConnection) + .getValue(); + } + + @Override + public boolean cancel() { + boolean res = super.cancel(); + this.queryToCancel = null; + return res; + } + + @Override + public void run() { + + Thread cancelThread = new Thread() { + + @Override + public void run() { + Query localQueryToCancel = CancelQueryTaskImpl.this.queryToCancel; + if (localQueryToCancel == null) { + return; + } + NativeSession session = (NativeSession) localQueryToCancel.getSession(); + if (session == null) { + return; + } + + try { + if (CancelQueryTaskImpl.this.queryTimeoutKillsConnection) { + localQueryToCancel.setCancelStatus(CancelStatus.CANCELED_BY_TIMEOUT); + session.invokeCleanupListeners(new OperationCancelledException(Messages.getString("Statement.ConnectionKilledDueToTimeout"))); + } else { + synchronized (localQueryToCancel.getCancelTimeoutMutex()) { + long origConnId = session.getThreadId(); + HostInfo hostInfo = session.getHostInfo(); + String database = hostInfo.getDatabase(); + String user = StringUtils.isNullOrEmpty(hostInfo.getUser()) ? "" : hostInfo.getUser(); + String password = StringUtils.isNullOrEmpty(hostInfo.getPassword()) ? "" : hostInfo.getPassword(); + + NativeSession newSession = new NativeSession(hostInfo, session.getPropertySet()); + newSession.connect(hostInfo, user, password, database, 30000, new TransactionEventHandler() { + @Override + public void transactionCompleted() { + } + + public void transactionBegun() { + } + }); + newSession.sendCommand(new NativeMessageBuilder().buildComQuery(newSession.getSharedSendPacket(), "KILL QUERY " + origConnId), false, 0); + + localQueryToCancel.setCancelStatus(CancelStatus.CANCELED_BY_TIMEOUT); + } + } + // } catch (NullPointerException npe) { + // Case when connection closed while starting to cancel. + // We can't easily synchronise this, because then one thread can't cancel() a running query. + // Ignore, we shouldn't re-throw this, because the connection's already closed, so the statement has been timed out. + } catch (Throwable t) { + CancelQueryTaskImpl.this.caughtWhileCancelling = t; + } finally { + setQueryToCancel(null); + } + } + }; + + cancelThread.start(); + } + + public Throwable getCaughtWhileCancelling() { + return this.caughtWhileCancelling; + } + + public void setCaughtWhileCancelling(Throwable caughtWhileCancelling) { + this.caughtWhileCancelling = caughtWhileCancelling; + } + + public Query getQueryToCancel() { + return this.queryToCancel; + } + + public void setQueryToCancel(Query queryToCancel) { + this.queryToCancel = queryToCancel; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/CharsetMapping.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/CharsetMapping.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/CharsetMapping.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,799 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/** + * Mapping between MySQL charset names and Java charset names. I've investigated placing these in a .properties file, but unfortunately under most appservers + * this complicates configuration because the security policy needs to be changed by the user to allow the driver to read them :( + */ +public class CharsetMapping { + + public static final int MAP_SIZE = 2048; // Size of static maps + public static final String[] COLLATION_INDEX_TO_COLLATION_NAME; + public static final MysqlCharset[] COLLATION_INDEX_TO_CHARSET; + + public static final Map CHARSET_NAME_TO_CHARSET; + public static final Map CHARSET_NAME_TO_COLLATION_INDEX; + + private static final Map> JAVA_ENCODING_UC_TO_MYSQL_CHARSET; + + private static final Set MULTIBYTE_ENCODINGS; + + public static final Set UTF8MB4_INDEXES; + + private static final String MYSQL_CHARSET_NAME_armscii8 = "armscii8"; + private static final String MYSQL_CHARSET_NAME_ascii = "ascii"; + private static final String MYSQL_CHARSET_NAME_big5 = "big5"; + private static final String MYSQL_CHARSET_NAME_binary = "binary"; + private static final String MYSQL_CHARSET_NAME_cp1250 = "cp1250"; + private static final String MYSQL_CHARSET_NAME_cp1251 = "cp1251"; + private static final String MYSQL_CHARSET_NAME_cp1256 = "cp1256"; + private static final String MYSQL_CHARSET_NAME_cp1257 = "cp1257"; + private static final String MYSQL_CHARSET_NAME_cp850 = "cp850"; + private static final String MYSQL_CHARSET_NAME_cp852 = "cp852"; + private static final String MYSQL_CHARSET_NAME_cp866 = "cp866"; + private static final String MYSQL_CHARSET_NAME_cp932 = "cp932"; + private static final String MYSQL_CHARSET_NAME_dec8 = "dec8"; + private static final String MYSQL_CHARSET_NAME_eucjpms = "eucjpms"; + private static final String MYSQL_CHARSET_NAME_euckr = "euckr"; + private static final String MYSQL_CHARSET_NAME_gb18030 = "gb18030"; + private static final String MYSQL_CHARSET_NAME_gb2312 = "gb2312"; + private static final String MYSQL_CHARSET_NAME_gbk = "gbk"; + private static final String MYSQL_CHARSET_NAME_geostd8 = "geostd8"; + private static final String MYSQL_CHARSET_NAME_greek = "greek"; + private static final String MYSQL_CHARSET_NAME_hebrew = "hebrew"; + private static final String MYSQL_CHARSET_NAME_hp8 = "hp8"; + private static final String MYSQL_CHARSET_NAME_keybcs2 = "keybcs2"; + private static final String MYSQL_CHARSET_NAME_koi8r = "koi8r"; + private static final String MYSQL_CHARSET_NAME_koi8u = "koi8u"; + private static final String MYSQL_CHARSET_NAME_latin1 = "latin1"; + private static final String MYSQL_CHARSET_NAME_latin2 = "latin2"; + private static final String MYSQL_CHARSET_NAME_latin5 = "latin5"; + private static final String MYSQL_CHARSET_NAME_latin7 = "latin7"; + private static final String MYSQL_CHARSET_NAME_macce = "macce"; + private static final String MYSQL_CHARSET_NAME_macroman = "macroman"; + private static final String MYSQL_CHARSET_NAME_sjis = "sjis"; + private static final String MYSQL_CHARSET_NAME_swe7 = "swe7"; + private static final String MYSQL_CHARSET_NAME_tis620 = "tis620"; + private static final String MYSQL_CHARSET_NAME_ucs2 = "ucs2"; + private static final String MYSQL_CHARSET_NAME_ujis = "ujis"; + private static final String MYSQL_CHARSET_NAME_utf16 = "utf16"; + private static final String MYSQL_CHARSET_NAME_utf16le = "utf16le"; + private static final String MYSQL_CHARSET_NAME_utf32 = "utf32"; + private static final String MYSQL_CHARSET_NAME_utf8 = "utf8"; + private static final String MYSQL_CHARSET_NAME_utf8mb4 = "utf8mb4"; + + public static final String NOT_USED = MYSQL_CHARSET_NAME_latin1; // punting for not-used character sets + public static final String COLLATION_NOT_DEFINED = "none"; + + public static final int MYSQL_COLLATION_INDEX_utf8 = 33; + public static final int MYSQL_COLLATION_INDEX_binary = 63; + + private static int numberOfEncodingsConfigured = 0; + + static { + // complete list of mysql character sets and their corresponding java encoding names + MysqlCharset[] charset = new MysqlCharset[] { new MysqlCharset(MYSQL_CHARSET_NAME_ascii, 1, 0, new String[] { "US-ASCII", "ASCII" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_big5, 2, 0, new String[] { "Big5" }), + new MysqlCharset(MYSQL_CHARSET_NAME_gbk, 2, 0, new String[] { "GBK" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_sjis, 2, 0, new String[] { "SHIFT_JIS", "Cp943", "WINDOWS-31J" }), // SJIS is alias for SHIFT_JIS, Cp943 is rather a cp932 but we map it to sjis for years + new MysqlCharset(MYSQL_CHARSET_NAME_cp932, 2, 1, new String[] { "WINDOWS-31J" }), // MS932 is alias for WINDOWS-31J + + new MysqlCharset(MYSQL_CHARSET_NAME_gb2312, 2, 0, new String[] { "GB2312" }), + new MysqlCharset(MYSQL_CHARSET_NAME_ujis, 3, 0, new String[] { "EUC_JP" }), + new MysqlCharset(MYSQL_CHARSET_NAME_eucjpms, 3, 0, new String[] { "EUC_JP_Solaris" }, new ServerVersion(5, 0, 3)), // "EUC_JP_Solaris = >5.0.3 eucjpms," + + new MysqlCharset(MYSQL_CHARSET_NAME_gb18030, 4, 0, new String[] { "GB18030" }, new ServerVersion(5, 7, 4)), + + new MysqlCharset(MYSQL_CHARSET_NAME_euckr, 2, 0, new String[] { "EUC-KR" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_latin1, 1, 1, new String[] { "Cp1252", "ISO8859_1" }), + new MysqlCharset(MYSQL_CHARSET_NAME_swe7, 1, 0, new String[] { "Cp1252" }), // new mapping, Cp1252 ? + new MysqlCharset(MYSQL_CHARSET_NAME_hp8, 1, 0, new String[] { "Cp1252" }), // new mapping, Cp1252 ? + new MysqlCharset(MYSQL_CHARSET_NAME_dec8, 1, 0, new String[] { "Cp1252" }), // new mapping, Cp1252 ? + new MysqlCharset(MYSQL_CHARSET_NAME_armscii8, 1, 0, new String[] { "Cp1252" }), // new mapping, Cp1252 ? + new MysqlCharset(MYSQL_CHARSET_NAME_geostd8, 1, 0, new String[] { "Cp1252" }), // new mapping, Cp1252 ? + + new MysqlCharset(MYSQL_CHARSET_NAME_latin2, 1, 0, new String[] { "ISO8859_2" }), // latin2 is an alias + + new MysqlCharset(MYSQL_CHARSET_NAME_greek, 1, 0, new String[] { "ISO8859_7", "greek" }), + new MysqlCharset(MYSQL_CHARSET_NAME_latin7, 1, 0, new String[] { "ISO-8859-13" }), // was ISO8859_7, that's incorrect; also + "LATIN7 = latin7," is wrong java encoding name + + new MysqlCharset(MYSQL_CHARSET_NAME_hebrew, 1, 0, new String[] { "ISO8859_8" }), // hebrew is an alias + new MysqlCharset(MYSQL_CHARSET_NAME_latin5, 1, 0, new String[] { "ISO8859_9" }), // LATIN5 is an alias + + new MysqlCharset(MYSQL_CHARSET_NAME_cp850, 1, 0, new String[] { "Cp850", "Cp437" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_cp852, 1, 0, new String[] { "Cp852" }), + new MysqlCharset(MYSQL_CHARSET_NAME_keybcs2, 1, 0, new String[] { "Cp852" }), // new, Kamenicky encoding usually known as Cp895 but there is no official cp895 specification; close to Cp852, see http://ftp.muni.cz/pub/localization/charsets/cs-encodings-faq + + new MysqlCharset(MYSQL_CHARSET_NAME_cp866, 1, 0, new String[] { "Cp866" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_koi8r, 1, 1, new String[] { "KOI8_R" }), + new MysqlCharset(MYSQL_CHARSET_NAME_koi8u, 1, 0, new String[] { "KOI8_R" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_tis620, 1, 0, new String[] { "TIS620" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_cp1250, 1, 0, new String[] { "Cp1250" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_cp1251, 1, 1, new String[] { "Cp1251" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_cp1256, 1, 0, new String[] { "Cp1256" }), + new MysqlCharset(MYSQL_CHARSET_NAME_cp1257, 1, 0, new String[] { "Cp1257" }), + new MysqlCharset(MYSQL_CHARSET_NAME_macroman, 1, 0, new String[] { "MacRoman" }), + new MysqlCharset(MYSQL_CHARSET_NAME_macce, 1, 0, new String[] { "MacCentralEurope" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_utf8, 3, 1, new String[] { "UTF-8" }), + new MysqlCharset(MYSQL_CHARSET_NAME_utf8mb4, 4, 0, new String[] { "UTF-8" }), // "UTF-8 = *> 5.5.2 utf8mb4," + + new MysqlCharset(MYSQL_CHARSET_NAME_ucs2, 2, 0, new String[] { "UnicodeBig" }), + + new MysqlCharset(MYSQL_CHARSET_NAME_binary, 1, 1, new String[] { "ISO8859_1" }), // US-ASCII ? + + new MysqlCharset(MYSQL_CHARSET_NAME_utf16, 4, 0, new String[] { "UTF-16" }), + new MysqlCharset(MYSQL_CHARSET_NAME_utf16le, 4, 0, new String[] { "UTF-16LE" }), + new MysqlCharset(MYSQL_CHARSET_NAME_utf32, 4, 0, new String[] { "UTF-32" }) + + }; + HashMap charsetNameToMysqlCharsetMap = new HashMap<>(); + HashMap> javaUcToMysqlCharsetMap = new HashMap<>(); + Set tempMultibyteEncodings = new HashSet<>(); // Character sets that we can't convert ourselves. + for (int i = 0; i < charset.length; i++) { + String charsetName = charset[i].charsetName; + + charsetNameToMysqlCharsetMap.put(charsetName, charset[i]); + + numberOfEncodingsConfigured += charset[i].javaEncodingsUc.size(); + + for (String encUC : charset[i].javaEncodingsUc) { + + // fill javaUcToMysqlCharsetMap + List charsets = javaUcToMysqlCharsetMap.get(encUC); + if (charsets == null) { + charsets = new ArrayList<>(); + javaUcToMysqlCharsetMap.put(encUC, charsets); + } + charsets.add(charset[i]); + + // fill multi-byte charsets + if (charset[i].mblen > 1) { + tempMultibyteEncodings.add(encUC); + } + + } + + } + CHARSET_NAME_TO_CHARSET = Collections.unmodifiableMap(charsetNameToMysqlCharsetMap); + JAVA_ENCODING_UC_TO_MYSQL_CHARSET = Collections.unmodifiableMap(javaUcToMysqlCharsetMap); + MULTIBYTE_ENCODINGS = Collections.unmodifiableSet(tempMultibyteEncodings); + + // complete list of mysql collations and their corresponding character sets each element of collation[1]..collation[MAP_SIZE-1] must not be null + Collation[] collation = new Collation[MAP_SIZE]; + collation[1] = new Collation(1, "big5_chinese_ci", 1, MYSQL_CHARSET_NAME_big5); + collation[2] = new Collation(2, "latin2_czech_cs", 0, MYSQL_CHARSET_NAME_latin2); + collation[3] = new Collation(3, "dec8_swedish_ci", 0, MYSQL_CHARSET_NAME_dec8); + collation[4] = new Collation(4, "cp850_general_ci", 1, MYSQL_CHARSET_NAME_cp850); + collation[5] = new Collation(5, "latin1_german1_ci", 1, MYSQL_CHARSET_NAME_latin1); + collation[6] = new Collation(6, "hp8_english_ci", 0, MYSQL_CHARSET_NAME_hp8); + collation[7] = new Collation(7, "koi8r_general_ci", 0, MYSQL_CHARSET_NAME_koi8r); + collation[8] = new Collation(8, "latin1_swedish_ci", 0, MYSQL_CHARSET_NAME_latin1); + collation[9] = new Collation(9, "latin2_general_ci", 1, MYSQL_CHARSET_NAME_latin2); + collation[10] = new Collation(10, "swe7_swedish_ci", 0, MYSQL_CHARSET_NAME_swe7); + collation[11] = new Collation(11, "ascii_general_ci", 0, MYSQL_CHARSET_NAME_ascii); + collation[12] = new Collation(12, "ujis_japanese_ci", 0, MYSQL_CHARSET_NAME_ujis); + collation[13] = new Collation(13, "sjis_japanese_ci", 0, MYSQL_CHARSET_NAME_sjis); + collation[14] = new Collation(14, "cp1251_bulgarian_ci", 0, MYSQL_CHARSET_NAME_cp1251); + collation[15] = new Collation(15, "latin1_danish_ci", 0, MYSQL_CHARSET_NAME_latin1); + collation[16] = new Collation(16, "hebrew_general_ci", 0, MYSQL_CHARSET_NAME_hebrew); + + collation[18] = new Collation(18, "tis620_thai_ci", 0, MYSQL_CHARSET_NAME_tis620); + collation[19] = new Collation(19, "euckr_korean_ci", 0, MYSQL_CHARSET_NAME_euckr); + collation[20] = new Collation(20, "latin7_estonian_cs", 0, MYSQL_CHARSET_NAME_latin7); + collation[21] = new Collation(21, "latin2_hungarian_ci", 0, MYSQL_CHARSET_NAME_latin2); + collation[22] = new Collation(22, "koi8u_general_ci", 0, MYSQL_CHARSET_NAME_koi8u); + collation[23] = new Collation(23, "cp1251_ukrainian_ci", 0, MYSQL_CHARSET_NAME_cp1251); + collation[24] = new Collation(24, "gb2312_chinese_ci", 0, MYSQL_CHARSET_NAME_gb2312); + collation[25] = new Collation(25, "greek_general_ci", 0, MYSQL_CHARSET_NAME_greek); + collation[26] = new Collation(26, "cp1250_general_ci", 1, MYSQL_CHARSET_NAME_cp1250); + collation[27] = new Collation(27, "latin2_croatian_ci", 0, MYSQL_CHARSET_NAME_latin2); + collation[28] = new Collation(28, "gbk_chinese_ci", 1, MYSQL_CHARSET_NAME_gbk); + collation[29] = new Collation(29, "cp1257_lithuanian_ci", 0, MYSQL_CHARSET_NAME_cp1257); + collation[30] = new Collation(30, "latin5_turkish_ci", 1, MYSQL_CHARSET_NAME_latin5); + collation[31] = new Collation(31, "latin1_german2_ci", 0, MYSQL_CHARSET_NAME_latin1); + collation[32] = new Collation(32, "armscii8_general_ci", 0, MYSQL_CHARSET_NAME_armscii8); + collation[33] = new Collation(33, "utf8_general_ci", 1, MYSQL_CHARSET_NAME_utf8); + collation[34] = new Collation(34, "cp1250_czech_cs", 0, MYSQL_CHARSET_NAME_cp1250); + collation[35] = new Collation(35, "ucs2_general_ci", 1, MYSQL_CHARSET_NAME_ucs2); + collation[36] = new Collation(36, "cp866_general_ci", 1, MYSQL_CHARSET_NAME_cp866); + collation[37] = new Collation(37, "keybcs2_general_ci", 1, MYSQL_CHARSET_NAME_keybcs2); + collation[38] = new Collation(38, "macce_general_ci", 1, MYSQL_CHARSET_NAME_macce); + collation[39] = new Collation(39, "macroman_general_ci", 1, MYSQL_CHARSET_NAME_macroman); + collation[40] = new Collation(40, "cp852_general_ci", 1, MYSQL_CHARSET_NAME_cp852); + collation[41] = new Collation(41, "latin7_general_ci", 1, MYSQL_CHARSET_NAME_latin7); + collation[42] = new Collation(42, "latin7_general_cs", 0, MYSQL_CHARSET_NAME_latin7); + collation[43] = new Collation(43, "macce_bin", 0, MYSQL_CHARSET_NAME_macce); + collation[44] = new Collation(44, "cp1250_croatian_ci", 0, MYSQL_CHARSET_NAME_cp1250); + collation[45] = new Collation(45, "utf8mb4_general_ci", 1, MYSQL_CHARSET_NAME_utf8mb4); + collation[46] = new Collation(46, "utf8mb4_bin", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[47] = new Collation(47, "latin1_bin", 0, MYSQL_CHARSET_NAME_latin1); + collation[48] = new Collation(48, "latin1_general_ci", 0, MYSQL_CHARSET_NAME_latin1); + collation[49] = new Collation(49, "latin1_general_cs", 0, MYSQL_CHARSET_NAME_latin1); + collation[50] = new Collation(50, "cp1251_bin", 0, MYSQL_CHARSET_NAME_cp1251); + collation[51] = new Collation(51, "cp1251_general_ci", 1, MYSQL_CHARSET_NAME_cp1251); + collation[52] = new Collation(52, "cp1251_general_cs", 0, MYSQL_CHARSET_NAME_cp1251); + collation[53] = new Collation(53, "macroman_bin", 0, MYSQL_CHARSET_NAME_macroman); + collation[54] = new Collation(54, "utf16_general_ci", 1, MYSQL_CHARSET_NAME_utf16); + collation[55] = new Collation(55, "utf16_bin", 0, MYSQL_CHARSET_NAME_utf16); + collation[56] = new Collation(56, "utf16le_general_ci", 1, MYSQL_CHARSET_NAME_utf16le); + collation[57] = new Collation(57, "cp1256_general_ci", 1, MYSQL_CHARSET_NAME_cp1256); + collation[58] = new Collation(58, "cp1257_bin", 0, MYSQL_CHARSET_NAME_cp1257); + collation[59] = new Collation(59, "cp1257_general_ci", 1, MYSQL_CHARSET_NAME_cp1257); + collation[60] = new Collation(60, "utf32_general_ci", 1, MYSQL_CHARSET_NAME_utf32); + collation[61] = new Collation(61, "utf32_bin", 0, MYSQL_CHARSET_NAME_utf32); + collation[62] = new Collation(62, "utf16le_bin", 0, MYSQL_CHARSET_NAME_utf16le); + collation[63] = new Collation(63, "binary", 1, MYSQL_CHARSET_NAME_binary); + collation[64] = new Collation(64, "armscii8_bin", 0, MYSQL_CHARSET_NAME_armscii8); + collation[65] = new Collation(65, "ascii_bin", 0, MYSQL_CHARSET_NAME_ascii); + collation[66] = new Collation(66, "cp1250_bin", 0, MYSQL_CHARSET_NAME_cp1250); + collation[67] = new Collation(67, "cp1256_bin", 0, MYSQL_CHARSET_NAME_cp1256); + collation[68] = new Collation(68, "cp866_bin", 0, MYSQL_CHARSET_NAME_cp866); + collation[69] = new Collation(69, "dec8_bin", 0, MYSQL_CHARSET_NAME_dec8); + collation[70] = new Collation(70, "greek_bin", 0, MYSQL_CHARSET_NAME_greek); + collation[71] = new Collation(71, "hebrew_bin", 0, MYSQL_CHARSET_NAME_hebrew); + collation[72] = new Collation(72, "hp8_bin", 0, MYSQL_CHARSET_NAME_hp8); + collation[73] = new Collation(73, "keybcs2_bin", 0, MYSQL_CHARSET_NAME_keybcs2); + collation[74] = new Collation(74, "koi8r_bin", 0, MYSQL_CHARSET_NAME_koi8r); + collation[75] = new Collation(75, "koi8u_bin", 0, MYSQL_CHARSET_NAME_koi8u); + collation[76] = new Collation(76, "utf8_tolower_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[77] = new Collation(77, "latin2_bin", 0, MYSQL_CHARSET_NAME_latin2); + collation[78] = new Collation(78, "latin5_bin", 0, MYSQL_CHARSET_NAME_latin5); + collation[79] = new Collation(79, "latin7_bin", 0, MYSQL_CHARSET_NAME_latin7); + collation[80] = new Collation(80, "cp850_bin", 0, MYSQL_CHARSET_NAME_cp850); + collation[81] = new Collation(81, "cp852_bin", 0, MYSQL_CHARSET_NAME_cp852); + collation[82] = new Collation(82, "swe7_bin", 0, MYSQL_CHARSET_NAME_swe7); + collation[83] = new Collation(83, "utf8_bin", 0, MYSQL_CHARSET_NAME_utf8); + collation[84] = new Collation(84, "big5_bin", 0, MYSQL_CHARSET_NAME_big5); + collation[85] = new Collation(85, "euckr_bin", 0, MYSQL_CHARSET_NAME_euckr); + collation[86] = new Collation(86, "gb2312_bin", 0, MYSQL_CHARSET_NAME_gb2312); + collation[87] = new Collation(87, "gbk_bin", 0, MYSQL_CHARSET_NAME_gbk); + collation[88] = new Collation(88, "sjis_bin", 0, MYSQL_CHARSET_NAME_sjis); + collation[89] = new Collation(89, "tis620_bin", 0, MYSQL_CHARSET_NAME_tis620); + collation[90] = new Collation(90, "ucs2_bin", 0, MYSQL_CHARSET_NAME_ucs2); + collation[91] = new Collation(91, "ujis_bin", 0, MYSQL_CHARSET_NAME_ujis); + collation[92] = new Collation(92, "geostd8_general_ci", 0, MYSQL_CHARSET_NAME_geostd8); + collation[93] = new Collation(93, "geostd8_bin", 0, MYSQL_CHARSET_NAME_geostd8); + collation[94] = new Collation(94, "latin1_spanish_ci", 0, MYSQL_CHARSET_NAME_latin1); + collation[95] = new Collation(95, "cp932_japanese_ci", 1, MYSQL_CHARSET_NAME_cp932); + collation[96] = new Collation(96, "cp932_bin", 0, MYSQL_CHARSET_NAME_cp932); + collation[97] = new Collation(97, "eucjpms_japanese_ci", 1, MYSQL_CHARSET_NAME_eucjpms); + collation[98] = new Collation(98, "eucjpms_bin", 0, MYSQL_CHARSET_NAME_eucjpms); + collation[99] = new Collation(99, "cp1250_polish_ci", 0, MYSQL_CHARSET_NAME_cp1250); + + collation[101] = new Collation(101, "utf16_unicode_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[102] = new Collation(102, "utf16_icelandic_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[103] = new Collation(103, "utf16_latvian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[104] = new Collation(104, "utf16_romanian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[105] = new Collation(105, "utf16_slovenian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[106] = new Collation(106, "utf16_polish_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[107] = new Collation(107, "utf16_estonian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[108] = new Collation(108, "utf16_spanish_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[109] = new Collation(109, "utf16_swedish_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[110] = new Collation(110, "utf16_turkish_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[111] = new Collation(111, "utf16_czech_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[112] = new Collation(112, "utf16_danish_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[113] = new Collation(113, "utf16_lithuanian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[114] = new Collation(114, "utf16_slovak_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[115] = new Collation(115, "utf16_spanish2_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[116] = new Collation(116, "utf16_roman_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[117] = new Collation(117, "utf16_persian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[118] = new Collation(118, "utf16_esperanto_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[119] = new Collation(119, "utf16_hungarian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[120] = new Collation(120, "utf16_sinhala_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[121] = new Collation(121, "utf16_german2_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[122] = new Collation(122, "utf16_croatian_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[123] = new Collation(123, "utf16_unicode_520_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[124] = new Collation(124, "utf16_vietnamese_ci", 0, MYSQL_CHARSET_NAME_utf16); + + collation[128] = new Collation(128, "ucs2_unicode_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[129] = new Collation(129, "ucs2_icelandic_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[130] = new Collation(130, "ucs2_latvian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[131] = new Collation(131, "ucs2_romanian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[132] = new Collation(132, "ucs2_slovenian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[133] = new Collation(133, "ucs2_polish_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[134] = new Collation(134, "ucs2_estonian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[135] = new Collation(135, "ucs2_spanish_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[136] = new Collation(136, "ucs2_swedish_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[137] = new Collation(137, "ucs2_turkish_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[138] = new Collation(138, "ucs2_czech_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[139] = new Collation(139, "ucs2_danish_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[140] = new Collation(140, "ucs2_lithuanian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[141] = new Collation(141, "ucs2_slovak_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[142] = new Collation(142, "ucs2_spanish2_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[143] = new Collation(143, "ucs2_roman_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[144] = new Collation(144, "ucs2_persian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[145] = new Collation(145, "ucs2_esperanto_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[146] = new Collation(146, "ucs2_hungarian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[147] = new Collation(147, "ucs2_sinhala_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[148] = new Collation(148, "ucs2_german2_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[149] = new Collation(149, "ucs2_croatian_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[150] = new Collation(150, "ucs2_unicode_520_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[151] = new Collation(151, "ucs2_vietnamese_ci", 0, MYSQL_CHARSET_NAME_ucs2); + + collation[159] = new Collation(159, "ucs2_general_mysql500_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[160] = new Collation(160, "utf32_unicode_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[161] = new Collation(161, "utf32_icelandic_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[162] = new Collation(162, "utf32_latvian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[163] = new Collation(163, "utf32_romanian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[164] = new Collation(164, "utf32_slovenian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[165] = new Collation(165, "utf32_polish_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[166] = new Collation(166, "utf32_estonian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[167] = new Collation(167, "utf32_spanish_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[168] = new Collation(168, "utf32_swedish_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[169] = new Collation(169, "utf32_turkish_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[170] = new Collation(170, "utf32_czech_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[171] = new Collation(171, "utf32_danish_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[172] = new Collation(172, "utf32_lithuanian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[173] = new Collation(173, "utf32_slovak_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[174] = new Collation(174, "utf32_spanish2_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[175] = new Collation(175, "utf32_roman_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[176] = new Collation(176, "utf32_persian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[177] = new Collation(177, "utf32_esperanto_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[178] = new Collation(178, "utf32_hungarian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[179] = new Collation(179, "utf32_sinhala_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[180] = new Collation(180, "utf32_german2_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[181] = new Collation(181, "utf32_croatian_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[182] = new Collation(182, "utf32_unicode_520_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[183] = new Collation(183, "utf32_vietnamese_ci", 0, MYSQL_CHARSET_NAME_utf32); + + collation[192] = new Collation(192, "utf8_unicode_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[193] = new Collation(193, "utf8_icelandic_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[194] = new Collation(194, "utf8_latvian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[195] = new Collation(195, "utf8_romanian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[196] = new Collation(196, "utf8_slovenian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[197] = new Collation(197, "utf8_polish_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[198] = new Collation(198, "utf8_estonian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[199] = new Collation(199, "utf8_spanish_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[200] = new Collation(200, "utf8_swedish_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[201] = new Collation(201, "utf8_turkish_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[202] = new Collation(202, "utf8_czech_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[203] = new Collation(203, "utf8_danish_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[204] = new Collation(204, "utf8_lithuanian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[205] = new Collation(205, "utf8_slovak_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[206] = new Collation(206, "utf8_spanish2_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[207] = new Collation(207, "utf8_roman_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[208] = new Collation(208, "utf8_persian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[209] = new Collation(209, "utf8_esperanto_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[210] = new Collation(210, "utf8_hungarian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[211] = new Collation(211, "utf8_sinhala_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[212] = new Collation(212, "utf8_german2_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[213] = new Collation(213, "utf8_croatian_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[214] = new Collation(214, "utf8_unicode_520_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[215] = new Collation(215, "utf8_vietnamese_ci", 0, MYSQL_CHARSET_NAME_utf8); + + collation[223] = new Collation(223, "utf8_general_mysql500_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[224] = new Collation(224, "utf8mb4_unicode_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[225] = new Collation(225, "utf8mb4_icelandic_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[226] = new Collation(226, "utf8mb4_latvian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[227] = new Collation(227, "utf8mb4_romanian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[228] = new Collation(228, "utf8mb4_slovenian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[229] = new Collation(229, "utf8mb4_polish_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[230] = new Collation(230, "utf8mb4_estonian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[231] = new Collation(231, "utf8mb4_spanish_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[232] = new Collation(232, "utf8mb4_swedish_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[233] = new Collation(233, "utf8mb4_turkish_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[234] = new Collation(234, "utf8mb4_czech_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[235] = new Collation(235, "utf8mb4_danish_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[236] = new Collation(236, "utf8mb4_lithuanian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[237] = new Collation(237, "utf8mb4_slovak_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[238] = new Collation(238, "utf8mb4_spanish2_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[239] = new Collation(239, "utf8mb4_roman_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[240] = new Collation(240, "utf8mb4_persian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[241] = new Collation(241, "utf8mb4_esperanto_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[242] = new Collation(242, "utf8mb4_hungarian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[243] = new Collation(243, "utf8mb4_sinhala_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[244] = new Collation(244, "utf8mb4_german2_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[245] = new Collation(245, "utf8mb4_croatian_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[246] = new Collation(246, "utf8mb4_unicode_520_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[247] = new Collation(247, "utf8mb4_vietnamese_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[248] = new Collation(248, "gb18030_chinese_ci", 1, MYSQL_CHARSET_NAME_gb18030); + collation[249] = new Collation(249, "gb18030_bin", 0, MYSQL_CHARSET_NAME_gb18030); + collation[250] = new Collation(250, "gb18030_unicode_520_ci", 0, MYSQL_CHARSET_NAME_gb18030); + + collation[255] = new Collation(255, "utf8mb4_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[256] = new Collation(256, "utf8mb4_de_pb_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[257] = new Collation(257, "utf8mb4_is_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[258] = new Collation(258, "utf8mb4_lv_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[259] = new Collation(259, "utf8mb4_ro_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[260] = new Collation(260, "utf8mb4_sl_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[261] = new Collation(261, "utf8mb4_pl_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[262] = new Collation(262, "utf8mb4_et_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[263] = new Collation(263, "utf8mb4_es_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[264] = new Collation(264, "utf8mb4_sv_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[265] = new Collation(265, "utf8mb4_tr_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[266] = new Collation(266, "utf8mb4_cs_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[267] = new Collation(267, "utf8mb4_da_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[268] = new Collation(268, "utf8mb4_lt_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[269] = new Collation(269, "utf8mb4_sk_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[270] = new Collation(270, "utf8mb4_es_trad_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[271] = new Collation(271, "utf8mb4_la_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + + collation[273] = new Collation(273, "utf8mb4_eo_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[274] = new Collation(274, "utf8mb4_hu_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[275] = new Collation(275, "utf8mb4_hr_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + + collation[277] = new Collation(277, "utf8mb4_vi_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + + collation[278] = new Collation(278, "utf8mb4_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[279] = new Collation(279, "utf8mb4_de_pb_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[280] = new Collation(280, "utf8mb4_is_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[281] = new Collation(281, "utf8mb4_lv_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[282] = new Collation(282, "utf8mb4_ro_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[283] = new Collation(283, "utf8mb4_sl_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[284] = new Collation(284, "utf8mb4_pl_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[285] = new Collation(285, "utf8mb4_et_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[286] = new Collation(286, "utf8mb4_es_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[287] = new Collation(287, "utf8mb4_sv_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[288] = new Collation(288, "utf8mb4_tr_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[289] = new Collation(289, "utf8mb4_cs_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[290] = new Collation(290, "utf8mb4_da_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[291] = new Collation(291, "utf8mb4_lt_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[292] = new Collation(292, "utf8mb4_sk_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[293] = new Collation(293, "utf8mb4_es_trad_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[294] = new Collation(294, "utf8mb4_la_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + + collation[296] = new Collation(296, "utf8mb4_eo_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[297] = new Collation(297, "utf8mb4_hu_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[298] = new Collation(298, "utf8mb4_hr_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + + collation[300] = new Collation(300, "utf8mb4_vi_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + + collation[303] = new Collation(303, "utf8mb4_ja_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[304] = new Collation(304, "utf8mb4_ja_0900_as_cs_ks", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[305] = new Collation(305, "utf8mb4_0900_as_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[306] = new Collation(306, "utf8mb4_ru_0900_ai_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[307] = new Collation(307, "utf8mb4_ru_0900_as_cs", 0, MYSQL_CHARSET_NAME_utf8mb4); + + collation[326] = new Collation(326, "utf8mb4_test_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + collation[327] = new Collation(327, "utf16_test_ci", 0, MYSQL_CHARSET_NAME_utf16); + collation[328] = new Collation(328, "utf8mb4_test_400_ci", 0, MYSQL_CHARSET_NAME_utf8mb4); + + collation[336] = new Collation(336, "utf8_bengali_standard_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[337] = new Collation(337, "utf8_bengali_traditional_ci", 0, MYSQL_CHARSET_NAME_utf8); + + collation[352] = new Collation(352, "utf8_phone_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[353] = new Collation(353, "utf8_test_ci", 0, MYSQL_CHARSET_NAME_utf8); + collation[354] = new Collation(354, "utf8_5624_1", 0, MYSQL_CHARSET_NAME_utf8); + collation[355] = new Collation(355, "utf8_5624_2", 0, MYSQL_CHARSET_NAME_utf8); + collation[356] = new Collation(356, "utf8_5624_3", 0, MYSQL_CHARSET_NAME_utf8); + collation[357] = new Collation(357, "utf8_5624_4", 0, MYSQL_CHARSET_NAME_utf8); + collation[358] = new Collation(358, "ucs2_test_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[359] = new Collation(359, "ucs2_vn_ci", 0, MYSQL_CHARSET_NAME_ucs2); + collation[360] = new Collation(360, "ucs2_5624_1", 0, MYSQL_CHARSET_NAME_ucs2); + + collation[368] = new Collation(368, "utf8_5624_5", 0, MYSQL_CHARSET_NAME_utf8); + collation[391] = new Collation(391, "utf32_test_ci", 0, MYSQL_CHARSET_NAME_utf32); + collation[2047] = new Collation(2047, "utf8_maxuserid_ci", 0, MYSQL_CHARSET_NAME_utf8); + + COLLATION_INDEX_TO_COLLATION_NAME = new String[MAP_SIZE]; + COLLATION_INDEX_TO_CHARSET = new MysqlCharset[MAP_SIZE]; + Map charsetNameToCollationIndexMap = new TreeMap<>(); + Map charsetNameToCollationPriorityMap = new TreeMap<>(); + Set tempUTF8MB4Indexes = new HashSet<>(); + + Collation notUsedCollation = new Collation(0, COLLATION_NOT_DEFINED, 0, NOT_USED); + for (int i = 1; i < MAP_SIZE; i++) { + Collation coll = collation[i] != null ? collation[i] : notUsedCollation; + COLLATION_INDEX_TO_COLLATION_NAME[i] = coll.collationName; + COLLATION_INDEX_TO_CHARSET[i] = coll.mysqlCharset; + String charsetName = coll.mysqlCharset.charsetName; + + if (!charsetNameToCollationIndexMap.containsKey(charsetName) || charsetNameToCollationPriorityMap.get(charsetName) < coll.priority) { + charsetNameToCollationIndexMap.put(charsetName, i); + charsetNameToCollationPriorityMap.put(charsetName, coll.priority); + } + + // Filling indexes of utf8mb4 collations + if (charsetName.equals(MYSQL_CHARSET_NAME_utf8mb4)) { + tempUTF8MB4Indexes.add(i); + } + } + + CHARSET_NAME_TO_COLLATION_INDEX = Collections.unmodifiableMap(charsetNameToCollationIndexMap); + UTF8MB4_INDEXES = Collections.unmodifiableSet(tempUTF8MB4Indexes); + + collation = null; + } + + public final static String getMysqlCharsetForJavaEncoding(String javaEncoding, ServerVersion version) { + + List mysqlCharsets = CharsetMapping.JAVA_ENCODING_UC_TO_MYSQL_CHARSET.get(javaEncoding.toUpperCase(Locale.ENGLISH)); + + if (mysqlCharsets != null) { + Iterator iter = mysqlCharsets.iterator(); + + MysqlCharset currentChoice = null; + + while (iter.hasNext()) { + MysqlCharset charset = iter.next(); + + if (version == null) { + // Take the first one we get + + return charset.charsetName; + } + + if (currentChoice == null || currentChoice.minimumVersion.compareTo(charset.minimumVersion) < 0 + || currentChoice.priority < charset.priority && currentChoice.minimumVersion.compareTo(charset.minimumVersion) == 0) { + if (charset.isOkayForVersion(version)) { + currentChoice = charset; + } + } + } + + if (currentChoice != null) { + return currentChoice.charsetName; + } + } + + return null; + } + + public static int getCollationIndexForJavaEncoding(String javaEncoding, ServerVersion version) { + String charsetName = getMysqlCharsetForJavaEncoding(javaEncoding, version); + if (charsetName != null) { + Integer ci = CHARSET_NAME_TO_COLLATION_INDEX.get(charsetName); + if (ci != null) { + return ci.intValue(); + } + } + return 0; + } + + public static String getMysqlCharsetNameForCollationIndex(Integer collationIndex) { + if (collationIndex != null && collationIndex > 0 && collationIndex < MAP_SIZE) { + return COLLATION_INDEX_TO_CHARSET[collationIndex].charsetName; + } + return null; + } + + /** + * MySQL charset could map to several Java encodings. + * So here we choose the one according to next rules: + *

    + *
  • if there is no static mapping for this charset then return javaEncoding value as is because this + * could be a custom charset for example + *
  • if static mapping exists and javaEncoding equals to one of Java encoding canonical names or aliases available + * for this mapping then javaEncoding value as is; this is required when result should match to connection encoding, for example if connection encoding is + * Cp943 we must avoid getting SHIFT_JIS for sjis mysql charset + *
  • if static mapping exists and javaEncoding doesn't match any Java encoding canonical + * names or aliases available for this mapping then return default Java encoding (the first in mapping list) + *
+ * + * @param mysqlCharsetName + * MySQL charset name + * @param javaEncoding + * fall-back java encoding name + * @return java encoding name + */ + public static String getJavaEncodingForMysqlCharset(String mysqlCharsetName, String javaEncoding) { + String res = javaEncoding; + MysqlCharset cs = CHARSET_NAME_TO_CHARSET.get(mysqlCharsetName); + if (cs != null) { + res = cs.getMatchingJavaEncoding(javaEncoding); + } + return res; + } + + public static String getJavaEncodingForMysqlCharset(String mysqlCharsetName) { + return getJavaEncodingForMysqlCharset(mysqlCharsetName, null); + } + + public static String getJavaEncodingForCollationIndex(Integer collationIndex, String javaEncoding) { + if (collationIndex != null && collationIndex > 0 && collationIndex < MAP_SIZE) { + MysqlCharset cs = COLLATION_INDEX_TO_CHARSET[collationIndex]; + return cs.getMatchingJavaEncoding(javaEncoding); + } + return null; + } + + public static String getJavaEncodingForCollationIndex(Integer collationIndex) { + return getJavaEncodingForCollationIndex(collationIndex, null); + } + + public final static int getNumberOfCharsetsConfigured() { + return numberOfEncodingsConfigured; + } + + /** + * Does the character set contain multi-byte encoded characters. + * + * @param javaEncodingName + * java encoding name + * @return true if the character set contains multi-byte encoded characters. + */ + final public static boolean isMultibyteCharset(String javaEncodingName) { + return MULTIBYTE_ENCODINGS.contains(javaEncodingName.toUpperCase(Locale.ENGLISH)); + } + + public static int getMblen(String charsetName) { + if (charsetName != null) { + MysqlCharset cs = CHARSET_NAME_TO_CHARSET.get(charsetName); + if (cs != null) { + return cs.mblen; + } + } + return 0; + } +} + +class MysqlCharset { + public final String charsetName; + public final int mblen; + public final int priority; + public final List javaEncodingsUc = new ArrayList<>(); + + public final ServerVersion minimumVersion; + + /** + * Constructs MysqlCharset object + * + * @param charsetName + * MySQL charset name + * @param mblen + * Max number of bytes per character + * @param priority + * MysqlCharset with highest value of this param will be used for Java encoding --> Mysql charsets conversion. + * @param javaEncodings + * List of Java encodings corresponding to this MySQL charset; the first name in list is the default for mysql --> java data conversion + */ + public MysqlCharset(String charsetName, int mblen, int priority, String[] javaEncodings) { + this(charsetName, mblen, priority, javaEncodings, new ServerVersion(0, 0, 0)); + } + + private void addEncodingMapping(String encoding) { + String encodingUc = encoding.toUpperCase(Locale.ENGLISH); + + if (!this.javaEncodingsUc.contains(encodingUc)) { + this.javaEncodingsUc.add(encodingUc); + } + } + + public MysqlCharset(String charsetName, int mblen, int priority, String[] javaEncodings, ServerVersion minimumVersion) { + this.charsetName = charsetName; + this.mblen = mblen; + this.priority = priority; + + for (int i = 0; i < javaEncodings.length; i++) { + String encoding = javaEncodings[i]; + try { + Charset cs = Charset.forName(encoding); + addEncodingMapping(cs.name()); + + Set als = cs.aliases(); + Iterator ali = als.iterator(); + while (ali.hasNext()) { + addEncodingMapping(ali.next()); + } + } catch (Exception e) { + // if there is no support of this charset in JVM it's still possible to use our converter for 1-byte charsets + if (mblen == 1) { + addEncodingMapping(encoding); + } + } + } + + if (this.javaEncodingsUc.size() == 0) { + if (mblen > 1) { + addEncodingMapping("UTF-8"); + } else { + addEncodingMapping("Cp1252"); + } + } + + this.minimumVersion = minimumVersion; + } + + @Override + public String toString() { + StringBuilder asString = new StringBuilder(); + asString.append("["); + asString.append("charsetName="); + asString.append(this.charsetName); + asString.append(",mblen="); + asString.append(this.mblen); + // asString.append(",javaEncoding="); + // asString.append(this.javaEncodings.toString()); + asString.append("]"); + return asString.toString(); + } + + boolean isOkayForVersion(ServerVersion version) { + return version.meetsMinimum(this.minimumVersion); + } + + /** + * If javaEncoding parameter value is one of available java encodings for this charset + * then returns javaEncoding value as is. Otherwise returns first available java encoding name. + * + * @param javaEncoding + * java encoding name + * @return java encoding name + */ + String getMatchingJavaEncoding(String javaEncoding) { + if (javaEncoding != null && this.javaEncodingsUc.contains(javaEncoding.toUpperCase(Locale.ENGLISH))) { + return javaEncoding; + } + return this.javaEncodingsUc.get(0); + } +} + +class Collation { + public final int index; + public final String collationName; + public final int priority; + public final MysqlCharset mysqlCharset; + + public Collation(int index, String collationName, int priority, String charsetName) { + this.index = index; + this.collationName = collationName; + this.priority = priority; + this.mysqlCharset = CharsetMapping.CHARSET_NAME_TO_CHARSET.get(charsetName); + } + + @Override + public String toString() { + StringBuilder asString = new StringBuilder(); + asString.append("["); + asString.append("index="); + asString.append(this.index); + asString.append(",collationName="); + asString.append(this.collationName); + asString.append(",charsetName="); + asString.append(this.mysqlCharset.charsetName); + asString.append(",javaCharsetName="); + asString.append(this.mysqlCharset.getMatchingJavaEncoding(null)); + asString.append("]"); + return asString.toString(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/ClientPreparedQuery.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/ClientPreparedQuery.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/ClientPreparedQuery.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import com.mysql.cj.util.StringUtils; + +//TODO should not be protocol-specific + +public class ClientPreparedQuery extends AbstractPreparedQuery { + + public ClientPreparedQuery(NativeSession sess) { + super(sess); + } + + /** + * Computes the maximum parameter set size, and entire batch size given + * the number of arguments in the batch. + * + */ + @Override + protected long[] computeMaxParameterSetSizeAndBatchSize(int numBatchedArgs) { + long sizeOfEntireBatch = 0; + long maxSizeOfParameterSet = 0; + + for (int i = 0; i < numBatchedArgs; i++) { + ClientPreparedQueryBindings qBindings = (ClientPreparedQueryBindings) this.batchedArgs.get(i); + + BindValue[] bindValues = qBindings.getBindValues(); + + long sizeOfParameterSet = 0; + + for (int j = 0; j < bindValues.length; j++) { + if (!bindValues[j].isNull()) { + + if (bindValues[j].isStream()) { + int streamLength = bindValues[j].getStreamLength(); + + if (streamLength != -1) { + sizeOfParameterSet += streamLength * 2; // for safety in escaping + } else { + int paramLength = qBindings.getBindValues()[j].getByteValue().length; + sizeOfParameterSet += paramLength; + } + } else { + sizeOfParameterSet += qBindings.getBindValues()[j].getByteValue().length; + } + } else { + sizeOfParameterSet += 4; // for NULL literal in SQL + } + } + + // + // Account for static part of values clause + // This is a little naive, because the ?s will be replaced but it gives us some padding, and is less housekeeping to ignore them. We're looking + // for a "fuzzy" value here anyway + // + + if (this.parseInfo.getValuesClause() != null) { + sizeOfParameterSet += this.parseInfo.getValuesClause().length() + 1; + } else { + sizeOfParameterSet += this.originalSql.length() + 1; + } + + sizeOfEntireBatch += sizeOfParameterSet; + + if (sizeOfParameterSet > maxSizeOfParameterSet) { + maxSizeOfParameterSet = sizeOfParameterSet; + } + } + + return new long[] { maxSizeOfParameterSet, sizeOfEntireBatch }; + } + + /** + * @param parameterIndex + */ + public byte[] getBytesRepresentation(int parameterIndex) { + BindValue bv = this.queryBindings.getBindValues()[parameterIndex]; + + if (bv.isStream()) { + return streamToBytes(bv.getStreamValue(), false, bv.getStreamLength(), this.useStreamLengthsInPrepStmts.getValue()); + } + + byte[] parameterVal = bv.getByteValue(); + + if (parameterVal == null) { + return null; + } + + if ((parameterVal[0] == '\'') && (parameterVal[parameterVal.length - 1] == '\'')) { + byte[] valNoQuotes = new byte[parameterVal.length - 2]; + System.arraycopy(parameterVal, 1, valNoQuotes, 0, parameterVal.length - 2); + + return valNoQuotes; + } + + return parameterVal; + } + + /** + * Get bytes representation for a parameter in a statement batch. + * + * @param parameterIndex + * @param commandIndex + */ + public byte[] getBytesRepresentationForBatch(int parameterIndex, int commandIndex) { + Object batchedArg = this.batchedArgs.get(commandIndex); + if (batchedArg instanceof String) { + return StringUtils.getBytes((String) batchedArg, this.charEncoding); + } + + BindValue bv = ((ClientPreparedQueryBindings) batchedArg).getBindValues()[parameterIndex]; + if (bv.isStream()) { + return streamToBytes(bv.getStreamValue(), false, bv.getStreamLength(), this.useStreamLengthsInPrepStmts.getValue()); + } + byte parameterVal[] = bv.getByteValue(); + if (parameterVal == null) { + return null; + } + + if ((parameterVal[0] == '\'') && (parameterVal[parameterVal.length - 1] == '\'')) { + byte[] valNoQuotes = new byte[parameterVal.length - 2]; + System.arraycopy(parameterVal, 1, valNoQuotes, 0, parameterVal.length - 2); + + return valNoQuotes; + } + + return parameterVal; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/ClientPreparedQueryBindValue.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/ClientPreparedQueryBindValue.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/ClientPreparedQueryBindValue.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.io.InputStream; + +//TODO should not be protocol-specific + +public class ClientPreparedQueryBindValue implements BindValue { + + /** NULL indicator */ + protected boolean isNull; + + private boolean isStream = false; + + protected MysqlType parameterType = MysqlType.NULL; + + private byte[] parameterValue = null; + + private InputStream parameterStream = null; + + private int streamLength; + + public ClientPreparedQueryBindValue() { + } + + @Override + public ClientPreparedQueryBindValue clone() { + return new ClientPreparedQueryBindValue(this); + } + + protected ClientPreparedQueryBindValue(ClientPreparedQueryBindValue copyMe) { + this.isNull = copyMe.isNull; + this.isStream = copyMe.isStream; + this.parameterType = copyMe.parameterType; + if (copyMe.parameterValue != null) { + this.parameterValue = new byte[copyMe.parameterValue.length]; + System.arraycopy(copyMe.parameterValue, 0, this.parameterValue, 0, copyMe.parameterValue.length); + } + this.parameterStream = copyMe.parameterStream; + this.streamLength = copyMe.streamLength; + } + + public void reset() { + this.isNull = false; + this.isStream = false; + this.parameterType = MysqlType.NULL; + } + + @Override + public boolean isNull() { + return this.isNull; + } + + public void setNull(boolean isNull) { + this.isNull = isNull; + if (isNull) { + this.parameterType = MysqlType.NULL; + } + } + + public boolean isStream() { + return this.isStream; + } + + public void setIsStream(boolean isStream) { + this.isStream = isStream; + } + + @Override + public MysqlType getMysqlType() { + return this.parameterType; + } + + @Override + public void setMysqlType(MysqlType type) { + this.parameterType = type; + } + + public byte[] getByteValue() { + return this.parameterValue; + } + + public void setByteValue(byte[] parameterValue) { + this.isNull = false; + this.isStream = false; + this.parameterValue = parameterValue; + this.parameterStream = null; + this.streamLength = 0; + } + + public InputStream getStreamValue() { + return this.parameterStream; + } + + public void setStreamValue(InputStream parameterStream, int streamLength) { + this.parameterStream = parameterStream; + this.streamLength = streamLength; + } + + public int getStreamLength() { + return this.streamLength; + } + + public boolean isSet() { + return this.parameterValue != null || this.parameterStream != null; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/ClientPreparedQueryBindings.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/ClientPreparedQueryBindings.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/ClientPreparedQueryBindings.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,805 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Time; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Locale; +import java.util.TimeZone; + +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.util.StringUtils; +import com.mysql.cj.util.TimeUtil; +import com.mysql.cj.util.Util; + +//TODO should not be protocol-specific + +public class ClientPreparedQueryBindings extends AbstractQueryBindings { + + /** Charset encoder used to escape if needed, such as Yen sign in SJIS */ + private CharsetEncoder charsetEncoder; + + private SimpleDateFormat ddf; + + private SimpleDateFormat tdf; + + private SimpleDateFormat tsdf = null; + + public ClientPreparedQueryBindings(int parameterCount, Session sess) { + super(parameterCount, sess); + if (((NativeSession) this.session).getRequiresEscapingEncoder()) { + this.charsetEncoder = Charset.forName(this.charEncoding).newEncoder(); + } + } + + @Override + protected void initBindValues(int parameterCount) { + this.bindValues = new ClientPreparedQueryBindValue[parameterCount]; + for (int i = 0; i < parameterCount; i++) { + this.bindValues[i] = new ClientPreparedQueryBindValue(); + } + } + + @Override + public ClientPreparedQueryBindings clone() { + ClientPreparedQueryBindings newBindings = new ClientPreparedQueryBindings(this.bindValues.length, this.session); + ClientPreparedQueryBindValue[] bvs = new ClientPreparedQueryBindValue[this.bindValues.length]; + for (int i = 0; i < this.bindValues.length; i++) { + bvs[i] = this.bindValues[i].clone(); + } + newBindings.setBindValues(bvs); + newBindings.isLoadDataQuery = this.isLoadDataQuery; + return newBindings; + } + + @Override + public void checkParameterSet(int columnIndex) { + if (!this.bindValues[columnIndex].isSet()) { + throw ExceptionFactory.createException(Messages.getString("PreparedStatement.40") + (columnIndex + 1), + MysqlErrorNumbers.SQL_STATE_WRONG_NO_OF_PARAMETERS, 0, true, null, this.session.getExceptionInterceptor()); + } + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x) { + setAsciiStream(parameterIndex, x, -1); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) { + if (x == null) { + setNull(parameterIndex); + } else { + setBinaryStream(parameterIndex, x, length); + } + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, long length) { + setAsciiStream(parameterIndex, x, (int) length); + this.bindValues[parameterIndex].setMysqlType(MysqlType.TEXT); // TODO was Types.CLOB, check; use length to find right TEXT type + } + + @Override + public void setBigDecimal(int parameterIndex, BigDecimal x) { + if (x == null) { + setNull(parameterIndex); + } else { + setValue(parameterIndex, StringUtils.fixDecimalExponent(x.toPlainString()), MysqlType.DECIMAL); + } + } + + @Override + public void setBigInteger(int parameterIndex, BigInteger x) { + setValue(parameterIndex, x.toString(), MysqlType.BIGINT_UNSIGNED); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x) { + setBinaryStream(parameterIndex, x, -1); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) { + if (x == null) { + setNull(parameterIndex); + } else { + this.bindValues[parameterIndex].setNull(false); + this.bindValues[parameterIndex].setIsStream(true); + this.bindValues[parameterIndex].setMysqlType(MysqlType.BLOB); // TODO use length to find the right BLOB type + this.bindValues[parameterIndex].setStreamValue(x, length); + } + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, long length) { + setBinaryStream(parameterIndex, x, (int) length); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream) { + setBinaryStream(parameterIndex, inputStream); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream, long length) { + setBinaryStream(parameterIndex, inputStream, (int) length); + } + + @Override + public void setBlob(int parameterIndex, Blob x) { + if (x == null) { + setNull(parameterIndex); + } else { + try { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + + bytesOut.write('\''); + StringUtils.escapeblockFast(x.getBytes(1, (int) x.length()), bytesOut, (int) x.length(), + this.session.getServerSession().useAnsiQuotedIdentifiers()); + bytesOut.write('\''); + + setValue(parameterIndex, bytesOut.toByteArray(), MysqlType.BLOB); + } catch (Throwable t) { + throw ExceptionFactory.createException(t.getMessage(), t); + } + } + } + + @Override + public void setBoolean(int parameterIndex, boolean x) { + setValue(parameterIndex, x ? "1" : "0"); + } + + @Override + public void setByte(int parameterIndex, byte x) { + setValue(parameterIndex, String.valueOf(x), MysqlType.TINYINT); + } + + public void setBytes(int parameterIndex, byte[] x) { + setBytes(parameterIndex, x, true, true); + + if (x != null) { + this.bindValues[parameterIndex].setMysqlType(MysqlType.BINARY); // TODO VARBINARY ? + } + } + + public synchronized void setBytes(int parameterIndex, byte[] x, boolean checkForIntroducer, boolean escapeForMBChars) { + if (x == null) { + setNull(parameterIndex); // setNull(parameterIndex, MysqlType.BINARY); + } else { + if (this.session.getServerSession().isNoBackslashEscapesSet() || (escapeForMBChars && CharsetMapping.isMultibyteCharset(this.charEncoding))) { + + // Send as hex + + ByteArrayOutputStream bOut = new ByteArrayOutputStream((x.length * 2) + 3); + bOut.write('x'); + bOut.write('\''); + + for (int i = 0; i < x.length; i++) { + int lowBits = (x[i] & 0xff) / 16; + int highBits = (x[i] & 0xff) % 16; + + bOut.write(HEX_DIGITS[lowBits]); + bOut.write(HEX_DIGITS[highBits]); + } + + bOut.write('\''); + + setValue(parameterIndex, bOut.toByteArray()); + + return; + } + + // escape them + int numBytes = x.length; + + int pad = 2; + + if (checkForIntroducer) { + pad += 7; + } + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(numBytes + pad); + + if (checkForIntroducer) { + bOut.write('_'); + bOut.write('b'); + bOut.write('i'); + bOut.write('n'); + bOut.write('a'); + bOut.write('r'); + bOut.write('y'); + } + bOut.write('\''); + + for (int i = 0; i < numBytes; ++i) { + byte b = x[i]; + + switch (b) { + case 0: /* Must be escaped for 'mysql' */ + bOut.write('\\'); + bOut.write('0'); + break; + case '\n': /* Must be escaped for logs */ + bOut.write('\\'); + bOut.write('n'); + break; + case '\r': + bOut.write('\\'); + bOut.write('r'); + break; + case '\\': + bOut.write('\\'); + bOut.write('\\'); + break; + case '\'': + bOut.write('\\'); + bOut.write('\''); + break; + case '"': /* Better safe than sorry */ + bOut.write('\\'); + bOut.write('"'); + break; + case '\032': /* This gives problems on Win32 */ + bOut.write('\\'); + bOut.write('Z'); + break; + default: + bOut.write(b); + } + } + + bOut.write('\''); + + setValue(parameterIndex, bOut.toByteArray()); + } + } + + @Override + public void setBytesNoEscape(int parameterIndex, byte[] parameterAsBytes) { + byte[] parameterWithQuotes = new byte[parameterAsBytes.length + 2]; + parameterWithQuotes[0] = '\''; + System.arraycopy(parameterAsBytes, 0, parameterWithQuotes, 1, parameterAsBytes.length); + parameterWithQuotes[parameterAsBytes.length + 1] = '\''; + + setValue(parameterIndex, parameterWithQuotes); + } + + @Override + public void setBytesNoEscapeNoQuotes(int parameterIndex, byte[] parameterAsBytes) { + setValue(parameterIndex, parameterAsBytes); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader) { + setCharacterStream(parameterIndex, reader, -1); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) { + try { + if (reader == null) { + setNull(parameterIndex); + } else { + char[] c = null; + int len = 0; + + boolean useLength = this.useStreamLengthsInPrepStmts.getValue(); + + String forcedEncoding = this.session.getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_clobCharacterEncoding) + .getStringValue(); + + if (useLength && (length != -1)) { + c = new char[length]; + + int numCharsRead = Util.readFully(reader, c, length); // blocks until all read + + if (forcedEncoding == null) { + setString(parameterIndex, new String(c, 0, numCharsRead)); + } else { + setBytes(parameterIndex, StringUtils.getBytes(new String(c, 0, numCharsRead), forcedEncoding)); + } + } else { + c = new char[4096]; + + StringBuilder buf = new StringBuilder(); + + while ((len = reader.read(c)) != -1) { + buf.append(c, 0, len); + } + + if (forcedEncoding == null) { + setString(parameterIndex, buf.toString()); + } else { + setBytes(parameterIndex, StringUtils.getBytes(buf.toString(), forcedEncoding)); + } + } + + this.bindValues[parameterIndex].setMysqlType(MysqlType.TEXT); // TODO was Types.CLOB + } + } catch (UnsupportedEncodingException uec) { + throw ExceptionFactory.createException(WrongArgumentException.class, uec.toString(), uec, this.session.getExceptionInterceptor()); + } catch (IOException ioEx) { + throw ExceptionFactory.createException(ioEx.toString(), ioEx, this.session.getExceptionInterceptor()); + } + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, long length) { + setCharacterStream(parameterIndex, reader, (int) length); + } + + @Override + public void setClob(int parameterIndex, Reader reader) { + setCharacterStream(parameterIndex, reader); + } + + @Override + public void setClob(int parameterIndex, Reader reader, long length) { + setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setClob(int i, Clob x) { + if (x == null) { + setNull(i); + } else { + try { + String forcedEncoding = this.session.getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_clobCharacterEncoding) + .getStringValue(); + + if (forcedEncoding == null) { + setString(i, x.getSubString(1L, (int) x.length())); + } else { + setBytes(i, StringUtils.getBytes(x.getSubString(1L, (int) x.length()), forcedEncoding)); + } + + this.bindValues[i].setMysqlType(MysqlType.TEXT); // TODO was Types.CLOB + } catch (Throwable t) { + throw ExceptionFactory.createException(t.getMessage(), t); + } + } + } + + @Override + public void setDate(int parameterIndex, Date x) { + setDate(parameterIndex, x, this.session.getServerSession().getDefaultTimeZone()); + } + + @Override + public void setDate(int parameterIndex, Date x, Calendar cal) { + setDate(parameterIndex, x, cal.getTimeZone()); + } + + @Override + public void setDate(int parameterIndex, Date x, TimeZone tz) { + if (x == null) { + setNull(parameterIndex); + } else { + if (this.ddf == null) { + this.ddf = new SimpleDateFormat("''yyyy-MM-dd''", Locale.US); + } + this.ddf.setTimeZone(tz); + setValue(parameterIndex, this.ddf.format(x)); // TODO set MysqlType? + } + } + + @Override + public void setDouble(int parameterIndex, double x) { + if (!this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_allowNanAndInf).getValue() + && (x == Double.POSITIVE_INFINITY || x == Double.NEGATIVE_INFINITY || Double.isNaN(x))) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedStatement.64", new Object[] { x }), + this.session.getExceptionInterceptor()); + } + setValue(parameterIndex, StringUtils.fixDecimalExponent(String.valueOf(x)), MysqlType.DOUBLE); + } + + @Override + public void setFloat(int parameterIndex, float x) { + setValue(parameterIndex, StringUtils.fixDecimalExponent(String.valueOf(x)), MysqlType.FLOAT); // TODO check; was Types.FLOAT but should be Types.REAL to map to SQL FLOAT + } + + @Override + public void setInt(int parameterIndex, int x) { + setValue(parameterIndex, String.valueOf(x), MysqlType.INT); + } + + @Override + public void setLong(int parameterIndex, long x) { + setValue(parameterIndex, String.valueOf(x), MysqlType.BIGINT); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value) { + setNCharacterStream(parameterIndex, value, -1); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader reader, long length) { + if (reader == null) { + setNull(parameterIndex); + } else { + try { + char[] c = null; + int len = 0; + + boolean useLength = this.useStreamLengthsInPrepStmts.getValue(); + + // Ignore "clobCharacterEncoding" because utf8 should be used this time. + + if (useLength && (length != -1)) { + c = new char[(int) length]; // can't take more than Integer.MAX_VALUE + + int numCharsRead = Util.readFully(reader, c, (int) length); // blocks until all read + setNString(parameterIndex, new String(c, 0, numCharsRead)); + + } else { + c = new char[4096]; + + StringBuilder buf = new StringBuilder(); + + while ((len = reader.read(c)) != -1) { + buf.append(c, 0, len); + } + + setNString(parameterIndex, buf.toString()); + } + + this.bindValues[parameterIndex].setMysqlType(MysqlType.TEXT); // TODO was Types.NCLOB; use length to find right TEXT type + } catch (Throwable t) { + throw ExceptionFactory.createException(t.getMessage(), t, this.session.getExceptionInterceptor()); + } + } + } + + @Override + public void setNClob(int parameterIndex, Reader reader) { + setNCharacterStream(parameterIndex, reader); + } + + @Override + public void setNClob(int parameterIndex, Reader reader, long length) { + if (reader == null) { + setNull(parameterIndex); + } else { + setNCharacterStream(parameterIndex, reader, length); + } + } + + @Override + public void setNClob(int parameterIndex, NClob value) { + if (value == null) { + setNull(parameterIndex); + } else { + try { + setNCharacterStream(parameterIndex, value.getCharacterStream(), value.length()); + } catch (Throwable t) { + throw ExceptionFactory.createException(t.getMessage(), t, this.session.getExceptionInterceptor()); + } + } + } + + @Override + public void setNString(int parameterIndex, String x) { + if (x == null) { + setNull(parameterIndex); + } else { + if (this.charEncoding.equalsIgnoreCase("UTF-8") || this.charEncoding.equalsIgnoreCase("utf8")) { + setString(parameterIndex, x); + return; + } + + int stringLength = x.length(); + // Ignore sql_mode=NO_BACKSLASH_ESCAPES in current implementation. + + // Add introducer _utf8 for NATIONAL CHARACTER + StringBuilder buf = new StringBuilder((int) (x.length() * 1.1 + 4)); + buf.append("_utf8"); + buf.append('\''); + + // + // Note: buf.append(char) is _faster_ than appending in blocks, because the block append requires a System.arraycopy().... go figure... + // + + for (int i = 0; i < stringLength; ++i) { + char c = x.charAt(i); + + switch (c) { + case 0: /* Must be escaped for 'mysql' */ + buf.append('\\'); + buf.append('0'); + break; + case '\n': /* Must be escaped for logs */ + buf.append('\\'); + buf.append('n'); + break; + case '\r': + buf.append('\\'); + buf.append('r'); + break; + case '\\': + buf.append('\\'); + buf.append('\\'); + break; + case '\'': + buf.append('\\'); + buf.append('\''); + break; + case '"': /* Better safe than sorry */ + if (this.session.getServerSession().useAnsiQuotedIdentifiers()) { + buf.append('\\'); + } + buf.append('"'); + break; + case '\032': /* This gives problems on Win32 */ + buf.append('\\'); + buf.append('Z'); + break; + default: + buf.append(c); + } + } + + buf.append('\''); + + byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(buf.toString()) : StringUtils.getBytes(buf.toString(), "UTF-8"); + + setValue(parameterIndex, parameterAsBytes, MysqlType.VARCHAR); // TODO was Types.NVARCHAR + } + } + + @Override + public synchronized void setNull(int parameterIndex) { + setValue(parameterIndex, "null"); + this.bindValues[parameterIndex].setNull(true); + } + + @Override + public void setShort(int parameterIndex, short x) { + setValue(parameterIndex, String.valueOf(x), MysqlType.SMALLINT); + } + + @Override + public void setString(int parameterIndex, String x) { + if (x == null) { + setNull(parameterIndex); + } else { + int stringLength = x.length(); + + if (this.session.getServerSession().isNoBackslashEscapesSet()) { + // Scan for any nasty chars + + boolean needsHexEscape = isEscapeNeededForString(x, stringLength); + + if (!needsHexEscape) { + StringBuilder quotedString = new StringBuilder(x.length() + 2); + quotedString.append('\''); + quotedString.append(x); + quotedString.append('\''); + + byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(quotedString.toString()) + : StringUtils.getBytes(quotedString.toString(), this.charEncoding); + setValue(parameterIndex, parameterAsBytes); + + } else { + byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(x) : StringUtils.getBytes(x, this.charEncoding); + setBytes(parameterIndex, parameterAsBytes); + } + + return; + } + + String parameterAsString = x; + boolean needsQuoted = true; + + if (this.isLoadDataQuery || isEscapeNeededForString(x, stringLength)) { + needsQuoted = false; // saves an allocation later + + StringBuilder buf = new StringBuilder((int) (x.length() * 1.1)); + + buf.append('\''); + + // + // Note: buf.append(char) is _faster_ than appending in blocks, because the block append requires a System.arraycopy().... go figure... + // + + for (int i = 0; i < stringLength; ++i) { + char c = x.charAt(i); + + switch (c) { + case 0: /* Must be escaped for 'mysql' */ + buf.append('\\'); + buf.append('0'); + break; + case '\n': /* Must be escaped for logs */ + buf.append('\\'); + buf.append('n'); + break; + case '\r': + buf.append('\\'); + buf.append('r'); + break; + case '\\': + buf.append('\\'); + buf.append('\\'); + break; + case '\'': + buf.append('\\'); + buf.append('\''); + break; + case '"': /* Better safe than sorry */ + if (this.session.getServerSession().useAnsiQuotedIdentifiers()) { + buf.append('\\'); + } + buf.append('"'); + break; + case '\032': /* This gives problems on Win32 */ + buf.append('\\'); + buf.append('Z'); + break; + case '\u00a5': + case '\u20a9': + // escape characters interpreted as backslash by mysql + if (this.charsetEncoder != null) { + CharBuffer cbuf = CharBuffer.allocate(1); + ByteBuffer bbuf = ByteBuffer.allocate(1); + cbuf.put(c); + cbuf.position(0); + this.charsetEncoder.encode(cbuf, bbuf, true); + if (bbuf.get(0) == '\\') { + buf.append('\\'); + } + } + buf.append(c); + break; + + default: + buf.append(c); + } + } + + buf.append('\''); + + parameterAsString = buf.toString(); + } + + byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(parameterAsString) + : (needsQuoted ? StringUtils.getBytesWrapped(parameterAsString, '\'', '\'', this.charEncoding) + : StringUtils.getBytes(parameterAsString, this.charEncoding)); + + setValue(parameterIndex, parameterAsBytes, MysqlType.VARCHAR); + } + } + + private boolean isEscapeNeededForString(String x, int stringLength) { + boolean needsHexEscape = false; + + for (int i = 0; i < stringLength; ++i) { + char c = x.charAt(i); + + switch (c) { + case 0: /* Must be escaped for 'mysql' */ + case '\n': /* Must be escaped for logs */ + case '\r': + case '\\': + case '\'': + case '"': /* Better safe than sorry */ + case '\032': /* This gives problems on Win32 */ + needsHexEscape = true; + break; + } + + if (needsHexEscape) { + break; // no need to scan more + } + } + return needsHexEscape; + } + + public void setTime(int parameterIndex, Time x, Calendar cal) { + setTime(parameterIndex, x, cal.getTimeZone()); + } + + public void setTime(int parameterIndex, Time x) { + setTime(parameterIndex, x, this.session.getServerSession().getDefaultTimeZone()); + } + + @Override + public void setTime(int parameterIndex, Time x, TimeZone tz) { + if (x == null) { + setNull(parameterIndex); + } else { + if (this.tdf == null) { + this.tdf = new SimpleDateFormat("''HH:mm:ss''", Locale.US); + } + this.tdf.setTimeZone(tz); + setValue(parameterIndex, this.tdf.format(x), MysqlType.TIME); + } + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x) { + setTimestamp(parameterIndex, x, this.session.getServerSession().getDefaultTimeZone()); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) { + setTimestamp(parameterIndex, x, cal.getTimeZone()); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x, TimeZone tz) { + if (x == null) { + setNull(parameterIndex); + } else { + if (!this.sendFractionalSeconds.getValue()) { + x = TimeUtil.truncateFractionalSeconds(x); + } + + if (this.tsdf == null) { + this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss", Locale.US); + } + + this.tsdf.setTimeZone(tz); + + StringBuffer buf = new StringBuffer(); + buf.append(this.tsdf.format(x)); + if (this.session.getServerSession().getCapabilities().serverSupportsFracSecs()) { + buf.append('.'); + buf.append(TimeUtil.formatNanos(x.getNanos(), true)); + } + buf.append('\''); + + setValue(parameterIndex, buf.toString(), MysqlType.TIMESTAMP); + } + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/Constants.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/Constants.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/Constants.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import com.mysql.cj.conf.PropertyDefinitions; + +/** + * Represents various constants used in the driver. + */ +public class Constants { + /** + * Avoids allocation of empty byte[] when representing 0-length strings. + */ + public final static byte[] EMPTY_BYTE_ARRAY = new byte[0]; + + /** + * I18N'd representation of the abbreviation for "ms" + */ + public final static String MILLIS_I18N = Messages.getString("Milliseconds"); + + public final static byte[] SLASH_STAR_SPACE_AS_BYTES = new byte[] { (byte) '/', (byte) '*', (byte) ' ' }; + + public final static byte[] SPACE_STAR_SLASH_SPACE_AS_BYTES = new byte[] { (byte) ' ', (byte) '*', (byte) '/', (byte) ' ' }; + + public static final String JVM_VENDOR = System.getProperty(PropertyDefinitions.SYSP_java_vendor); + public static final String JVM_VERSION = System.getProperty(PropertyDefinitions.SYSP_java_version); + + public static final String OS_NAME = System.getProperty(PropertyDefinitions.SYSP_os_name); + public static final String OS_ARCH = System.getProperty(PropertyDefinitions.SYSP_os_arch); + public static final String OS_VERSION = System.getProperty(PropertyDefinitions.SYSP_os_version); + public static final String PLATFORM_ENCODING = System.getProperty(PropertyDefinitions.SYSP_file_encoding); + + public static final String CJ_NAME = "@MYSQL_CJ_DISPLAY_PROD_NAME@"; + public static final String CJ_FULL_NAME = "@MYSQL_CJ_FULL_PROD_NAME@"; + public static final String CJ_REVISION = "@MYSQL_CJ_REVISION@"; + public static final String CJ_VERSION = "@MYSQL_CJ_VERSION@"; + public static final String CJ_MAJOR_VERSION = "@MYSQL_CJ_MAJOR_VERSION@"; + public static final String CJ_MINOR_VERSION = "@MYSQL_CJ_MINOR_VERSION@"; + public static final String CJ_LICENSE = "@MYSQL_CJ_LICENSE_TYPE@"; + + /** + * Prevents instantiation + */ + private Constants() { + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/CoreSession.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/CoreSession.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/CoreSession.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.net.SocketAddress; +import java.util.Iterator; +import java.util.Spliterators; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collector; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.conf.ModifiableProperty; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.PropertySet; +import com.mysql.cj.conf.ReadableProperty; +import com.mysql.cj.exceptions.CJOperationNotSupportedException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.log.Log; +import com.mysql.cj.log.LogFactory; +import com.mysql.cj.log.NullLogger; +import com.mysql.cj.log.ProfilerEventHandler; +import com.mysql.cj.log.ProfilerEventHandlerFactory; +import com.mysql.cj.protocol.ColumnDefinition; +import com.mysql.cj.protocol.Message; +import com.mysql.cj.protocol.Protocol; +import com.mysql.cj.protocol.ServerSession; +import com.mysql.cj.result.Row; + +public abstract class CoreSession implements Session { + + protected PropertySet propertySet; + protected ExceptionInterceptor exceptionInterceptor; + + /** The logger we're going to use */ + protected transient Log log; + + /** Null logger shared by all connections at startup */ + protected static final Log NULL_LOGGER = new NullLogger(Log.LOGGER_INSTANCE_NAME); + + protected transient Protocol protocol; + protected MessageBuilder messageBuilder; + + /** The point in time when this connection was created */ + protected long connectionCreationTimeMillis = 0; + protected HostInfo hostInfo = null; + + protected ReadableProperty gatherPerfMetrics; + protected ModifiableProperty characterEncoding; + protected ReadableProperty useOldUTF8Behavior; + protected ReadableProperty disconnectOnExpiredPasswords; + protected ReadableProperty cacheServerConfiguration; + protected ModifiableProperty autoReconnect; + protected ReadableProperty autoReconnectForPools; + protected ReadableProperty maintainTimeStats; + + /** The max-rows setting for current session */ + protected int sessionMaxRows = -1; + + /** The event sink to use for profiling */ + private ProfilerEventHandler eventSink; + + public CoreSession(HostInfo hostInfo, PropertySet propSet) { + this.connectionCreationTimeMillis = System.currentTimeMillis(); + this.hostInfo = hostInfo; + this.propertySet = propSet; + + this.gatherPerfMetrics = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_gatherPerfMetrics); + this.characterEncoding = getPropertySet().getModifiableProperty(PropertyDefinitions.PNAME_characterEncoding); + this.useOldUTF8Behavior = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useOldUTF8Behavior); + this.disconnectOnExpiredPasswords = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_disconnectOnExpiredPasswords); + this.cacheServerConfiguration = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_cacheServerConfiguration); + this.autoReconnect = getPropertySet(). getModifiableProperty(PropertyDefinitions.PNAME_autoReconnect); + this.autoReconnectForPools = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_autoReconnectForPools); + this.maintainTimeStats = getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_maintainTimeStats); + + this.log = LogFactory.getLogger(getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_logger).getStringValue(), + Log.LOGGER_INSTANCE_NAME); + if (getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_profileSQL).getValue() + || getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useUsageAdvisor).getValue()) { + ProfilerEventHandlerFactory.getInstance(this); + } + } + + /** + * Change user as given by parameters. This implementation only supports calling this during the initial handshake. + */ + public void changeUser(String user, String password, String database) { + // reset maxRows to default value + this.sessionMaxRows = -1; + + this.protocol.changeUser(user, password, database); + } + + @Override + public PropertySet getPropertySet() { + return this.propertySet; + } + + public ExceptionInterceptor getExceptionInterceptor() { + return this.exceptionInterceptor; + } + + public void setExceptionInterceptor(ExceptionInterceptor exceptionInterceptor) { + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * Returns the log mechanism that should be used to log information from/for this Session. + * + * @return the Log instance to use for logging messages. + */ + public Log getLog() { + return this.log; + } + + @SuppressWarnings("unchecked") + @Override + public MessageBuilder getMessageBuilder() { + return (MessageBuilder) this.messageBuilder; + } + + public QR sendMessage(Message message) { + this.protocol.send(message, 0); + return this.protocol.readQueryResult(); + } + + public CompletableFuture asyncSendMessage(Message message) { + return this.protocol.sendAsync(message); + } + + public RES_T query(M message, Predicate filterRow, Function mapRow, Collector collector) { + this.protocol.send(message, 0); + ColumnDefinition metadata = this.protocol.readMetadata(); + Iterator ris = this.protocol.getRowInputStream(metadata); + Stream stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(ris, 0), false); + if (filterRow != null) { + stream = stream.filter(filterRow); + } + RES_T result = stream.map(mapRow).collect(collector); + this.protocol.readQueryResult(); + return result; + } + + @Override + public ServerSession getServerSession() { + return this.protocol.getServerSession(); + } + + @Override + public boolean versionMeetsMinimum(int major, int minor, int subminor) { + return this.protocol.versionMeetsMinimum(major, minor, subminor); + } + + @Override + public long getThreadId() { + return this.protocol.getServerSession().getThreadId(); + } + + public void forceClose() { + throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported"); + // TODO: REPLACE ME WITH close() unless there's different semantics here + } + + public boolean isSetNeededForAutoCommitMode(boolean autoCommitFlag) { + throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported"); + } + + @Override + public ProfilerEventHandler getProfilerEventHandler() { + return this.eventSink; + } + + @Override + public void setProfilerEventHandler(ProfilerEventHandler h) { + this.eventSink = h; + } + + @Override + public boolean isSSLEstablished() { + throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported"); + } + + @Override + public SocketAddress getRemoteSocketAddress() { + throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported"); + } + + @Override + public void addListener(SessionEventListener l) { + throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported"); + } + + @Override + public void removeListener(SessionEventListener l) { + throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported"); + } + + @Override + public String getIdentifierQuoteString() { + throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported"); + } + + @Override + public DataStoreMetadata getDataStoreMetadata() { + return new DataStoreMetadataImpl(this); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/DataStoreMetadata.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/DataStoreMetadata.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/DataStoreMetadata.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +public interface DataStoreMetadata { + + boolean schemaExists(String schemaName); + + boolean tableExists(String schemaName, String tableName); + + long getTableRowCount(String schemaName, String tableName); + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/DataStoreMetadataImpl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/DataStoreMetadataImpl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/DataStoreMetadataImpl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +import com.mysql.cj.result.LongValueFactory; +import com.mysql.cj.xdevapi.ExprUnparser; + +public class DataStoreMetadataImpl implements DataStoreMetadata { + + private Session session; + + public DataStoreMetadataImpl(Session sess) { + this.session = sess; + } + + public boolean schemaExists(String schemaName) { + StringBuilder stmt = new StringBuilder("select count(*) from information_schema.schemata where schema_name = '"); + // TODO: verify quoting rules + stmt.append(schemaName.replaceAll("'", "\\'")); + stmt.append("'"); + + Function rowToLong = r -> r.getValue(0, new LongValueFactory()); + List counters = this.session.query(this.session.getMessageBuilder().buildSqlStatement(stmt.toString()), null, rowToLong, Collectors.toList()); + return 1 == counters.get(0); + } + + public boolean tableExists(String schemaName, String tableName) { + StringBuilder stmt = new StringBuilder("select count(*) from information_schema.tables where table_schema = '"); + // TODO: verify quoting rules + stmt.append(schemaName.replaceAll("'", "\\'")); + stmt.append("' and table_name = '"); + stmt.append(tableName.replaceAll("'", "\\'")); + stmt.append("'"); + + Function rowToLong = r -> r.getValue(0, new LongValueFactory()); + List counters = this.session.query(this.session.getMessageBuilder().buildSqlStatement(stmt.toString()), null, rowToLong, Collectors.toList()); + return 1 == counters.get(0); + } + + @Override + public long getTableRowCount(String schemaName, String tableName) { + StringBuilder stmt = new StringBuilder("select count(*) from "); + stmt.append(ExprUnparser.quoteIdentifier(schemaName)); + stmt.append("."); + stmt.append(ExprUnparser.quoteIdentifier(tableName)); + + Function rowToLong = r -> r.getValue(0, new LongValueFactory()); + List counters = this.session.query(this.session.getMessageBuilder().buildSqlStatement(stmt.toString()), null, rowToLong, Collectors.toList()); + return counters.get(0); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/LicenseConfiguration.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/LicenseConfiguration.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/LicenseConfiguration.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.util.Map; + +import com.mysql.cj.exceptions.CJException; + +/** + * Used in commercially-licensed clients that require connections to commercially-licensed servers as part of the licensing terms. + */ +public class LicenseConfiguration { + + /** + * Used in commercially-licensed clients that require connections to + * commercially-licensed servers as part of the licensing terms. + * + * @param serverVariables + * a Map of the output of 'show variables' from the server we're + * connecting to. + * + * @throws CJException + * if commercial license is required, but not found + */ + public static void checkLicenseType(Map serverVariables) { + // we don't check anything by default + } + + private LicenseConfiguration() { + // this is a static utility class + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/MessageBuilder.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/MessageBuilder.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/MessageBuilder.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.util.List; + +import com.mysql.cj.protocol.Message; + +public interface MessageBuilder { + + M buildSqlStatement(String statement); + + M buildSqlStatement(String statement, List args); + + M buildClose(); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/Messages.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/Messages.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/Messages.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Support for localized messages. + */ +public class Messages { + + private static final String BUNDLE_NAME = "com.mysql.cj.LocalizedErrorMessages"; + + private static final ResourceBundle RESOURCE_BUNDLE; + + static { + ResourceBundle temp = null; + + // + // Overly-pedantic here, some appserver and JVM combos don't deal well with the no-args version, others don't deal well with the three-arg version, so + // we need to try both :( + // + + try { + temp = ResourceBundle.getBundle(BUNDLE_NAME, Locale.getDefault(), Messages.class.getClassLoader()); + } catch (Throwable t) { + try { + temp = ResourceBundle.getBundle(BUNDLE_NAME); + } catch (Throwable t2) { + RuntimeException rt = new RuntimeException("Can't load resource bundle due to underlying exception " + t.toString()); + rt.initCause(t2); + + throw rt; + } + } finally { + RESOURCE_BUNDLE = temp; + } + } + + /** + * Returns the localized message for the given message key + * + * @param key + * the message key + * @return The localized message for the key + */ + public static String getString(String key) { + if (RESOURCE_BUNDLE == null) { + throw new RuntimeException("Localized messages from resource bundle '" + BUNDLE_NAME + "' not loaded during initialization of driver."); + } + + try { + if (key == null) { + throw new IllegalArgumentException("Message key can not be null"); + } + + String message = RESOURCE_BUNDLE.getString(key); + + if (message == null) { + message = "Missing error message for key '" + key + "'"; + } + + return message; + } catch (MissingResourceException e) { + return '!' + key + '!'; + } + } + + public static String getString(String key, Object[] args) { + return MessageFormat.format(getString(key), args); + } + + /** + * Dis-allow construction ... + */ + private Messages() { + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/MysqlConnection.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/MysqlConnection.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/MysqlConnection.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.util.Properties; + +import com.mysql.cj.conf.PropertySet; +import com.mysql.cj.exceptions.ExceptionInterceptor; + +public interface MysqlConnection { + + PropertySet getPropertySet(); + + void createNewIO(boolean isForReconnect); + + long getId(); + + /** + * Returns the parsed and passed in properties for this connection. + * + * @return {@link Properties} + */ + Properties getProperties(); + + Object getConnectionMutex(); + + Session getSession(); + + String getURL(); + + String getUser(); + + ExceptionInterceptor getExceptionInterceptor(); + + void checkClosed(); + + void normalClose(); + + void cleanup(Throwable whyCleanedUp); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/MysqlType.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/MysqlType.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/MysqlType.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,1010 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Date; +import java.sql.SQLType; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; + +import com.mysql.cj.exceptions.FeatureNotAvailableException; +import com.mysql.cj.util.StringUtils; + +public enum MysqlType implements SQLType { + + /** + * DECIMAL[(M[,D])] [UNSIGNED] [ZEROFILL] + * A packed "exact" fixed-point number. M is the total number of digits (the precision) and D is the number of digits + * after the decimal point (the scale). The decimal point and (for negative numbers) the "-" sign are not counted in M. + * If D is 0, values have no decimal point or fractional part. The maximum number of digits (M) for DECIMAL is 65. + * The maximum number of supported decimals (D) is 30. If D is omitted, the default is 0. If M is omitted, the default is 10. + * + * Protocol: FIELD_TYPE_DECIMAL = 0 + * Protocol: FIELD_TYPE_NEWDECIMAL = 246 + * + * These types are synonyms for DECIMAL: + * DEC[(M[,D])] [UNSIGNED] [ZEROFILL], + * NUMERIC[(M[,D])] [UNSIGNED] [ZEROFILL], + * FIXED[(M[,D])] [UNSIGNED] [ZEROFILL] + */ + DECIMAL("DECIMAL", Types.DECIMAL, BigDecimal.class, MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 65L, "[(M[,D])] [UNSIGNED] [ZEROFILL]"), + /** + * DECIMAL[(M[,D])] UNSIGNED [ZEROFILL] + * + * @see MysqlType#DECIMAL + */ + DECIMAL_UNSIGNED("DECIMAL UNSIGNED", Types.DECIMAL, BigDecimal.class, MysqlType.FIELD_FLAG_UNSIGNED | MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 65L, "[(M[,D])] [UNSIGNED] [ZEROFILL]"), + /** + * TINYINT[(M)] [UNSIGNED] [ZEROFILL] + * A very small integer. The signed range is -128 to 127. The unsigned range is 0 to 255. + * + * Protocol: FIELD_TYPE_TINY = 1 + */ + TINYINT("TINYINT", Types.TINYINT, Integer.class, MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 3L, "[(M)] [UNSIGNED] [ZEROFILL]"), + /** + * TINYINT[(M)] UNSIGNED [ZEROFILL] + * + * @see MysqlType#TINYINT + */ + TINYINT_UNSIGNED("TINYINT UNSIGNED", Types.TINYINT, Integer.class, MysqlType.FIELD_FLAG_UNSIGNED | MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 3L, "[(M)] [UNSIGNED] [ZEROFILL]"), + /** + * BOOL, BOOLEAN + * These types are synonyms for TINYINT(1). A value of zero is considered false. Nonzero values are considered true + * + * BOOLEAN is converted to TINYINT(1) during DDL execution i.e. it has the same precision=3. Thus we have to + * look at full data type name and convert TINYINT to BOOLEAN (or BIT) if it has "(1)" length specification. + * + * Protocol: FIELD_TYPE_TINY = 1 + */ + BOOLEAN("BOOLEAN", Types.BOOLEAN, Boolean.class, 0, MysqlType.IS_NOT_DECIMAL, 3L, ""), + /** + * SMALLINT[(M)] [UNSIGNED] [ZEROFILL] + * A small integer. The signed range is -32768 to 32767. The unsigned range is 0 to 65535. + * + * Protocol: FIELD_TYPE_SHORT = 2 + */ + SMALLINT("SMALLINT", Types.SMALLINT, Integer.class, MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 5L, "[(M)] [UNSIGNED] [ZEROFILL]"), + /** + * SMALLINT[(M)] UNSIGNED [ZEROFILL] + * + * @see MysqlType#SMALLINT + */ + SMALLINT_UNSIGNED("SMALLINT UNSIGNED", Types.SMALLINT, Integer.class, MysqlType.FIELD_FLAG_UNSIGNED | MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 5L, "[(M)] [UNSIGNED] [ZEROFILL]"), + /** + * INT[(M)] [UNSIGNED] [ZEROFILL] + * A normal-size integer. The signed range is -2147483648 to 2147483647. The unsigned range is 0 to 4294967295. + * + * Protocol: FIELD_TYPE_LONG = 3 + * + * INTEGER[(M)] [UNSIGNED] [ZEROFILL] is a synonym for INT. + */ + INT("INT", Types.INTEGER, Integer.class, MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 10L, "[(M)] [UNSIGNED] [ZEROFILL]"), + /** + * INT[(M)] UNSIGNED [ZEROFILL] + * + * @see MysqlType#INT + */ + INT_UNSIGNED("INT UNSIGNED", Types.INTEGER, Long.class, MysqlType.FIELD_FLAG_UNSIGNED | MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 10L, "[(M)] [UNSIGNED] [ZEROFILL]"), + /** + * FLOAT[(M,D)] [UNSIGNED] [ZEROFILL] + * A small (single-precision) floating-point number. Permissible values are -3.402823466E+38 to -1.175494351E-38, 0, + * and 1.175494351E-38 to 3.402823466E+38. These are the theoretical limits, based on the IEEE standard. The actual + * range might be slightly smaller depending on your hardware or operating system. + * + * M is the total number of digits and D is the number of digits following the decimal point. If M and D are omitted, + * values are stored to the limits permitted by the hardware. A single-precision floating-point number is accurate to + * approximately 7 decimal places. + * + * Protocol: FIELD_TYPE_FLOAT = 4 + * + * Additionally: + * FLOAT(p) [UNSIGNED] [ZEROFILL] + * A floating-point number. p represents the precision in bits, but MySQL uses this value only to determine whether + * to use FLOAT or DOUBLE for the resulting data type. If p is from 0 to 24, the data type becomes FLOAT with no M or D values. + * If p is from 25 to 53, the data type becomes DOUBLE with no M or D values. The range of the resulting column is the same as + * for the single-precision FLOAT or double-precision DOUBLE data types. + */ + FLOAT("FLOAT", Types.REAL, Float.class, MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 12L, "[(M,D)] [UNSIGNED] [ZEROFILL]"), + /** + * FLOAT[(M,D)] UNSIGNED [ZEROFILL] + * + * @see MysqlType#FLOAT + */ + FLOAT_UNSIGNED("FLOAT UNSIGNED", Types.REAL, Float.class, MysqlType.FIELD_FLAG_UNSIGNED | MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 12L, "[(M,D)] [UNSIGNED] [ZEROFILL]"), + /** + * DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL] + * A normal-size (double-precision) floating-point number. Permissible values are -1.7976931348623157E+308 to + * -2.2250738585072014E-308, 0, and 2.2250738585072014E-308 to 1.7976931348623157E+308. These are the theoretical limits, + * based on the IEEE standard. The actual range might be slightly smaller depending on your hardware or operating system. + * + * M is the total number of digits and D is the number of digits following the decimal point. If M and D are omitted, + * values are stored to the limits permitted by the hardware. A double-precision floating-point number is accurate to + * approximately 15 decimal places. + * + * Protocol: FIELD_TYPE_DOUBLE = 5 + * + * These types are synonyms for DOUBLE: + * DOUBLE PRECISION[(M,D)] [UNSIGNED] [ZEROFILL], + * REAL[(M,D)] [UNSIGNED] [ZEROFILL]. Exception: If the REAL_AS_FLOAT SQL mode is enabled, REAL is a synonym for FLOAT rather than DOUBLE. + */ + DOUBLE("DOUBLE", Types.DOUBLE, Double.class, MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 22L, "[(M,D)] [UNSIGNED] [ZEROFILL]"), + /** + * DOUBLE[(M,D)] UNSIGNED [ZEROFILL] + * + * @see MysqlType#DOUBLE + */ + DOUBLE_UNSIGNED("DOUBLE UNSIGNED", Types.DOUBLE, Double.class, MysqlType.FIELD_FLAG_UNSIGNED | MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 22L, "[(M,D)] [UNSIGNED] [ZEROFILL]"), + /** + * FIELD_TYPE_NULL = 6 + */ + NULL("NULL", Types.NULL, Object.class, 0, MysqlType.IS_NOT_DECIMAL, 0L, ""), + /** + * TIMESTAMP[(fsp)] + * A timestamp. The range is '1970-01-01 00:00:01.000000' UTC to '2038-01-19 03:14:07.999999' UTC. + * TIMESTAMP values are stored as the number of seconds since the epoch ('1970-01-01 00:00:00' UTC). + * A TIMESTAMP cannot represent the value '1970-01-01 00:00:00' because that is equivalent to 0 seconds + * from the epoch and the value 0 is reserved for representing '0000-00-00 00:00:00', the "zero" TIMESTAMP value. + * An optional fsp value in the range from 0 to 6 may be given to specify fractional seconds precision. A value + * of 0 signifies that there is no fractional part. If omitted, the default precision is 0. + * + * Protocol: FIELD_TYPE_TIMESTAMP = 7 + * + */ + // TODO If MySQL server run with the MAXDB SQL mode enabled, TIMESTAMP is identical with DATETIME. If this mode is enabled at the time that a table is created, TIMESTAMP columns are created as DATETIME columns. + // As a result, such columns use DATETIME display format, have the same range of values, and there is no automatic initialization or updating to the current date and time + TIMESTAMP("TIMESTAMP", Types.TIMESTAMP, Timestamp.class, 0, MysqlType.IS_NOT_DECIMAL, 26L, "[(fsp)]"), + /** + * BIGINT[(M)] [UNSIGNED] [ZEROFILL] + * A large integer. The signed range is -9223372036854775808 to 9223372036854775807. The unsigned range is 0 to 18446744073709551615. + * + * Protocol: FIELD_TYPE_LONGLONG = 8 + * + * SERIAL is an alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE. + */ + BIGINT("BIGINT", Types.BIGINT, Long.class, MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 19L, "[(M)] [UNSIGNED] [ZEROFILL]"), + /** + * BIGINT[(M)] UNSIGNED [ZEROFILL] + * + * @see MysqlType#BIGINT + */ + BIGINT_UNSIGNED("BIGINT UNSIGNED", Types.BIGINT, BigInteger.class, MysqlType.FIELD_FLAG_UNSIGNED | MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 20L, "[(M)] [UNSIGNED] [ZEROFILL]"), + /** + * MEDIUMINT[(M)] [UNSIGNED] [ZEROFILL] + * A medium-sized integer. The signed range is -8388608 to 8388607. The unsigned range is 0 to 16777215. + * + * Protocol: FIELD_TYPE_INT24 = 9 + */ + MEDIUMINT("MEDIUMINT", Types.INTEGER, Integer.class, MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 7L, "[(M)] [UNSIGNED] [ZEROFILL]"), + /** + * MEDIUMINT[(M)] UNSIGNED [ZEROFILL] + * + * @see MysqlType#MEDIUMINT + */ + MEDIUMINT_UNSIGNED("MEDIUMINT UNSIGNED", Types.INTEGER, Integer.class, MysqlType.FIELD_FLAG_UNSIGNED | MysqlType.FIELD_FLAG_ZEROFILL, MysqlType.IS_DECIMAL, 8L, "[(M)] [UNSIGNED] [ZEROFILL]"), + /** + * DATE + * A date. The supported range is '1000-01-01' to '9999-12-31'. MySQL displays DATE values in 'YYYY-MM-DD' format, + * but permits assignment of values to DATE columns using either strings or numbers. + * + * Protocol: FIELD_TYPE_DATE = 10 + */ + DATE("DATE", Types.DATE, Date.class, 0, MysqlType.IS_NOT_DECIMAL, 10L, ""), + /** + * TIME[(fsp)] + * A time. The range is '-838:59:59.000000' to '838:59:59.000000'. MySQL displays TIME values in + * 'HH:MM:SS[.fraction]' format, but permits assignment of values to TIME columns using either strings or numbers. + * An optional fsp value in the range from 0 to 6 may be given to specify fractional seconds precision. A value + * of 0 signifies that there is no fractional part. If omitted, the default precision is 0. + * + * Protocol: FIELD_TYPE_TIME = 11 + */ + TIME("TIME", Types.TIME, Time.class, 0, MysqlType.IS_NOT_DECIMAL, 16L, "[(fsp)]"), + /** + * DATETIME[(fsp)] + * A date and time combination. The supported range is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999'. + * MySQL displays DATETIME values in 'YYYY-MM-DD HH:MM:SS[.fraction]' format, but permits assignment of values to + * DATETIME columns using either strings or numbers. + * An optional fsp value in the range from 0 to 6 may be given to specify fractional seconds precision. A value + * of 0 signifies that there is no fractional part. If omitted, the default precision is 0. + * + * Protocol: FIELD_TYPE_DATETIME = 12 + */ + DATETIME("DATETIME", Types.TIMESTAMP, Timestamp.class, 0, MysqlType.IS_NOT_DECIMAL, 26L, "[(fsp)]"), + /** + * YEAR[(4)] + * A year in four-digit format. MySQL displays YEAR values in YYYY format, but permits assignment of + * values to YEAR columns using either strings or numbers. Values display as 1901 to 2155, and 0000. + * Protocol: FIELD_TYPE_YEAR = 13 + */ + YEAR("YEAR", Types.DATE, Date.class, 0, MysqlType.IS_NOT_DECIMAL, 4L, "[(4)]"), + /** + * [NATIONAL] VARCHAR(M) [CHARACTER SET charset_name] [COLLATE collation_name] + * A variable-length string. M represents the maximum column length in characters. The range of M is 0 to 65,535. + * The effective maximum length of a VARCHAR is subject to the maximum row size (65,535 bytes, which is shared among + * all columns) and the character set used. For example, utf8 characters can require up to three bytes per character, + * so a VARCHAR column that uses the utf8 character set can be declared to be a maximum of 21,844 characters. + * + * MySQL stores VARCHAR values as a 1-byte or 2-byte length prefix plus data. The length prefix indicates the number + * of bytes in the value. A VARCHAR column uses one length byte if values require no more than 255 bytes, two length + * bytes if values may require more than 255 bytes. + * + * Note + * MySQL 5.7 follows the standard SQL specification, and does not remove trailing spaces from VARCHAR values. + * + * VARCHAR is shorthand for CHARACTER VARYING. NATIONAL VARCHAR is the standard SQL way to define that a VARCHAR + * column should use some predefined character set. MySQL 4.1 and up uses utf8 as this predefined character set. + * NVARCHAR is shorthand for NATIONAL VARCHAR. + * + * Protocol: FIELD_TYPE_VARCHAR = 15 + * Protocol: FIELD_TYPE_VAR_STRING = 253 + */ + VARCHAR("VARCHAR", Types.VARCHAR, String.class, 0, MysqlType.IS_NOT_DECIMAL, 65535L, "(M) [CHARACTER SET charset_name] [COLLATE collation_name]"), + /** + * VARBINARY(M) + * The VARBINARY type is similar to the VARCHAR type, but stores binary byte strings rather than nonbinary + * character strings. M represents the maximum column length in bytes. + * + * Protocol: FIELD_TYPE_VARCHAR = 15 + * Protocol: FIELD_TYPE_VAR_STRING = 253 + */ + VARBINARY("VARBINARY", Types.VARBINARY, null, 0, MysqlType.IS_NOT_DECIMAL, 65535L, "(M)"), + /** + * BIT[(M)] + * A bit-field type. M indicates the number of bits per value, from 1 to 64. The default is 1 if M is omitted. + * Protocol: FIELD_TYPE_BIT = 16 + */ + BIT("BIT", Types.BIT, Boolean.class, 0, MysqlType.IS_DECIMAL, 1L, "[(M)]"), // TODO maybe precision=8 ? + /** + * The size of JSON documents stored in JSON columns is limited to the value of the max_allowed_packet system variable (max value 1073741824). + * (While the server manipulates a JSON value internally in memory, it can be larger; the limit applies when the server stores it.) + * + * Protocol: FIELD_TYPE_BIT = 245 + */ + JSON("JSON", Types.LONGVARCHAR, String.class, 0, MysqlType.IS_NOT_DECIMAL, 1073741824L, ""), + /** + * ENUM('value1','value2',...) [CHARACTER SET charset_name] [COLLATE collation_name] + * An enumeration. A string object that can have only one value, chosen from the list of values 'value1', + * 'value2', ..., NULL or the special '' error value. ENUM values are represented internally as integers. + * An ENUM column can have a maximum of 65,535 distinct elements. (The practical limit is less than 3000.) + * A table can have no more than 255 unique element list definitions among its ENUM and SET columns considered as a group + * + * Protocol: FIELD_TYPE_ENUM = 247 + */ + ENUM("ENUM", Types.CHAR, String.class, 0, MysqlType.IS_NOT_DECIMAL, 65535L, "('value1','value2',...) [CHARACTER SET charset_name] [COLLATE collation_name]"), + /** + * SET('value1','value2',...) [CHARACTER SET charset_name] [COLLATE collation_name] + * A set. A string object that can have zero or more values, each of which must be chosen from the list + * of values 'value1', 'value2', ... SET values are represented internally as integers. + * A SET column can have a maximum of 64 distinct members. A table can have no more than 255 unique + * element list definitions among its ENUM and SET columns considered as a group + * + * Protocol: FIELD_TYPE_SET = 248 + */ + SET("SET", Types.CHAR, String.class, 0, MysqlType.IS_NOT_DECIMAL, 64L, "('value1','value2',...) [CHARACTER SET charset_name] [COLLATE collation_name]"), + /** + * TINYBLOB + * A BLOB column with a maximum length of 255 (28 − 1) bytes. Each TINYBLOB value is stored using a + * 1-byte length prefix that indicates the number of bytes in the value. + * + * Protocol:FIELD_TYPE_TINY_BLOB = 249 + */ + TINYBLOB("TINYBLOB", Types.VARBINARY, null, 0, MysqlType.IS_NOT_DECIMAL, 255L, ""), + /** + * TINYTEXT [CHARACTER SET charset_name] [COLLATE collation_name] + * A TEXT column with a maximum length of 255 (28 − 1) characters. The effective maximum length + * is less if the value contains multibyte characters. Each TINYTEXT value is stored using + * a 1-byte length prefix that indicates the number of bytes in the value. + * + * Protocol:FIELD_TYPE_TINY_BLOB = 249 + */ + TINYTEXT("TINYTEXT", Types.VARCHAR, String.class, 0, MysqlType.IS_NOT_DECIMAL, 255L, " [CHARACTER SET charset_name] [COLLATE collation_name]"), + /** + * MEDIUMBLOB + * A BLOB column with a maximum length of 16,777,215 (224 − 1) bytes. Each MEDIUMBLOB value is stored + * using a 3-byte length prefix that indicates the number of bytes in the value. + * + * Protocol: FIELD_TYPE_MEDIUM_BLOB = 250 + */ + MEDIUMBLOB("MEDIUMBLOB", Types.LONGVARBINARY, null, 0, MysqlType.IS_NOT_DECIMAL, 16777215L, ""), + /** + * MEDIUMTEXT [CHARACTER SET charset_name] [COLLATE collation_name] + * A TEXT column with a maximum length of 16,777,215 (224 − 1) characters. The effective maximum length + * is less if the value contains multibyte characters. Each MEDIUMTEXT value is stored using a 3-byte + * length prefix that indicates the number of bytes in the value. + * + * Protocol: FIELD_TYPE_MEDIUM_BLOB = 250 + */ + MEDIUMTEXT("MEDIUMTEXT", Types.LONGVARCHAR, String.class, 0, MysqlType.IS_NOT_DECIMAL, 16777215L, " [CHARACTER SET charset_name] [COLLATE collation_name]"), + /** + * LONGBLOB + * A BLOB column with a maximum length of 4,294,967,295 or 4GB (232 − 1) bytes. The effective maximum length + * of LONGBLOB columns depends on the configured maximum packet size in the client/server protocol and available + * memory. Each LONGBLOB value is stored using a 4-byte length prefix that indicates the number of bytes in the value. + * + * Protocol: FIELD_TYPE_LONG_BLOB = 251 + */ + LONGBLOB("LONGBLOB", Types.LONGVARBINARY, null, 0, MysqlType.IS_NOT_DECIMAL, 4294967295L, ""), + /** + * LONGTEXT [CHARACTER SET charset_name] [COLLATE collation_name] + * A TEXT column with a maximum length of 4,294,967,295 or 4GB (232 − 1) characters. The effective + * maximum length is less if the value contains multibyte characters. The effective maximum length + * of LONGTEXT columns also depends on the configured maximum packet size in the client/server protocol + * and available memory. Each LONGTEXT value is stored using a 4-byte length prefix that indicates + * the number of bytes in the value. + * + * Protocol: FIELD_TYPE_LONG_BLOB = 251 + */ + LONGTEXT("LONGTEXT", Types.LONGVARCHAR, String.class, 0, MysqlType.IS_NOT_DECIMAL, 4294967295L, " [CHARACTER SET charset_name] [COLLATE collation_name]"), + /** + * BLOB[(M)] + * A BLOB column with a maximum length of 65,535 (216 − 1) bytes. Each BLOB value is stored using + * a 2-byte length prefix that indicates the number of bytes in the value. + * An optional length M can be given for this type. If this is done, MySQL creates the column as + * the smallest BLOB type large enough to hold values M bytes long. + * + * Protocol: FIELD_TYPE_BLOB = 252 + */ + BLOB("BLOB", Types.LONGVARBINARY, null, 0, MysqlType.IS_NOT_DECIMAL, 65535L, "[(M)]"), + /** + * TEXT[(M)] [CHARACTER SET charset_name] [COLLATE collation_name] + * A TEXT column with a maximum length of 65,535 (216 − 1) characters. The effective maximum length + * is less if the value contains multibyte characters. Each TEXT value is stored using a 2-byte length + * prefix that indicates the number of bytes in the value. + * An optional length M can be given for this type. If this is done, MySQL creates the column as + * the smallest TEXT type large enough to hold values M characters long. + * + * Protocol: FIELD_TYPE_BLOB = 252 + */ + TEXT("TEXT", Types.LONGVARCHAR, String.class, 0, MysqlType.IS_NOT_DECIMAL, 65535L, "[(M)] [CHARACTER SET charset_name] [COLLATE collation_name]"), + /** + * [NATIONAL] CHAR[(M)] [CHARACTER SET charset_name] [COLLATE collation_name] + * A fixed-length string that is always right-padded with spaces to the specified length when stored. + * M represents the column length in characters. The range of M is 0 to 255. If M is omitted, the length is 1. + * Note + * Trailing spaces are removed when CHAR values are retrieved unless the PAD_CHAR_TO_FULL_LENGTH SQL mode is enabled. + * CHAR is shorthand for CHARACTER. NATIONAL CHAR (or its equivalent short form, NCHAR) is the standard SQL way + * to define that a CHAR column should use some predefined character set. MySQL 4.1 and up uses utf8 + * as this predefined character set. + * + * MySQL permits you to create a column of type CHAR(0). This is useful primarily when you have to be compliant + * with old applications that depend on the existence of a column but that do not actually use its value. + * CHAR(0) is also quite nice when you need a column that can take only two values: A column that is defined + * as CHAR(0) NULL occupies only one bit and can take only the values NULL and '' (the empty string). + * + * Protocol: FIELD_TYPE_STRING = 254 + */ + CHAR("CHAR", Types.CHAR, String.class, 0, MysqlType.IS_NOT_DECIMAL, 255L, "[(M)] [CHARACTER SET charset_name] [COLLATE collation_name]"), + /** + * BINARY(M) + * The BINARY type is similar to the CHAR type, but stores binary byte strings rather than nonbinary character strings. + * M represents the column length in bytes. + * + * The CHAR BYTE data type is an alias for the BINARY data type. + * + * Protocol: no concrete type on the wire TODO: really? + */ + BINARY("BINARY", Types.BINARY, null, 0, MysqlType.IS_NOT_DECIMAL, 255L, "(M)"), + /** + * Top class for Spatial Data Types + * + * Protocol: FIELD_TYPE_GEOMETRY = 255 + */ + GEOMETRY("GEOMETRY", Types.BINARY, null, 0, MysqlType.IS_NOT_DECIMAL, 65535L, ""), // TODO check precision, it isn't well documented, only mentioned that WKB format is represented by BLOB + /** + * Fall-back type for those MySQL data types which c/J can't recognize. + * Handled the same as BLOB. + * + * Has no protocol ID. + */ + UNKNOWN("UNKNOWN", Types.OTHER, null, 0, MysqlType.IS_NOT_DECIMAL, 65535L, ""); + + /** + * Get MysqlType matching the full MySQL type name, for example "DECIMAL(5,3) UNSIGNED ZEROFILL". + * Distinct *_UNSIGNED type will be returned if "UNSIGNED" is present in fullMysqlTypeName. + * + * @param fullMysqlTypeName + * full MySQL type name + * @return MysqlType + */ + public static MysqlType getByName(String fullMysqlTypeName) { + + // TODO parse complex names like [NATIONAL] VARCHAR(M) [CHARACTER SET charset_name] [COLLATE collation_name] + + String typeName = ""; + + if (fullMysqlTypeName.indexOf("(") != -1) { + typeName = fullMysqlTypeName.substring(0, fullMysqlTypeName.indexOf("(")).trim(); + } else { + typeName = fullMysqlTypeName; + } + + // the order of checks is important because some short names could match parts of longer names + if (StringUtils.indexOfIgnoreCase(typeName, "DECIMAL") != -1 || StringUtils.indexOfIgnoreCase(typeName, "DEC") != -1 + || StringUtils.indexOfIgnoreCase(typeName, "NUMERIC") != -1 || StringUtils.indexOfIgnoreCase(typeName, "FIXED") != -1) { + return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? DECIMAL_UNSIGNED : DECIMAL; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "TINYBLOB") != -1) { + // IMPORTANT: "TINYBLOB" must be checked before "TINY" + return TINYBLOB; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "TINYTEXT") != -1) { + // IMPORTANT: "TINYTEXT" must be checked before "TINY" + return TINYTEXT; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "TINYINT") != -1 || StringUtils.indexOfIgnoreCase(typeName, "TINY") != -1 + || StringUtils.indexOfIgnoreCase(typeName, "INT1") != -1) { + + // TODO BOOLEAN is a synonym for TINYINT(1) + return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? TINYINT_UNSIGNED : TINYINT; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "MEDIUMINT") != -1 + // IMPORTANT: "INT24" must be checked before "INT2" + || StringUtils.indexOfIgnoreCase(typeName, "INT24") != -1 || StringUtils.indexOfIgnoreCase(typeName, "INT3") != -1 + || StringUtils.indexOfIgnoreCase(typeName, "MIDDLEINT") != -1) { + return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? MEDIUMINT_UNSIGNED : MEDIUMINT; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "SMALLINT") != -1 || StringUtils.indexOfIgnoreCase(typeName, "INT2") != -1) { + return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? SMALLINT_UNSIGNED : SMALLINT; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "BIGINT") != -1 || StringUtils.indexOfIgnoreCase(typeName, "SERIAL") != -1 + || StringUtils.indexOfIgnoreCase(typeName, "INT8") != -1) { + // SERIAL is an alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE. + return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? BIGINT_UNSIGNED : BIGINT; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "POINT") != -1) { + // also covers "MULTIPOINT" + // IMPORTANT: "POINT" must be checked before "INT" + return GEOMETRY; // TODO think about different MysqlTypes for Spatial Data Types + + } else if (StringUtils.indexOfIgnoreCase(typeName, "INT") != -1 || StringUtils.indexOfIgnoreCase(typeName, "INTEGER") != -1 + || StringUtils.indexOfIgnoreCase(typeName, "INT4") != -1) { + // IMPORTANT: "INT" must be checked after all "*INT*" types + return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? INT_UNSIGNED : INT; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "DOUBLE") != -1 || StringUtils.indexOfIgnoreCase(typeName, "REAL") != -1 + /* || StringUtils.indexOfIgnoreCase(name, "DOUBLE PRECISION") != -1 is caught by "DOUBLE" check */ + // IMPORTANT: "FLOAT8" must be checked before "FLOAT" + || StringUtils.indexOfIgnoreCase(typeName, "FLOAT8") != -1) { + // TODO Exception: If the REAL_AS_FLOAT SQL mode is enabled, REAL is a synonym for FLOAT rather than DOUBLE. + return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? DOUBLE_UNSIGNED : DOUBLE; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "FLOAT") != -1 /* + * || StringUtils.indexOfIgnoreCase(name, "FLOAT4") != -1 is caught by + * "FLOAT" check + */) { + // TODO FLOAT(p) [UNSIGNED] [ZEROFILL]. If p is from 0 to 24, the data type becomes FLOAT with no M or D values. If p is from 25 to 53, the data type becomes DOUBLE with no M or D values. + return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? FLOAT_UNSIGNED : FLOAT; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "NULL") != -1) { + return NULL; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "TIMESTAMP") != -1) { + // IMPORTANT: "TIMESTAMP" must be checked before "TIME" + return TIMESTAMP; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "DATETIME") != -1) { + // IMPORTANT: "DATETIME" must be checked before "DATE" and "TIME" + return DATETIME; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "DATE") != -1) { + return DATE; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "TIME") != -1) { + return TIME; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "YEAR") != -1) { + return YEAR; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "LONGBLOB") != -1) { + // IMPORTANT: "LONGBLOB" must be checked before "LONG" and "BLOB" + return LONGBLOB; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "LONGTEXT") != -1) { + // IMPORTANT: "LONGTEXT" must be checked before "LONG" and "TEXT" + return LONGTEXT; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "MEDIUMBLOB") != -1 || StringUtils.indexOfIgnoreCase(typeName, "LONG VARBINARY") != -1) { + // IMPORTANT: "MEDIUMBLOB" must be checked before "BLOB" + // IMPORTANT: "LONG VARBINARY" must be checked before "LONG" and "VARBINARY" + return MEDIUMBLOB; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "MEDIUMTEXT") != -1 || StringUtils.indexOfIgnoreCase(typeName, "LONG VARCHAR") != -1 + || StringUtils.indexOfIgnoreCase(typeName, "LONG") != -1) { + // IMPORTANT: "MEDIUMTEXT" must be checked before "TEXT" + // IMPORTANT: "LONG VARCHAR" must be checked before "VARCHAR" + return MEDIUMTEXT; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "VARCHAR") != -1 || StringUtils.indexOfIgnoreCase(typeName, "NVARCHAR") != -1 + || StringUtils.indexOfIgnoreCase(typeName, "NATIONAL VARCHAR") != -1 || StringUtils.indexOfIgnoreCase(typeName, "CHARACTER VARYING") != -1) { + // IMPORTANT: "CHARACTER VARYING" must be checked before "CHARACTER" and "CHAR" + return VARCHAR; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "VARBINARY") != -1) { + return VARBINARY; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "BINARY") != -1 || StringUtils.indexOfIgnoreCase(typeName, "CHAR BYTE") != -1) { + // IMPORTANT: "BINARY" must be checked after all "*BINARY" types + // IMPORTANT: "CHAR BYTE" must be checked before "CHAR" + return BINARY; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "LINESTRING") != -1) { + // also covers "MULTILINESTRING" + // IMPORTANT: "LINESTRING" must be checked before "STRING" + return GEOMETRY; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "STRING") != -1 + // IMPORTANT: "CHAR" must be checked after all "*CHAR*" types + || StringUtils.indexOfIgnoreCase(typeName, "CHAR") != -1 || StringUtils.indexOfIgnoreCase(typeName, "NCHAR") != -1 + || StringUtils.indexOfIgnoreCase(typeName, "NATIONAL CHAR") != -1 || StringUtils.indexOfIgnoreCase(typeName, "CHARACTER") != -1) { + return CHAR; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "BOOLEAN") != -1 || StringUtils.indexOfIgnoreCase(typeName, "BOOL") != -1) { + return BOOLEAN; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "BIT") != -1) { + return BIT; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "JSON") != -1) { + return JSON; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "ENUM") != -1) { + return ENUM; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "SET") != -1) { + return SET; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "BLOB") != -1) { + return BLOB; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "TEXT") != -1) { + return TEXT; + + } else if (StringUtils.indexOfIgnoreCase(typeName, "GEOM") != -1 // covers "GEOMETRY", "GEOMETRYCOLLECTION" and "GEOMCOLLECTION" + || StringUtils.indexOfIgnoreCase(typeName, "POINT") != -1 // also covers "MULTIPOINT" + || StringUtils.indexOfIgnoreCase(typeName, "POLYGON") != -1 // also covers "MULTIPOLYGON" + ) { + return GEOMETRY; // TODO think about different MysqlTypes for Spatial Data Types + + } + + return UNKNOWN; + } + + public static MysqlType getByJdbcType(int jdbcType) { + switch (jdbcType) { + case Types.BIGINT: + return BIGINT; + case Types.BINARY: + return BINARY; + case Types.BIT: + return BIT; + case Types.BOOLEAN: + return BOOLEAN; + case Types.CHAR: + case Types.NCHAR: // TODO check that it's correct + return CHAR; + case Types.DATE: + return DATE; + case Types.DECIMAL: + case Types.NUMERIC: + return DECIMAL; + case Types.DOUBLE: + case Types.FLOAT: + return DOUBLE; + case Types.INTEGER: + return INT; + case Types.LONGVARBINARY: + case Types.BLOB: // TODO check that it's correct + case Types.JAVA_OBJECT: // TODO check that it's correct + return BLOB; + case Types.LONGVARCHAR: + case Types.LONGNVARCHAR: // TODO check that it's correct + case Types.CLOB: // TODO check that it's correct + case Types.NCLOB: // TODO check that it's correct + return TEXT; + case Types.NULL: + return NULL; + case Types.REAL: + return FLOAT; + case Types.SMALLINT: + return SMALLINT; + case Types.TIME: + return TIME; + case Types.TIMESTAMP: + return TIMESTAMP; + case Types.TINYINT: + return TINYINT; + case Types.VARBINARY: + return VARBINARY; + case Types.VARCHAR: + case Types.NVARCHAR: // TODO check that it's correct + case Types.DATALINK: // TODO check that it's correct + case Types.SQLXML: // TODO check that it's correct + return VARCHAR; + + case Types.REF_CURSOR: + case Types.TIME_WITH_TIMEZONE: + case Types.TIMESTAMP_WITH_TIMEZONE: + throw new FeatureNotAvailableException("Document IDs are not assigned for SQL statements"); + + // TODO check next types + case Types.ARRAY: + case Types.DISTINCT: + case Types.OTHER: + case Types.REF: + case Types.ROWID: + case Types.STRUCT: + + default: + return UNKNOWN; + } + } + + /** + * Is CONVERT between the given SQL types supported? + * + * @param fromType + * the type to convert from + * @param toType + * the type to convert to + * @return true if so + * @see Types + */ + public static boolean supportsConvert(int fromType, int toType) { + + // TODO use MysqlTypes here ? + + switch (fromType) { + /* + * The char/binary types can be converted to pretty much anything. + */ + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + + switch (toType) { + case java.sql.Types.DECIMAL: + case java.sql.Types.NUMERIC: + case java.sql.Types.REAL: + case java.sql.Types.TINYINT: + case java.sql.Types.SMALLINT: + case java.sql.Types.INTEGER: + case java.sql.Types.BIGINT: + case java.sql.Types.FLOAT: + case java.sql.Types.DOUBLE: + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + case java.sql.Types.OTHER: + case java.sql.Types.DATE: + case java.sql.Types.TIME: + case java.sql.Types.TIMESTAMP: + return true; + + default: + return false; + } + + /* + * We don't handle the BIT type yet. + */ + case java.sql.Types.BIT: + return false; + + /* + * The numeric types. Basically they can convert among themselves, and with char/binary types. + */ + case java.sql.Types.DECIMAL: + case java.sql.Types.NUMERIC: + case java.sql.Types.REAL: + case java.sql.Types.TINYINT: + case java.sql.Types.SMALLINT: + case java.sql.Types.INTEGER: + case java.sql.Types.BIGINT: + case java.sql.Types.FLOAT: + case java.sql.Types.DOUBLE: + + switch (toType) { + case java.sql.Types.DECIMAL: + case java.sql.Types.NUMERIC: + case java.sql.Types.REAL: + case java.sql.Types.TINYINT: + case java.sql.Types.SMALLINT: + case java.sql.Types.INTEGER: + case java.sql.Types.BIGINT: + case java.sql.Types.FLOAT: + case java.sql.Types.DOUBLE: + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + return true; + + default: + return false; + } + + /* MySQL doesn't support a NULL type. */ + case java.sql.Types.NULL: + return false; + + /* + * With this driver, this will always be a serialized object, so the char/binary types will work. + */ + case java.sql.Types.OTHER: + + switch (toType) { + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + return true; + + default: + return false; + } + + /* Dates can be converted to char/binary types. */ + case java.sql.Types.DATE: + + switch (toType) { + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + return true; + + default: + return false; + } + + /* Time can be converted to char/binary types */ + case java.sql.Types.TIME: + + switch (toType) { + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + return true; + + default: + return false; + } + + /* + * Timestamp can be converted to char/binary types and date/time types (with loss of precision). + */ + case java.sql.Types.TIMESTAMP: + + switch (toType) { + case java.sql.Types.CHAR: + case java.sql.Types.VARCHAR: + case java.sql.Types.LONGVARCHAR: + case java.sql.Types.BINARY: + case java.sql.Types.VARBINARY: + case java.sql.Types.LONGVARBINARY: + case java.sql.Types.TIME: + case java.sql.Types.DATE: + return true; + + default: + return false; + } + + /* We shouldn't get here! */ + default: + return false; // not sure + } + } + + public static boolean isSigned(MysqlType type) { + switch (type) { + case DECIMAL: + case TINYINT: + case SMALLINT: + case INT: + case BIGINT: + case MEDIUMINT: + case FLOAT: + case DOUBLE: + return true; + default: + return false; + } + } + + private final String name; + protected int jdbcType; + protected final Class javaClass; + private final int flagsMask; + private final boolean isDecimal; + private final Long precision; + private final String createParams; + + /** + * + * @param mysqlTypeName + * mysqlTypeName + * @param jdbcType + * jdbcType + * @param javaClass + * javaClass + * @param allowedFlags + * allowedFlags + * @param isDec + * isDec + * @param precision + * represents the maximum column size that the server supports for the given datatype. + *
    + *
  • For numeric data, this is the maximum precision. + *
  • + * For character data, this is the length in characters. + *
  • For datetime datatypes, this is the length in characters of the String + * representation (assuming the maximum allowed precision of the fractional seconds component). + *
  • For binary data, this is the length in bytes. + *
  • For the ROWID datatype, this is the length in bytes. + *
  • Null is returned for data types where the column size is not applicable. + *
+ * @param createParams + * params + */ + private MysqlType(String mysqlTypeName, int jdbcType, Class javaClass, int allowedFlags, boolean isDec, Long precision, String createParams) { + this.name = mysqlTypeName; + this.jdbcType = jdbcType; + this.javaClass = javaClass; + this.flagsMask = allowedFlags; + this.isDecimal = isDec; + this.precision = precision; + this.createParams = createParams; + } + + public String getName() { + return this.name; + } + + public int getJdbcType() { + return this.jdbcType; + } + + public boolean isAllowed(int flag) { + return ((this.flagsMask & flag) > 0); + } + + public String getClassName() { + if (this.javaClass == null) { + return "[B"; + } + return this.javaClass.getName(); + } + + /** + * Checks if the MySQL Type is a Decimal/Number Type + * + * @return true if the MySQL Type is a Decimal/Number Type + */ + public boolean isDecimal() { + return this.isDecimal; + } + + /** + * The PRECISION column represents the maximum column size that the server supports for the given datatype. + *
    + *
  • For numeric data, this is the maximum + * precision. + *
  • For character data, this is the length in characters. + *
  • For datetime datatypes, this is the length in characters of the String + * representation (assuming the maximum allowed precision of the fractional seconds component). + *
  • For binary data, this is the length in bytes. + *
  • For + * the ROWID datatype, this is the length in bytes. + *
  • Null is returned for data types where the column size is not applicable. + *
+ * + * @return precision + */ + public Long getPrecision() { + return this.precision; + } + + public String getCreateParams() { + return this.createParams; + } + + public static final int FIELD_FLAG_NOT_NULL = 1; + public static final int FIELD_FLAG_PRIMARY_KEY = 2; + public static final int FIELD_FLAG_UNIQUE_KEY = 4; + public static final int FIELD_FLAG_MULTIPLE_KEY = 8; + public static final int FIELD_FLAG_BLOB = 16; + public static final int FIELD_FLAG_UNSIGNED = 32; + public static final int FIELD_FLAG_ZEROFILL = 64; + public static final int FIELD_FLAG_BINARY = 128; + public static final int FIELD_FLAG_AUTO_INCREMENT = 512; + + private static final boolean IS_DECIMAL = true; + private static final boolean IS_NOT_DECIMAL = false; + + @Override + public String getVendor() { + return "com.mysql.cj"; + } + + @Override + public Integer getVendorTypeNumber() { + return this.jdbcType; + } + + // Protocol field type numbers + public static final int FIELD_TYPE_DECIMAL = 0; + public static final int FIELD_TYPE_TINY = 1; + public static final int FIELD_TYPE_SHORT = 2; + public static final int FIELD_TYPE_LONG = 3; + public static final int FIELD_TYPE_FLOAT = 4; + public static final int FIELD_TYPE_DOUBLE = 5; + public static final int FIELD_TYPE_NULL = 6; + public static final int FIELD_TYPE_TIMESTAMP = 7; + public static final int FIELD_TYPE_LONGLONG = 8; + public static final int FIELD_TYPE_INT24 = 9; + public static final int FIELD_TYPE_DATE = 10; + public static final int FIELD_TYPE_TIME = 11; + public static final int FIELD_TYPE_DATETIME = 12; + public static final int FIELD_TYPE_YEAR = 13; + public static final int FIELD_TYPE_VARCHAR = 15; + public static final int FIELD_TYPE_BIT = 16; + public static final int FIELD_TYPE_JSON = 245; + public static final int FIELD_TYPE_NEWDECIMAL = 246; + public static final int FIELD_TYPE_ENUM = 247; + public static final int FIELD_TYPE_SET = 248; + public static final int FIELD_TYPE_TINY_BLOB = 249; + public static final int FIELD_TYPE_MEDIUM_BLOB = 250; + public static final int FIELD_TYPE_LONG_BLOB = 251; + public static final int FIELD_TYPE_BLOB = 252; + public static final int FIELD_TYPE_VAR_STRING = 253; + public static final int FIELD_TYPE_STRING = 254; + public static final int FIELD_TYPE_GEOMETRY = 255; + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/MysqlxSession.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/MysqlxSession.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/MysqlxSession.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.conf.PropertySet; +import com.mysql.cj.exceptions.CJCommunicationsException; +import com.mysql.cj.protocol.ColumnDefinition; +import com.mysql.cj.protocol.ResultListener; +import com.mysql.cj.protocol.ResultStreamer; +import com.mysql.cj.protocol.x.ResultCreatingResultListener; +import com.mysql.cj.protocol.x.StatementExecuteOk; +import com.mysql.cj.protocol.x.StatementExecuteOkBuilder; +import com.mysql.cj.protocol.x.XMessageBuilder; +import com.mysql.cj.protocol.x.XProtocol; +import com.mysql.cj.result.RowList; +import com.mysql.cj.util.StringUtils; +import com.mysql.cj.xdevapi.FindParams; +import com.mysql.cj.xdevapi.SqlDataResult; +import com.mysql.cj.xdevapi.SqlResult; +import com.mysql.cj.xdevapi.SqlResultImpl; +import com.mysql.cj.xdevapi.SqlUpdateResult; + +public class MysqlxSession extends CoreSession { + + private String host; + private int port; + + public MysqlxSession(HostInfo hostInfo, PropertySet propSet) { + super(hostInfo, propSet); + + // create protocol instance + this.host = hostInfo.getHost(); + if (this.host == null || StringUtils.isEmptyOrWhitespaceOnly(this.host)) { + this.host = "localhost"; + } + this.port = hostInfo.getPort(); + if (this.port < 0) { + this.port = 33060; + } + + this.protocol = XProtocol.getInstance(this.host, this.port, propSet); + + this.messageBuilder = this.protocol.getMessageBuilder(); + + this.protocol.connect(hostInfo.getUser(), hostInfo.getPassword(), hostInfo.getDatabase()); + } + + @Override + public String getProcessHost() { + return this.host; + } + + public int getPort() { + return this.port; + } + + public void quit() { + try { + this.protocol.send(this.messageBuilder.buildClose(), 0); + ((XProtocol) this.protocol).readOk(); + } finally { + try { + this.protocol.close(); + } catch (IOException ex) { + throw new CJCommunicationsException(ex); + } + } + } + + public T find(FindParams findParams, + Function, T>> resultCtor) { + this.protocol.send(((XMessageBuilder) this.messageBuilder).buildFind(findParams), 0); + ColumnDefinition metadata = this.protocol.readMetadata(); + T res = resultCtor.apply(metadata).apply(((XProtocol) this.protocol).getRowInputStream(metadata), this.protocol::readQueryResult); + this.protocol.setCurrentResultStreamer(res); + return res; + } + + public T find(FindParams findParams, BiFunction, T> resultCtor) { + this.protocol.send(((XMessageBuilder) this.messageBuilder).buildFind(findParams), 0); + ColumnDefinition metadata = this.protocol.readMetadata(); + T res = resultCtor.apply(((XProtocol) this.protocol).getRowInputStream(metadata), this.protocol::readQueryResult); + this.protocol.setCurrentResultStreamer(res); + return res; + } + + public CompletableFuture asyncFind(FindParams findParams, + Function, RES_T>> resultCtor) { + CompletableFuture f = new CompletableFuture<>(); + ResultListener l = new ResultCreatingResultListener<>(resultCtor, f); + ((XProtocol) this.protocol).asyncFind(findParams, l, f); + return f; + } + + public SqlResult executeSql(String sql, List args) { + this.protocol.send(this.messageBuilder.buildSqlStatement(sql, args), 0); + boolean readLastResult[] = new boolean[1]; + Supplier okReader = () -> { + if (readLastResult[0]) { + throw new CJCommunicationsException("Invalid state attempting to read ok packet"); + } + if (((XProtocol) this.protocol).hasMoreResults()) { + // empty/fabricated OK packet + return new StatementExecuteOkBuilder().build(); + } + readLastResult[0] = true; + return this.protocol.readQueryResult(); + }; + Supplier resultStream = () -> { + if (readLastResult[0]) { + return null; + } else if (((XProtocol) this.protocol).isSqlResultPending()) { + ColumnDefinition metadata = this.protocol.readMetadata(); + return new SqlDataResult(metadata, this.protocol.getServerSession().getDefaultTimeZone(), this.protocol.getRowInputStream(metadata), okReader); + } else { + readLastResult[0] = true; + return new SqlUpdateResult(this.protocol.readQueryResult()); + } + }; + SqlResultImpl res = new SqlResultImpl(resultStream); + this.protocol.setCurrentResultStreamer(res); + return res; + } + + public CompletableFuture asyncExecuteSql(String sql, List args) { + return ((XProtocol) this.protocol).asyncExecuteSql(sql, args); + } + + public boolean isClosed() { + return !((XProtocol) this.protocol).isOpen(); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/NativeSession.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/NativeSession.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/NativeSession.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,1349 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.lang.ref.WeakReference; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Timer; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collector; + +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.conf.ModifiableProperty; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.PropertySet; +import com.mysql.cj.exceptions.CJCommunicationsException; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.CJOperationNotSupportedException; +import com.mysql.cj.exceptions.ConnectionIsClosedException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.ExceptionInterceptorChain; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.exceptions.OperationCancelledException; +import com.mysql.cj.exceptions.PasswordExpiredException; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.interceptors.QueryInterceptor; +import com.mysql.cj.log.Log; +import com.mysql.cj.log.ProfilerEventHandler; +import com.mysql.cj.log.ProfilerEventHandlerFactory; +import com.mysql.cj.protocol.ColumnDefinition; +import com.mysql.cj.protocol.Message; +import com.mysql.cj.protocol.NetworkResources; +import com.mysql.cj.protocol.ProtocolEntityFactory; +import com.mysql.cj.protocol.Resultset; +import com.mysql.cj.protocol.Resultset.Type; +import com.mysql.cj.protocol.ServerSession; +import com.mysql.cj.protocol.SocketConnection; +import com.mysql.cj.protocol.SocketFactory; +import com.mysql.cj.protocol.a.NativeMessageBuilder; +import com.mysql.cj.protocol.a.NativePacketPayload; +import com.mysql.cj.protocol.a.NativeProtocol; +import com.mysql.cj.protocol.a.NativeServerSession; +import com.mysql.cj.protocol.a.NativeSocketConnection; +import com.mysql.cj.protocol.a.ResultsetFactory; +import com.mysql.cj.result.Field; +import com.mysql.cj.result.IntegerValueFactory; +import com.mysql.cj.result.LongValueFactory; +import com.mysql.cj.result.Row; +import com.mysql.cj.result.StringValueFactory; +import com.mysql.cj.result.ValueFactory; +import com.mysql.cj.util.StringUtils; + +public class NativeSession extends CoreSession implements Serializable { + + private static final long serialVersionUID = 5323638898749073419L; + + private CacheAdapter> serverConfigCache; + + /** + * Actual collation index to mysql charset name map of user defined charsets for given server URLs. + */ + private static final Map> customIndexToCharsetMapByUrl = new HashMap<>(); + + /** + * Actual mysql charset name to mblen map of user defined charsets for given server URLs. + */ + private static final Map> customCharsetToMblenMapByUrl = new HashMap<>(); + + /** + * If a CharsetEncoder is required for escaping. Needed for SJIS and related + * problems with \u00A5. + */ + private boolean requiresEscapingEncoder; + + /** When did the last query finish? */ + private long lastQueryFinishedTime = 0; + + /** Does this connection need to be tested? */ + private boolean needsPing = false; + + private NativeMessageBuilder commandBuilder = new NativeMessageBuilder(); // TODO use shared builder + + /** Has this session been closed? */ + private boolean isClosed = true; + + /** Why was this session implicitly closed, if known? (for diagnostics) */ + private Throwable forceClosedReason; + + private CopyOnWriteArrayList> listeners = new CopyOnWriteArrayList<>(); + + private transient Timer cancelTimer; + + public NativeSession(HostInfo hostInfo, PropertySet propSet) { + super(hostInfo, propSet); + } + + public void connect(HostInfo hi, String user, String password, String database, int loginTimeout, TransactionEventHandler transactionManager) + throws IOException { + + this.hostInfo = hi; + + // reset max-rows to default value + this.setSessionMaxRows(-1); + + // TODO do we need different types of physical connections? + SocketConnection socketConnection = new NativeSocketConnection(); + socketConnection.connect(this.hostInfo.getHost(), this.hostInfo.getPort(), this.propertySet, getExceptionInterceptor(), this.log, loginTimeout); + + // we use physical connection to create a -> protocol + // this configuration places no knowledge of protocol or session on physical connection. + // physical connection is responsible *only* for I/O streams + if (this.protocol == null) { + this.protocol = NativeProtocol.getInstance(this, socketConnection, this.propertySet, this.log, transactionManager); + } else { + this.protocol.init(this, socketConnection, this.propertySet, transactionManager); + } + + // use protocol to create a -> session + // protocol is responsible for building a session and authenticating (using AuthenticationProvider) internally + this.protocol.connect(user, password, database); + + // error messages are returned according to character_set_results which, at this point, is set from the response packet + this.protocol.getServerSession().setErrorMessageEncoding(this.protocol.getAuthenticationProvider().getEncodingForHandshake()); + + this.isClosed = false; + } + + // TODO: this method should not be used in user-level APIs + public NativeProtocol getProtocol() { + return (NativeProtocol) this.protocol; + } + + @Override + public void quit() { + if (this.protocol != null) { + try { + ((NativeProtocol) this.protocol).quit(); + } catch (Exception e) { + } + + } + synchronized (this) { + if (this.cancelTimer != null) { + this.cancelTimer.cancel(); + this.cancelTimer = null; + } + } + this.isClosed = true; + } + + // TODO: we should examine the call flow here, we shouldn't have to know about the socket connection but this should be address in a wider scope. + @Override + public void forceClose() { + if (this.protocol != null) { + // checking this.protocol != null isn't enough if connection is used concurrently (the usual situation + // with application servers which have additional thread management), this.protocol can become null + // at any moment after this check, causing a race condition and NPEs on next calls; + // but we may ignore them because at this stage null this.protocol means that we successfully closed all resources by other thread. + try { + this.protocol.getSocketConnection().forceClose(); + ((NativeProtocol) this.protocol).releaseResources(); + } catch (Throwable t) { + // can't do anything about it, and we're forcibly aborting + } + //this.protocol = null; // TODO actually we shouldn't remove protocol instance because some it's methods can be called after closing socket + } + synchronized (this) { + if (this.cancelTimer != null) { + this.cancelTimer.cancel(); + this.cancelTimer = null; + } + } + this.isClosed = true; + } + + public void enableMultiQueries() { + sendCommand(this.commandBuilder.buildComSetOption(((NativeProtocol) this.protocol).getSharedSendPacket(), 0), false, 0); + } + + public void disableMultiQueries() { + sendCommand(this.commandBuilder.buildComSetOption(((NativeProtocol) this.protocol).getSharedSendPacket(), 1), false, 0); + } + + @Override + public boolean isSetNeededForAutoCommitMode(boolean autoCommitFlag) { + // Server Bug#66884 (SERVER_STATUS is always initiated with SERVER_STATUS_AUTOCOMMIT=1) invalidates "elideSetAutoCommits" feature. + // TODO Turn this feature back on as soon as the server bug is fixed. Consider making it version specific. + //return this.protocol.getServerSession().isSetNeededForAutoCommitMode(autoCommitFlag, + // getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_elideSetAutoCommits).getValue()); + return ((NativeServerSession) this.protocol.getServerSession()).isSetNeededForAutoCommitMode(autoCommitFlag, false); + } + + public int getSessionMaxRows() { + return this.sessionMaxRows; + } + + public void setSessionMaxRows(int sessionMaxRows) { + this.sessionMaxRows = sessionMaxRows; + } + + public HostInfo getHostInfo() { + return this.hostInfo; + } + + public void setQueryInterceptors(List queryInterceptors) { + ((NativeProtocol) this.protocol).setQueryInterceptors(queryInterceptors); + } + + public boolean isServerLocal(Session sess) { + SocketFactory factory = this.protocol.getSocketConnection().getSocketFactory(); + return factory.isLocallyConnected(sess); + } + + /** + * Used by MiniAdmin to shutdown a MySQL server + * + */ + public void shutdownServer() { + if (versionMeetsMinimum(5, 7, 9)) { + sendCommand(this.commandBuilder.buildComQuery(getSharedSendPacket(), "SHUTDOWN"), false, 0); + } else { + sendCommand(this.commandBuilder.buildComShutdown(getSharedSendPacket()), false, 0); + } + } + + public void setSocketTimeout(int milliseconds) { + getPropertySet().getModifiableProperty(PropertyDefinitions.PNAME_socketTimeout).setValue(milliseconds); // for re-connects + ((NativeProtocol) this.protocol).setSocketTimeout(milliseconds); + } + + public int getSocketTimeout() { + ModifiableProperty sto = getPropertySet().getModifiableProperty(PropertyDefinitions.PNAME_socketTimeout); + return sto.getValue(); + } + + /** + * Determines if the database charset is the same as the platform charset + */ + public void checkForCharsetMismatch() { + ((NativeProtocol) this.protocol).checkForCharsetMismatch(); + } + + /** + * 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() { + return ((NativeProtocol) this.protocol).getSharedSendPacket(); + } + + public void dumpPacketRingBuffer() { + ((NativeProtocol) this.protocol).dumpPacketRingBuffer(); + } + + public T invokeQueryInterceptorsPre(Supplier sql, Query interceptedQuery, boolean forceExecute) { + return ((NativeProtocol) this.protocol).invokeQueryInterceptorsPre(sql, interceptedQuery, forceExecute); + } + + public T invokeQueryInterceptorsPost(Supplier sql, Query interceptedQuery, T originalResultSet, boolean forceExecute) { + return ((NativeProtocol) this.protocol).invokeQueryInterceptorsPost(sql, interceptedQuery, originalResultSet, forceExecute); + } + + public boolean shouldIntercept() { + return ((NativeProtocol) this.protocol).getQueryInterceptors() != null; + } + + public long getCurrentTimeNanosOrMillis() { + return ((NativeProtocol) this.protocol).getCurrentTimeNanosOrMillis(); + } + + public final NativePacketPayload sendCommand(NativePacketPayload queryPacket, boolean skipCheck, int timeoutMillis) { + return (NativePacketPayload) this.protocol.sendCommand(queryPacket, skipCheck, timeoutMillis); + } + + public long getSlowQueryThreshold() { + return ((NativeProtocol) this.protocol).getSlowQueryThreshold(); + } + + public String getQueryTimingUnits() { + return ((NativeProtocol) this.protocol).getQueryTimingUnits(); + } + + public boolean hadWarnings() { + return ((NativeProtocol) this.protocol).hadWarnings(); + } + + public void clearInputStream() { + ((NativeProtocol) this.protocol).clearInputStream(); + } + + public NetworkResources getNetworkResources() { + return this.protocol.getSocketConnection().getNetworkResources(); + } + + @Override + public boolean isSSLEstablished() { + return this.protocol.getSocketConnection().isSSLEstablished(); + } + + public int getCommandCount() { + return ((NativeProtocol) this.protocol).getCommandCount(); + } + + @Override + public SocketAddress getRemoteSocketAddress() { + return this.protocol.getSocketConnection().getMysqlSocket().getRemoteSocketAddress(); + } + + public ProfilerEventHandler getProfilerEventHandlerInstanceFunction() { + return ProfilerEventHandlerFactory.getInstance(this); + } + + public InputStream getLocalInfileInputStream() { + return this.protocol.getLocalInfileInputStream(); + } + + public void setLocalInfileInputStream(InputStream stream) { + this.protocol.setLocalInfileInputStream(stream); + } + + public void registerQueryExecutionTime(long queryTimeMs) { + ((NativeProtocol) this.protocol).getMetricsHolder().registerQueryExecutionTime(queryTimeMs); + } + + public void reportNumberOfTablesAccessed(int numTablesAccessed) { + ((NativeProtocol) this.protocol).getMetricsHolder().reportNumberOfTablesAccessed(numTablesAccessed); + } + + public void incrementNumberOfPreparedExecutes() { + if (this.gatherPerfMetrics.getValue()) { + ((NativeProtocol) this.protocol).getMetricsHolder().incrementNumberOfPreparedExecutes(); + } + } + + public void incrementNumberOfPrepares() { + if (this.gatherPerfMetrics.getValue()) { + ((NativeProtocol) this.protocol).getMetricsHolder().incrementNumberOfPrepares(); + } + } + + public void incrementNumberOfResultSetsCreated() { + if (this.gatherPerfMetrics.getValue()) { + ((NativeProtocol) this.protocol).getMetricsHolder().incrementNumberOfResultSetsCreated(); + } + } + + public void reportMetrics() { + if (this.gatherPerfMetrics.getValue()) { + + } + } + + /** + * Configures client-side properties for character set information. + */ + private void configureCharsetProperties() { + if (this.characterEncoding.getValue() != null) { + // Attempt to use the encoding, and bail out if it can't be used + try { + String testString = "abc"; + StringUtils.getBytes(testString, this.characterEncoding.getValue()); + } catch (WrongArgumentException waEx) { + // Try the MySQL character encoding, then.... + String oldEncoding = this.characterEncoding.getValue(); + + this.characterEncoding.setValue(CharsetMapping.getJavaEncodingForMysqlCharset(oldEncoding)); + + if (this.characterEncoding.getValue() == null) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("Connection.5", new Object[] { oldEncoding }), + getExceptionInterceptor()); + } + + String testString = "abc"; + StringUtils.getBytes(testString, this.characterEncoding.getValue()); + } + } + } + + /** + * Sets up client character set. This must be done before any further communication with the server! + * + * @return true if this routine actually configured the client character + * set, or false if the driver needs to use 'older' methods to + * detect the character set, as it is connected to a MySQL server + * older than 4.1.0 + * @throws CJException + * if an exception happens while sending 'SET NAMES' to the + * server, or the server sends character set information that + * the client doesn't know about. + */ + public boolean configureClientCharacterSet(boolean dontCheckServerMatch) { + String realJavaEncoding = this.characterEncoding.getValue(); + ModifiableProperty characterSetResults = getPropertySet().getModifiableProperty(PropertyDefinitions.PNAME_characterSetResults); + boolean characterSetAlreadyConfigured = false; + + try { + characterSetAlreadyConfigured = true; + + configureCharsetProperties(); + realJavaEncoding = this.characterEncoding.getValue(); // we need to do this again to grab this for versions > 4.1.0 + + try { + + // Fault injection for testing server character set indices + /* + * if (this.props != null && this.props.getProperty(PropertyDefinitions.PNAME_testsuite_faultInjection_serverCharsetIndex) != null) { + * this.session.setServerDefaultCollationIndex( + * Integer.parseInt(this.props.getProperty(PropertyDefinitions.PNAME_testsuite_faultInjection_serverCharsetIndex))); + * } + */ + + String serverEncodingToSet = CharsetMapping.getJavaEncodingForCollationIndex(this.protocol.getServerSession().getServerDefaultCollationIndex()); + + if (serverEncodingToSet == null || serverEncodingToSet.length() == 0) { + if (realJavaEncoding != null) { + // user knows best, try it + this.characterEncoding.setValue(realJavaEncoding); + } else { + throw ExceptionFactory.createException( + Messages.getString("Connection.6", new Object[] { this.protocol.getServerSession().getServerDefaultCollationIndex() }), + getExceptionInterceptor()); + } + } + + // "latin1" on MySQL-4.1.0+ is actually CP1252, not ISO8859_1 + if ("ISO8859_1".equalsIgnoreCase(serverEncodingToSet)) { + serverEncodingToSet = "Cp1252"; + } + if ("UnicodeBig".equalsIgnoreCase(serverEncodingToSet) || "UTF-16".equalsIgnoreCase(serverEncodingToSet) + || "UTF-16LE".equalsIgnoreCase(serverEncodingToSet) || "UTF-32".equalsIgnoreCase(serverEncodingToSet)) { + serverEncodingToSet = "UTF-8"; + } + + this.characterEncoding.setValue(serverEncodingToSet); + + } catch (ArrayIndexOutOfBoundsException outOfBoundsEx) { + if (realJavaEncoding != null) { + // user knows best, try it + this.characterEncoding.setValue(realJavaEncoding); + } else { + throw ExceptionFactory.createException( + Messages.getString("Connection.6", new Object[] { this.protocol.getServerSession().getServerDefaultCollationIndex() }), + getExceptionInterceptor()); + } + } + + if (this.characterEncoding.getValue() == null) { + // punt? + this.characterEncoding.setValue("ISO8859_1"); + } + + if (realJavaEncoding != null) { + + // + // Now, inform the server what character set we will be using from now-on... + // + if (realJavaEncoding.equalsIgnoreCase("UTF-8") || realJavaEncoding.equalsIgnoreCase("UTF8")) { + // charset names are case-sensitive + + boolean useutf8mb4 = CharsetMapping.UTF8MB4_INDEXES.contains(this.protocol.getServerSession().getServerDefaultCollationIndex()); + + if (!this.useOldUTF8Behavior.getValue()) { + if (dontCheckServerMatch || !this.protocol.getServerSession().characterSetNamesMatches("utf8") + || (!this.protocol.getServerSession().characterSetNamesMatches("utf8mb4"))) { + + sendCommand(this.commandBuilder.buildComQuery(null, "SET NAMES " + (useutf8mb4 ? "utf8mb4" : "utf8")), false, 0); + + this.protocol.getServerSession().getServerVariables().put("character_set_client", useutf8mb4 ? "utf8mb4" : "utf8"); + this.protocol.getServerSession().getServerVariables().put("character_set_connection", useutf8mb4 ? "utf8mb4" : "utf8"); + } + } else { + sendCommand(this.commandBuilder.buildComQuery(null, "SET NAMES latin1"), false, 0); + + this.protocol.getServerSession().getServerVariables().put("character_set_client", "latin1"); + this.protocol.getServerSession().getServerVariables().put("character_set_connection", "latin1"); + } + + this.characterEncoding.setValue(realJavaEncoding); + } /* not utf-8 */else { + String mysqlCharsetName = CharsetMapping.getMysqlCharsetForJavaEncoding(realJavaEncoding.toUpperCase(Locale.ENGLISH), + getServerSession().getServerVersion()); + + if (mysqlCharsetName != null) { + + if (dontCheckServerMatch || !this.protocol.getServerSession().characterSetNamesMatches(mysqlCharsetName)) { + sendCommand(this.commandBuilder.buildComQuery(null, "SET NAMES " + mysqlCharsetName), false, 0); + + this.protocol.getServerSession().getServerVariables().put("character_set_client", mysqlCharsetName); + this.protocol.getServerSession().getServerVariables().put("character_set_connection", mysqlCharsetName); + } + } + + // Switch driver's encoding now, since the server knows what we're sending... + // + this.characterEncoding.setValue(realJavaEncoding); + } + } else if (this.characterEncoding.getValue() != null) { + // Tell the server we'll use the server default charset to send our queries from now on.... + String mysqlCharsetName = getServerSession().getServerDefaultCharset(); + + if (this.useOldUTF8Behavior.getValue()) { + mysqlCharsetName = "latin1"; + } + + boolean ucs2 = false; + if ("ucs2".equalsIgnoreCase(mysqlCharsetName) || "utf16".equalsIgnoreCase(mysqlCharsetName) || "utf16le".equalsIgnoreCase(mysqlCharsetName) + || "utf32".equalsIgnoreCase(mysqlCharsetName)) { + mysqlCharsetName = "utf8"; + ucs2 = true; + if (characterSetResults.getValue() == null) { + characterSetResults.setValue("UTF-8"); + } + } + + if (dontCheckServerMatch || !this.protocol.getServerSession().characterSetNamesMatches(mysqlCharsetName) || ucs2) { + try { + sendCommand(this.commandBuilder.buildComQuery(null, "SET NAMES " + mysqlCharsetName), false, 0); + + this.protocol.getServerSession().getServerVariables().put("character_set_client", mysqlCharsetName); + this.protocol.getServerSession().getServerVariables().put("character_set_connection", mysqlCharsetName); + } catch (PasswordExpiredException ex) { + if (this.disconnectOnExpiredPasswords.getValue()) { + throw ex; + } + } + } + + realJavaEncoding = this.characterEncoding.getValue(); + } + + // + // We know how to deal with any charset coming back from the database, so tell the server not to do conversion if the user hasn't 'forced' a + // result-set character set + // + + String onServer = this.protocol.getServerSession().getServerVariable("character_set_results"); + if (characterSetResults.getValue() == null) { + + // + // Only send if needed, if we're caching server variables we -have- to send, because we don't know what it was before we cached them. + // + if (onServer != null && onServer.length() > 0 && !"NULL".equalsIgnoreCase(onServer)) { + try { + sendCommand(this.commandBuilder.buildComQuery(null, "SET character_set_results = NULL"), false, 0); + + } catch (PasswordExpiredException ex) { + if (this.disconnectOnExpiredPasswords.getValue()) { + throw ex; + } + } + this.protocol.getServerSession().getServerVariables().put(ServerSession.LOCAL_CHARACTER_SET_RESULTS, null); + } else { + this.protocol.getServerSession().getServerVariables().put(ServerSession.LOCAL_CHARACTER_SET_RESULTS, onServer); + } + } else { + + if (this.useOldUTF8Behavior.getValue()) { + try { + sendCommand(this.commandBuilder.buildComQuery(null, "SET NAMES latin1"), false, 0); + + this.protocol.getServerSession().getServerVariables().put("character_set_client", "latin1"); + this.protocol.getServerSession().getServerVariables().put("character_set_connection", "latin1"); + } catch (PasswordExpiredException ex) { + if (this.disconnectOnExpiredPasswords.getValue()) { + throw ex; + } + } + } + String charsetResults = characterSetResults.getValue(); + String mysqlEncodingName = null; + + if ("UTF-8".equalsIgnoreCase(charsetResults) || "UTF8".equalsIgnoreCase(charsetResults)) { + mysqlEncodingName = "utf8"; + } else if ("null".equalsIgnoreCase(charsetResults)) { + mysqlEncodingName = "NULL"; + } else { + mysqlEncodingName = CharsetMapping.getMysqlCharsetForJavaEncoding(charsetResults.toUpperCase(Locale.ENGLISH), + getServerSession().getServerVersion()); + } + + // + // Only change the value if needed + // + + if (mysqlEncodingName == null) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("Connection.7", new Object[] { charsetResults }), + getExceptionInterceptor()); + } + + if (!mysqlEncodingName.equalsIgnoreCase(this.protocol.getServerSession().getServerVariable("character_set_results"))) { + StringBuilder setBuf = new StringBuilder("SET character_set_results = ".length() + mysqlEncodingName.length()); + setBuf.append("SET character_set_results = ").append(mysqlEncodingName); + + try { + sendCommand(this.commandBuilder.buildComQuery(null, setBuf.toString()), false, 0); + + } catch (PasswordExpiredException ex) { + if (this.disconnectOnExpiredPasswords.getValue()) { + throw ex; + } + } + + this.protocol.getServerSession().getServerVariables().put(ServerSession.LOCAL_CHARACTER_SET_RESULTS, mysqlEncodingName); + + // We have to set errorMessageEncoding according to new value of charsetResults for server version 5.5 and higher + this.protocol.getServerSession().setErrorMessageEncoding(charsetResults); + + } else { + this.protocol.getServerSession().getServerVariables().put(ServerSession.LOCAL_CHARACTER_SET_RESULTS, onServer); + } + } + + String connectionCollation = getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_connectionCollation).getStringValue(); + if (connectionCollation != null) { + StringBuilder setBuf = new StringBuilder("SET collation_connection = ".length() + connectionCollation.length()); + setBuf.append("SET collation_connection = ").append(connectionCollation); + + try { + sendCommand(this.commandBuilder.buildComQuery(null, setBuf.toString()), false, 0); + + } catch (PasswordExpiredException ex) { + if (this.disconnectOnExpiredPasswords.getValue()) { + throw ex; + } + } + } + } finally { + // Failsafe, make sure that the driver's notion of character encoding matches what the user has specified. + // + this.characterEncoding.setValue(realJavaEncoding); + } + + /** + * Check if we need a CharsetEncoder for escaping codepoints that are + * transformed to backslash (0x5c) in the connection encoding. + */ + try { + CharsetEncoder enc = Charset.forName(this.characterEncoding.getValue()).newEncoder(); + CharBuffer cbuf = CharBuffer.allocate(1); + ByteBuffer bbuf = ByteBuffer.allocate(1); + + cbuf.put("\u00a5"); + cbuf.position(0); + enc.encode(cbuf, bbuf, true); + if (bbuf.get(0) == '\\') { + this.requiresEscapingEncoder = true; + } else { + cbuf.clear(); + bbuf.clear(); + + cbuf.put("\u20a9"); + cbuf.position(0); + enc.encode(cbuf, bbuf, true); + if (bbuf.get(0) == '\\') { + this.requiresEscapingEncoder = true; + } + } + } catch (java.nio.charset.UnsupportedCharsetException ucex) { + // fallback to String API + byte bbuf[] = StringUtils.getBytes("\u00a5", this.characterEncoding.getValue()); + if (bbuf[0] == '\\') { + this.requiresEscapingEncoder = true; + } else { + bbuf = StringUtils.getBytes("\u20a9", this.characterEncoding.getValue()); + if (bbuf[0] == '\\') { + this.requiresEscapingEncoder = true; + } + } + } + + return characterSetAlreadyConfigured; + } + + public boolean getRequiresEscapingEncoder() { + return this.requiresEscapingEncoder; + } + + private void createConfigCacheIfNeeded(Object syncMutex) { + synchronized (syncMutex) { + if (this.serverConfigCache != null) { + return; + } + + try { + Class factoryClass; + + factoryClass = Class.forName(getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_serverConfigCacheFactory).getStringValue()); + + @SuppressWarnings("unchecked") + CacheAdapterFactory> cacheFactory = ((CacheAdapterFactory>) factoryClass.newInstance()); + + this.serverConfigCache = cacheFactory.getInstance(syncMutex, this.hostInfo.getDatabaseUrl(), Integer.MAX_VALUE, Integer.MAX_VALUE); + + ExceptionInterceptor evictOnCommsError = new ExceptionInterceptor() { + + public ExceptionInterceptor init(Properties config, Log log1) { + return this; + } + + public void destroy() { + } + + @SuppressWarnings("synthetic-access") + public Exception interceptException(Exception sqlEx) { + if (sqlEx instanceof SQLException && ((SQLException) sqlEx).getSQLState() != null + && ((SQLException) sqlEx).getSQLState().startsWith("08")) { + NativeSession.this.serverConfigCache.invalidate(NativeSession.this.hostInfo.getDatabaseUrl()); + } + return null; + } + }; + + if (this.exceptionInterceptor == null) { + this.exceptionInterceptor = evictOnCommsError; + } else { + ((ExceptionInterceptorChain) this.exceptionInterceptor).addRingZero(evictOnCommsError); + } + } catch (ClassNotFoundException e) { + throw ExceptionFactory.createException(Messages.getString("Connection.CantFindCacheFactory", + new Object[] { getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_parseInfoCacheFactory).getValue(), + PropertyDefinitions.PNAME_parseInfoCacheFactory }), + e, getExceptionInterceptor()); + } catch (InstantiationException | IllegalAccessException | CJException e) { + throw ExceptionFactory.createException(Messages.getString("Connection.CantLoadCacheFactory", + new Object[] { getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_parseInfoCacheFactory).getValue(), + PropertyDefinitions.PNAME_parseInfoCacheFactory }), + e, getExceptionInterceptor()); + } + } + } + + // TODO what's the purpose of this variable? + private final static String SERVER_VERSION_STRING_VAR_NAME = "server_version_string"; + + /** + * Loads the result of 'SHOW VARIABLES' into the serverVariables field so + * that the driver can configure itself. + */ + public void loadServerVariables(Object syncMutex, String version) { + + if (this.cacheServerConfiguration.getValue()) { + createConfigCacheIfNeeded(syncMutex); + + Map cachedVariableMap = this.serverConfigCache.get(this.hostInfo.getDatabaseUrl()); + + if (cachedVariableMap != null) { + String cachedServerVersion = cachedVariableMap.get(SERVER_VERSION_STRING_VAR_NAME); + + if (cachedServerVersion != null && getServerSession().getServerVersion() != null + && cachedServerVersion.equals(getServerSession().getServerVersion().toString())) { + this.protocol.getServerSession().setServerVariables(cachedVariableMap); + + return; + } + + this.serverConfigCache.invalidate(this.hostInfo.getDatabaseUrl()); + } + } + + try { + if (version != null && version.indexOf('*') != -1) { + StringBuilder buf = new StringBuilder(version.length() + 10); + for (int i = 0; i < version.length(); i++) { + char c = version.charAt(i); + buf.append(c == '*' ? "[star]" : c); + } + version = buf.toString(); + } + + String versionComment = (this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_paranoid).getValue() || version == null) ? "" + : "/* " + version + " */"; + + this.protocol.getServerSession().setServerVariables(new HashMap()); + + if (versionMeetsMinimum(5, 1, 0)) { + StringBuilder queryBuf = new StringBuilder(versionComment).append("SELECT"); + queryBuf.append(" @@session.auto_increment_increment AS auto_increment_increment"); + queryBuf.append(", @@character_set_client AS character_set_client"); + queryBuf.append(", @@character_set_connection AS character_set_connection"); + queryBuf.append(", @@character_set_results AS character_set_results"); + queryBuf.append(", @@character_set_server AS character_set_server"); + queryBuf.append(", @@collation_server AS collation_server"); + queryBuf.append(", @@init_connect AS init_connect"); + queryBuf.append(", @@interactive_timeout AS interactive_timeout"); + if (!versionMeetsMinimum(5, 5, 0)) { + queryBuf.append(", @@language AS language"); + } + queryBuf.append(", @@license AS license"); + queryBuf.append(", @@lower_case_table_names AS lower_case_table_names"); + queryBuf.append(", @@max_allowed_packet AS max_allowed_packet"); + queryBuf.append(", @@net_write_timeout AS net_write_timeout"); + if (!versionMeetsMinimum(8, 0, 3)) { + queryBuf.append(", @@query_cache_size AS query_cache_size"); + queryBuf.append(", @@query_cache_type AS query_cache_type"); + } + queryBuf.append(", @@sql_mode AS sql_mode"); + queryBuf.append(", @@system_time_zone AS system_time_zone"); + queryBuf.append(", @@time_zone AS time_zone"); + if (versionMeetsMinimum(8, 0, 3) || (versionMeetsMinimum(5, 7, 20) && !versionMeetsMinimum(8, 0, 0))) { + queryBuf.append(", @@transaction_isolation AS transaction_isolation"); + } else { + queryBuf.append(", @@tx_isolation AS transaction_isolation"); + } + queryBuf.append(", @@wait_timeout AS wait_timeout"); + + NativePacketPayload resultPacket = sendCommand(this.commandBuilder.buildComQuery(null, queryBuf.toString()), false, 0); + Resultset rs = ((NativeProtocol) this.protocol).readAllResults(-1, false, resultPacket, false, null, + new ResultsetFactory(Type.FORWARD_ONLY, null)); + Field[] f = rs.getColumnDefinition().getFields(); + if (f.length > 0) { + ValueFactory vf = new StringValueFactory(f[0].getEncoding()); + Row r; + if ((r = rs.getRows().next()) != null) { + for (int i = 0; i < f.length; i++) { + this.protocol.getServerSession().getServerVariables().put(f[i].getColumnLabel(), r.getValue(i, vf)); + } + } + } + + } else { + NativePacketPayload resultPacket = sendCommand(this.commandBuilder.buildComQuery(null, versionComment + "SHOW VARIABLES"), false, 0); + Resultset rs = ((NativeProtocol) this.protocol).readAllResults(-1, false, resultPacket, false, null, + new ResultsetFactory(Type.FORWARD_ONLY, null)); + ValueFactory vf = new StringValueFactory(rs.getColumnDefinition().getFields()[0].getEncoding()); + Row r; + while ((r = rs.getRows().next()) != null) { + this.protocol.getServerSession().getServerVariables().put(r.getValue(0, vf), r.getValue(1, vf)); + } + } + } catch (PasswordExpiredException ex) { + if (this.disconnectOnExpiredPasswords.getValue()) { + throw ex; + } + } catch (IOException e) { + throw ExceptionFactory.createException(e.getMessage(), e); + } + + if (this.cacheServerConfiguration.getValue()) { + this.protocol.getServerSession().getServerVariables().put(SERVER_VERSION_STRING_VAR_NAME, getServerSession().getServerVersion().toString()); + this.serverConfigCache.put(this.hostInfo.getDatabaseUrl(), this.protocol.getServerSession().getServerVariables()); + } + } + + public void setSessionVariables() { + String sessionVariables = getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_sessionVariables).getValue(); + if (sessionVariables != null) { + List variablesToSet = new ArrayList<>(); + for (String part : StringUtils.split(sessionVariables, ",", "\"'(", "\"')", "\"'", true)) { + variablesToSet.addAll(StringUtils.split(part, ";", "\"'(", "\"')", "\"'", true)); + } + + if (!variablesToSet.isEmpty()) { + StringBuilder query = new StringBuilder("SET "); + String separator = ""; + for (String variableToSet : variablesToSet) { + if (variableToSet.length() > 0) { + query.append(separator); + if (!variableToSet.startsWith("@")) { + query.append("SESSION "); + } + query.append(variableToSet); + separator = ","; + } + } + sendCommand(this.commandBuilder.buildComQuery(null, query.toString()), false, 0); + } + } + } + + /** + * Builds the map needed for 4.1.0 and newer servers that maps field-level + * charset/collation info to a java character encoding name. + */ + public void buildCollationMapping() { + + Map customCharset = null; + Map customMblen = null; + + String databaseURL = this.hostInfo.getDatabaseUrl(); + + if (this.cacheServerConfiguration.getValue()) { + synchronized (customIndexToCharsetMapByUrl) { + customCharset = customIndexToCharsetMapByUrl.get(databaseURL); + customMblen = customCharsetToMblenMapByUrl.get(databaseURL); + } + } + + if (customCharset == null && getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_detectCustomCollations).getValue()) { + customCharset = new HashMap<>(); + customMblen = new HashMap<>(); + + ValueFactory ivf = new IntegerValueFactory(); + + try { + NativePacketPayload resultPacket = sendCommand(this.commandBuilder.buildComQuery(null, "SHOW COLLATION"), false, 0); + Resultset rs = ((NativeProtocol) this.protocol).readAllResults(-1, false, resultPacket, false, null, + new ResultsetFactory(Type.FORWARD_ONLY, null)); + ValueFactory svf = new StringValueFactory(rs.getColumnDefinition().getFields()[1].getEncoding()); + Row r; + while ((r = rs.getRows().next()) != null) { + int collationIndex = ((Number) r.getValue(2, ivf)).intValue(); + String charsetName = r.getValue(1, svf); + + // if no static map for charsetIndex or server has a different mapping then our static map, adding it to custom map + if (collationIndex >= CharsetMapping.MAP_SIZE || !charsetName.equals(CharsetMapping.getMysqlCharsetNameForCollationIndex(collationIndex))) { + customCharset.put(collationIndex, charsetName); + } + + // if no static map for charsetName adding to custom map + if (!CharsetMapping.CHARSET_NAME_TO_CHARSET.containsKey(charsetName)) { + customMblen.put(charsetName, null); + } + } + } catch (PasswordExpiredException ex) { + if (this.disconnectOnExpiredPasswords.getValue()) { + throw ex; + } + } catch (IOException e) { + throw ExceptionFactory.createException(e.getMessage(), e, this.exceptionInterceptor); + } + + // if there is a number of custom charsets we should execute SHOW CHARACTER SET to know theirs mblen + if (customMblen.size() > 0) { + try { + NativePacketPayload resultPacket = sendCommand(this.commandBuilder.buildComQuery(null, "SHOW CHARACTER SET"), false, 0); + Resultset rs = ((NativeProtocol) this.protocol).readAllResults(-1, false, resultPacket, false, null, + new ResultsetFactory(Type.FORWARD_ONLY, null)); + + int charsetColumn = rs.getColumnDefinition().getColumnNameToIndex().get("Charset"); + int maxlenColumn = rs.getColumnDefinition().getColumnNameToIndex().get("Maxlen"); + + ValueFactory svf = new StringValueFactory(rs.getColumnDefinition().getFields()[1].getEncoding()); + Row r; + while ((r = rs.getRows().next()) != null) { + String charsetName = r.getValue(charsetColumn, svf); + if (customMblen.containsKey(charsetName)) { + customMblen.put(charsetName, r.getValue(maxlenColumn, ivf)); + } + } + } catch (PasswordExpiredException ex) { + if (this.disconnectOnExpiredPasswords.getValue()) { + throw ex; + } + } catch (IOException e) { + throw ExceptionFactory.createException(e.getMessage(), e, this.exceptionInterceptor); + } + } + + if (this.cacheServerConfiguration.getValue()) { + synchronized (customIndexToCharsetMapByUrl) { + customIndexToCharsetMapByUrl.put(databaseURL, customCharset); + customCharsetToMblenMapByUrl.put(databaseURL, customMblen); + } + } + } + + // set charset maps + if (customCharset != null) { + ((NativeServerSession) this.protocol.getServerSession()).indexToCustomMysqlCharset = Collections.unmodifiableMap(customCharset); + } + if (customMblen != null) { + ((NativeServerSession) this.protocol.getServerSession()).mysqlCharsetToCustomMblen = Collections.unmodifiableMap(customMblen); + } + + // Trying to workaround server collations with index > 255. Such index doesn't fit into server greeting packet, 0 is sent instead. + // Now we could set io.serverCharsetIndex according to "collation_server" value. + if (this.protocol.getServerSession().getServerDefaultCollationIndex() == 0) { + String collationServer = this.protocol.getServerSession().getServerVariable("collation_server"); + if (collationServer != null) { + for (int i = 1; i < CharsetMapping.COLLATION_INDEX_TO_COLLATION_NAME.length; i++) { + if (CharsetMapping.COLLATION_INDEX_TO_COLLATION_NAME[i].equals(collationServer)) { + this.protocol.getServerSession().setServerDefaultCollationIndex(i); + break; + } + } + } else { + // We can't do more, just trying to use utf8mb4_general_ci because the most of collations in that range are utf8mb4. + this.protocol.getServerSession().setServerDefaultCollationIndex(45); + } + } + + } + + public String getProcessHost() { + try { + long threadId = getThreadId(); + String processHost = findProcessHost(threadId); + + if (processHost == null) { + // http://bugs.mysql.com/bug.php?id=44167 - connection ids on the wire wrap at 4 bytes even though they're 64-bit numbers + this.log.logWarn(String.format( + "Connection id %d not found in \"SHOW PROCESSLIST\", assuming 32-bit overflow, using SELECT CONNECTION_ID() instead", threadId)); + + NativePacketPayload resultPacket = sendCommand(this.commandBuilder.buildComQuery(null, "SELECT CONNECTION_ID()"), false, 0); + Resultset rs = ((NativeProtocol) this.protocol).readAllResults(-1, false, resultPacket, false, null, + new ResultsetFactory(Type.FORWARD_ONLY, null)); + + ValueFactory lvf = new LongValueFactory(); + Row r; + if ((r = rs.getRows().next()) != null) { + threadId = r.getValue(0, lvf); + processHost = findProcessHost(threadId); + } else { + this.log.logError("No rows returned for statement \"SELECT CONNECTION_ID()\", local connection check will most likely be incorrect"); + } + } + + if (processHost == null) { + this.log.logWarn(String.format( + "Cannot find process listing for connection %d in SHOW PROCESSLIST output, unable to determine if locally connected", threadId)); + } + return processHost; + } catch (IOException e) { + throw ExceptionFactory.createException(e.getMessage(), e); + } + } + + private String findProcessHost(long threadId) { + try { + String processHost = null; + + NativePacketPayload resultPacket = sendCommand(this.commandBuilder.buildComQuery(null, "SHOW PROCESSLIST"), false, 0); + Resultset rs = ((NativeProtocol) this.protocol).readAllResults(-1, false, resultPacket, false, null, new ResultsetFactory(Type.FORWARD_ONLY, null)); + + ValueFactory lvf = new LongValueFactory(); + ValueFactory svf = new StringValueFactory(rs.getColumnDefinition().getFields()[2].getEncoding()); + Row r; + while ((r = rs.getRows().next()) != null) { + long id = r.getValue(0, lvf); + if (threadId == id) { + processHost = r.getValue(2, svf); + break; + } + } + + return processHost; + + } catch (IOException e) { + throw ExceptionFactory.createException(e.getMessage(), e); + } + } + + /** + * Get the variable value from server. + * + * @param varName + * @return + */ + public String queryServerVariable(String varName) { + try { + + NativePacketPayload resultPacket = sendCommand(this.commandBuilder.buildComQuery(null, "SELECT " + varName), false, 0); + Resultset rs = ((NativeProtocol) this.protocol).readAllResults(-1, false, resultPacket, false, null, new ResultsetFactory(Type.FORWARD_ONLY, null)); + + ValueFactory svf = new StringValueFactory(rs.getColumnDefinition().getFields()[0].getEncoding()); + Row r; + if ((r = rs.getRows().next()) != null) { + String s = r.getValue(0, svf); + if (s != null) { + return s; + } + } + + return null; + + } catch (IOException e) { + throw ExceptionFactory.createException(e.getMessage(), e); + } + } + + /** + * Send a query to the server. Returns one of the ResultSet objects. + * To ensure that Statement's queries are serialized, calls to this method + * should be enclosed in a connection mutex synchronized block. + * + * @param callingQuery + * @param query + * the SQL statement to be executed + * @param maxRows + * @param packet + * @param streamResults + * @param catalog + * @param cachedMetadata + * @param isBatch + * @return a ResultSet holding the results + */ + public T execSQL(Query callingQuery, String query, int maxRows, NativePacketPayload packet, boolean streamResults, + ProtocolEntityFactory resultSetFactory, String catalog, ColumnDefinition cachedMetadata, boolean isBatch) { + + long queryStartTime = 0; + int endOfQueryPacketPosition = 0; + if (packet != null) { + endOfQueryPacketPosition = packet.getPosition(); + } + + if (this.gatherPerfMetrics.getValue()) { + queryStartTime = System.currentTimeMillis(); + } + + this.lastQueryFinishedTime = 0; // we're busy! + + if (this.autoReconnect.getValue() && (getServerSession().isAutoCommit() || this.autoReconnectForPools.getValue()) && this.needsPing && !isBatch) { + try { + ping(false, 0); + this.needsPing = false; + + } catch (Exception Ex) { + invokeReconnectListeners(); + } + } + + try { + if (packet == null) { + String encoding = this.characterEncoding.getValue(); + return ((NativeProtocol) this.protocol).sendQueryString(callingQuery, query, encoding, maxRows, streamResults, catalog, cachedMetadata, + this::getProfilerEventHandlerInstanceFunction, resultSetFactory); + } + return ((NativeProtocol) this.protocol).sendQueryPacket(callingQuery, packet, maxRows, streamResults, catalog, cachedMetadata, + this::getProfilerEventHandlerInstanceFunction, resultSetFactory); + + } catch (CJException sqlE) { + if (getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_dumpQueriesOnException).getValue()) { + String extractedSql = NativePacketPayload.extractSqlFromPacket(query, packet, endOfQueryPacketPosition, + getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_maxQuerySizeToLog).getValue()); + StringBuilder messageBuf = new StringBuilder(extractedSql.length() + 32); + messageBuf.append("\n\nQuery being executed when exception was thrown:\n"); + messageBuf.append(extractedSql); + messageBuf.append("\n\n"); + sqlE.appendMessage(messageBuf.toString()); + } + + if ((this.autoReconnect.getValue())) { + if (sqlE instanceof CJCommunicationsException) { + // IO may be dirty or damaged beyond repair, force close it. + this.protocol.getSocketConnection().forceClose(); + } + this.needsPing = true; + } else if (sqlE instanceof CJCommunicationsException) { + invokeCleanupListeners(sqlE); + } + throw sqlE; + + } catch (Throwable ex) { + if (this.autoReconnect.getValue()) { + if (ex instanceof IOException) { + // IO may be dirty or damaged beyond repair, force close it. + this.protocol.getSocketConnection().forceClose(); + } else if (ex instanceof IOException) { + invokeCleanupListeners(ex); + } + this.needsPing = true; + } + throw ExceptionFactory.createException(ex.getMessage(), ex, this.exceptionInterceptor); + + } finally { + if (this.maintainTimeStats.getValue()) { + this.lastQueryFinishedTime = System.currentTimeMillis(); + } + + if (this.gatherPerfMetrics.getValue()) { + long queryTime = System.currentTimeMillis() - queryStartTime; + + registerQueryExecutionTime(queryTime); + } + } + + } + + public long getIdleFor() { + if (this.lastQueryFinishedTime == 0) { + return 0; + } + + long now = System.currentTimeMillis(); + long idleTime = now - this.lastQueryFinishedTime; + + return idleTime; + } + + public boolean isNeedsPing() { + return this.needsPing; + } + + public void setNeedsPing(boolean needsPing) { + this.needsPing = needsPing; + } + + public void ping(boolean checkForClosedConnection, int timeoutMillis) { + if (checkForClosedConnection) { + checkClosed(); + } + + long pingMillisLifetime = getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_selfDestructOnPingSecondsLifetime).getValue(); + int pingMaxOperations = getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_selfDestructOnPingMaxOperations).getValue(); + + if ((pingMillisLifetime > 0 && (System.currentTimeMillis() - this.connectionCreationTimeMillis) > pingMillisLifetime) + || (pingMaxOperations > 0 && pingMaxOperations <= getCommandCount())) { + + invokeNormalCloseListeners(); + + throw ExceptionFactory.createException(Messages.getString("Connection.exceededConnectionLifetime"), + MysqlErrorNumbers.SQL_STATE_COMMUNICATION_LINK_FAILURE, 0, false, null, this.exceptionInterceptor); + } + sendCommand(this.commandBuilder.buildComPing(null), false, timeoutMillis); // it isn't safe to use a shared packet here + } + + public long getConnectionCreationTimeMillis() { + return this.connectionCreationTimeMillis; + } + + public void setConnectionCreationTimeMillis(long connectionCreationTimeMillis) { + this.connectionCreationTimeMillis = connectionCreationTimeMillis; + } + + public boolean isClosed() { + return this.isClosed; + } + + public void checkClosed() { + if (this.isClosed) { + if (this.forceClosedReason != null && this.forceClosedReason.getClass().equals(OperationCancelledException.class)) { + throw (OperationCancelledException) this.forceClosedReason; + } + throw ExceptionFactory.createException(ConnectionIsClosedException.class, Messages.getString("Connection.2"), this.forceClosedReason, + getExceptionInterceptor()); + } + } + + public Throwable getForceClosedReason() { + return this.forceClosedReason; + } + + public void setForceClosedReason(Throwable forceClosedReason) { + this.forceClosedReason = forceClosedReason; + } + + @Override + public void addListener(SessionEventListener l) { + this.listeners.addIfAbsent(new WeakReference<>(l)); + } + + @Override + public void removeListener(SessionEventListener listener) { + for (WeakReference wr : this.listeners) { + SessionEventListener l = wr.get(); + if (l == listener) { + this.listeners.remove(wr); + break; + } + } + } + + protected void invokeNormalCloseListeners() { + for (WeakReference wr : this.listeners) { + SessionEventListener l = wr.get(); + if (l != null) { + l.handleNormalClose(); + } else { + this.listeners.remove(wr); + } + } + } + + protected void invokeReconnectListeners() { + for (WeakReference wr : this.listeners) { + SessionEventListener l = wr.get(); + if (l != null) { + l.handleReconnect(); + } else { + this.listeners.remove(wr); + } + } + } + + public void invokeCleanupListeners(Throwable whyCleanedUp) { + for (WeakReference wr : this.listeners) { + SessionEventListener l = wr.get(); + if (l != null) { + l.handleCleanup(whyCleanedUp); + } else { + this.listeners.remove(wr); + } + } + } + + @Override + public String getIdentifierQuoteString() { + return this.protocol != null && this.protocol.getServerSession().useAnsiQuotedIdentifiers() ? "\"" : "`"; + } + + public synchronized Timer getCancelTimer() { + if (this.cancelTimer == null) { + this.cancelTimer = new Timer("MySQL Statement Cancellation Timer", Boolean.TRUE); + } + return this.cancelTimer; + } + + @Override + public RES_T query(M message, Predicate filterRow, Function mapRow, Collector collector) { + throw ExceptionFactory.createException(CJOperationNotSupportedException.class, "Not supported"); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/NoSubInterceptorWrapper.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/NoSubInterceptorWrapper.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/NoSubInterceptorWrapper.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.util.Properties; +import java.util.function.Supplier; + +import com.mysql.cj.interceptors.QueryInterceptor; +import com.mysql.cj.log.Log; +import com.mysql.cj.protocol.Message; +import com.mysql.cj.protocol.Resultset; +import com.mysql.cj.protocol.ServerSession; + +/** + * Wraps query interceptors during driver startup so that they don't produce different result sets than we expect. + */ +public class NoSubInterceptorWrapper implements QueryInterceptor { + + private final QueryInterceptor underlyingInterceptor; + + public NoSubInterceptorWrapper(QueryInterceptor underlyingInterceptor) { + if (underlyingInterceptor == null) { + throw new RuntimeException(Messages.getString("NoSubInterceptorWrapper.0")); + } + + this.underlyingInterceptor = underlyingInterceptor; + } + + public void destroy() { + this.underlyingInterceptor.destroy(); + } + + public boolean executeTopLevelOnly() { + return this.underlyingInterceptor.executeTopLevelOnly(); + } + + public QueryInterceptor init(MysqlConnection conn, Properties props, Log log) { + this.underlyingInterceptor.init(conn, props, log); + return this; + } + + public T postProcess(Supplier sql, Query interceptedQuery, T originalResultSet, ServerSession serverSession) { + this.underlyingInterceptor.postProcess(sql, interceptedQuery, originalResultSet, serverSession); + + return null; // don't allow result set substitution + } + + public T preProcess(Supplier sql, Query interceptedQuery) { + this.underlyingInterceptor.preProcess(sql, interceptedQuery); + + return null; // don't allow result set substitution + } + + @Override + public M preProcess(M queryPacket) { + this.underlyingInterceptor.preProcess(queryPacket); + + return null; // don't allow PacketPayload substitution + } + + @Override + public M postProcess(M queryPacket, M originalResponsePacket) { + this.underlyingInterceptor.postProcess(queryPacket, originalResponsePacket); + + return null; // don't allow PacketPayload substitution + } + + public QueryInterceptor getUnderlyingInterceptor() { + return this.underlyingInterceptor; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/ParseInfo.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/ParseInfo.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/ParseInfo.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; + +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.util.StringUtils; + +/** + * Represents the "parsed" state of a prepared query, with the statement broken up into its static and dynamic (where parameters are bound) parts. + */ +public class ParseInfo { + + protected static final String[] ON_DUPLICATE_KEY_UPDATE_CLAUSE = new String[] { "ON", "DUPLICATE", "KEY", "UPDATE" }; + + private char firstStmtChar = 0; + + private boolean foundLoadData = false; + + long lastUsed = 0; + + int statementLength = 0; + + int statementStartPos = 0; + + boolean canRewriteAsMultiValueInsert = false; + + byte[][] staticSql = null; + + boolean isOnDuplicateKeyUpdate = false; + + int locationOfOnDuplicateKeyUpdate = -1; + + String valuesClause; + + boolean parametersInDuplicateKeyClause = false; + + String charEncoding; + + private ParseInfo batchHead; + + private ParseInfo batchValues; + + private ParseInfo batchODKUClause; + + private ParseInfo(byte[][] staticSql, char firstStmtChar, boolean foundLoadData, boolean isOnDuplicateKeyUpdate, int locationOfOnDuplicateKeyUpdate, + int statementLength, int statementStartPos) { + this.firstStmtChar = firstStmtChar; + this.foundLoadData = foundLoadData; + this.isOnDuplicateKeyUpdate = isOnDuplicateKeyUpdate; + this.locationOfOnDuplicateKeyUpdate = locationOfOnDuplicateKeyUpdate; + this.statementLength = statementLength; + this.statementStartPos = statementStartPos; + this.staticSql = staticSql; + } + + public ParseInfo(String sql, Session session, String encoding) { + this(sql, session, encoding, true); + } + + public ParseInfo(String sql, Session session, String encoding, boolean buildRewriteInfo) { + + try { + if (sql == null) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedStatement.61"), + session.getExceptionInterceptor()); + } + + this.charEncoding = encoding; + this.lastUsed = System.currentTimeMillis(); + + String quotedIdentifierString = session.getIdentifierQuoteString(); + + char quotedIdentifierChar = 0; + + if ((quotedIdentifierString != null) && !quotedIdentifierString.equals(" ") && (quotedIdentifierString.length() > 0)) { + quotedIdentifierChar = quotedIdentifierString.charAt(0); + } + + this.statementLength = sql.length(); + + ArrayList endpointList = new ArrayList<>(); + boolean inQuotes = false; + char quoteChar = 0; + boolean inQuotedId = false; + int lastParmEnd = 0; + int i; + + boolean noBackslashEscapes = session.getServerSession().isNoBackslashEscapesSet(); + + // we're not trying to be real pedantic here, but we'd like to skip comments at the beginning of statements, as frameworks such as Hibernate + // use them to aid in debugging + + this.statementStartPos = findStartOfStatement(sql); + + for (i = this.statementStartPos; i < this.statementLength; ++i) { + char c = sql.charAt(i); + + if ((this.firstStmtChar == 0) && Character.isLetter(c)) { + // Determine what kind of statement we're doing (_S_elect, _I_nsert, etc.) + this.firstStmtChar = Character.toUpperCase(c); + + // no need to search for "ON DUPLICATE KEY UPDATE" if not an INSERT statement + if (this.firstStmtChar == 'I') { + this.locationOfOnDuplicateKeyUpdate = getOnDuplicateKeyLocation(sql, + session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_dontCheckOnDuplicateKeyUpdateInSQL).getValue(), + session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_rewriteBatchedStatements).getValue(), + session.getServerSession().isNoBackslashEscapesSet()); + this.isOnDuplicateKeyUpdate = this.locationOfOnDuplicateKeyUpdate != -1; + } + } + + if (!noBackslashEscapes && c == '\\' && i < (this.statementLength - 1)) { + i++; + continue; // next character is escaped + } + + // are we in a quoted identifier? (only valid when the id is not inside a 'string') + if (!inQuotes && (quotedIdentifierChar != 0) && (c == quotedIdentifierChar)) { + inQuotedId = !inQuotedId; + } else if (!inQuotedId) { + // only respect quotes when not in a quoted identifier + + if (inQuotes) { + if (((c == '\'') || (c == '"')) && c == quoteChar) { + if (i < (this.statementLength - 1) && sql.charAt(i + 1) == quoteChar) { + i++; + continue; // inline quote escape + } + + inQuotes = !inQuotes; + quoteChar = 0; + } else if (((c == '\'') || (c == '"')) && c == quoteChar) { + inQuotes = !inQuotes; + quoteChar = 0; + } + } else { + if (c == '#' || (c == '-' && (i + 1) < this.statementLength && sql.charAt(i + 1) == '-')) { + // run out to end of statement, or newline, whichever comes first + int endOfStmt = this.statementLength - 1; + + for (; i < endOfStmt; i++) { + c = sql.charAt(i); + + if (c == '\r' || c == '\n') { + break; + } + } + + continue; + } else if (c == '/' && (i + 1) < this.statementLength) { + // Comment? + char cNext = sql.charAt(i + 1); + + if (cNext == '*') { + i += 2; + + for (int j = i; j < this.statementLength; j++) { + i++; + cNext = sql.charAt(j); + + if (cNext == '*' && (j + 1) < this.statementLength) { + if (sql.charAt(j + 1) == '/') { + i++; + + if (i < this.statementLength) { + c = sql.charAt(i); + } + + break; // comment done + } + } + } + } + } else if ((c == '\'') || (c == '"')) { + inQuotes = true; + quoteChar = c; + } + } + } + + if ((c == '?') && !inQuotes && !inQuotedId) { + endpointList.add(new int[] { lastParmEnd, i }); + lastParmEnd = i + 1; + + if (this.isOnDuplicateKeyUpdate && i > this.locationOfOnDuplicateKeyUpdate) { + this.parametersInDuplicateKeyClause = true; + } + } + } + + if (this.firstStmtChar == 'L') { + if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { + this.foundLoadData = true; + } else { + this.foundLoadData = false; + } + } else { + this.foundLoadData = false; + } + + endpointList.add(new int[] { lastParmEnd, this.statementLength }); + this.staticSql = new byte[endpointList.size()][]; + + for (i = 0; i < this.staticSql.length; i++) { + int[] ep = endpointList.get(i); + int end = ep[1]; + int begin = ep[0]; + int len = end - begin; + + if (this.foundLoadData) { + this.staticSql[i] = StringUtils.getBytes(sql, begin, len); + } else if (encoding == null) { + byte[] buf = new byte[len]; + + for (int j = 0; j < len; j++) { + buf[j] = (byte) sql.charAt(begin + j); + } + + this.staticSql[i] = buf; + } else { + this.staticSql[i] = StringUtils.getBytes(sql, begin, len, encoding); + } + } + } catch (StringIndexOutOfBoundsException oobEx) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedStatement.62", new Object[] { sql }), oobEx, + session.getExceptionInterceptor()); + } + + if (buildRewriteInfo) { + this.canRewriteAsMultiValueInsert = canRewrite(sql, this.isOnDuplicateKeyUpdate, this.locationOfOnDuplicateKeyUpdate, this.statementStartPos) + && !this.parametersInDuplicateKeyClause; + + if (this.canRewriteAsMultiValueInsert + && session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_rewriteBatchedStatements).getValue()) { + buildRewriteBatchedParams(sql, session, encoding); + } + } + + } + + public byte[][] getStaticSql() { + return this.staticSql; + } + + public String getValuesClause() { + return this.valuesClause; + } + + public int getLocationOfOnDuplicateKeyUpdate() { + return this.locationOfOnDuplicateKeyUpdate; + } + + public boolean canRewriteAsMultiValueInsertAtSqlLevel() { + return this.canRewriteAsMultiValueInsert; + } + + public boolean containsOnDuplicateKeyUpdateInSQL() { + return this.isOnDuplicateKeyUpdate; + } + + private void buildRewriteBatchedParams(String sql, Session session, String encoding) { + this.valuesClause = extractValuesClause(sql, session.getIdentifierQuoteString()); + String odkuClause = this.isOnDuplicateKeyUpdate ? sql.substring(this.locationOfOnDuplicateKeyUpdate) : null; + + String headSql = null; + + if (this.isOnDuplicateKeyUpdate) { + headSql = sql.substring(0, this.locationOfOnDuplicateKeyUpdate); + } else { + headSql = sql; + } + + this.batchHead = new ParseInfo(headSql, session, encoding, false); + this.batchValues = new ParseInfo("," + this.valuesClause, session, encoding, false); + this.batchODKUClause = null; + + if (odkuClause != null && odkuClause.length() > 0) { + this.batchODKUClause = new ParseInfo("," + this.valuesClause + " " + odkuClause, session, encoding, false); + } + } + + private String extractValuesClause(String sql, String quoteCharStr) { + int indexOfValues = -1; + int valuesSearchStart = this.statementStartPos; + + while (indexOfValues == -1) { + if (quoteCharStr.length() > 0) { + indexOfValues = StringUtils.indexOfIgnoreCase(valuesSearchStart, sql, "VALUES", quoteCharStr, quoteCharStr, + StringUtils.SEARCH_MODE__MRK_COM_WS); + } else { + indexOfValues = StringUtils.indexOfIgnoreCase(valuesSearchStart, sql, "VALUES"); + } + + if (indexOfValues > 0) { + /* check if the char immediately preceding VALUES may be part of the table name */ + char c = sql.charAt(indexOfValues - 1); + if (!(Character.isWhitespace(c) || c == ')' || c == '`')) { + valuesSearchStart = indexOfValues + 6; + indexOfValues = -1; + } else { + /* check if the char immediately following VALUES may be whitespace or open parenthesis */ + c = sql.charAt(indexOfValues + 6); + if (!(Character.isWhitespace(c) || c == '(')) { + valuesSearchStart = indexOfValues + 6; + indexOfValues = -1; + } + } + } else { + break; + } + } + + if (indexOfValues == -1) { + return null; + } + + int indexOfFirstParen = sql.indexOf('(', indexOfValues + 6); + + if (indexOfFirstParen == -1) { + return null; + } + + int endOfValuesClause = sql.lastIndexOf(')'); + + if (endOfValuesClause == -1) { + return null; + } + + if (this.isOnDuplicateKeyUpdate) { + endOfValuesClause = this.locationOfOnDuplicateKeyUpdate - 1; + } + + return sql.substring(indexOfFirstParen, endOfValuesClause + 1); + } + + /** + * Returns a ParseInfo for a multi-value INSERT for a batch of size numBatch (without parsing!). + * + * @param numBatch + * number of batched parameters + * @return {@link ParseInfo} + */ + public synchronized ParseInfo getParseInfoForBatch(int numBatch) { + AppendingBatchVisitor apv = new AppendingBatchVisitor(); + buildInfoForBatch(numBatch, apv); + + ParseInfo batchParseInfo = new ParseInfo(apv.getStaticSqlStrings(), this.firstStmtChar, this.foundLoadData, this.isOnDuplicateKeyUpdate, + this.locationOfOnDuplicateKeyUpdate, this.statementLength, this.statementStartPos); + + return batchParseInfo; + } + + /** + * Returns a preparable SQL string for the number of batched parameters; used by server-side prepared statements + * when re-writing batch INSERTs. + * + * @param numBatch + * number of batched parameters + * @return SQL string + * @throws UnsupportedEncodingException + * if an error occurs + */ + public String getSqlForBatch(int numBatch) throws UnsupportedEncodingException { + ParseInfo batchInfo = getParseInfoForBatch(numBatch); + + return batchInfo.getSqlForBatch(); + } + + /** + * Used for filling in the SQL for getPreparedSql() - for debugging + * + * @return sql string + * @throws UnsupportedEncodingException + * if an error occurs + */ + public String getSqlForBatch() throws UnsupportedEncodingException { + int size = 0; + final byte[][] sqlStrings = this.staticSql; + final int sqlStringsLength = sqlStrings.length; + + for (int i = 0; i < sqlStringsLength; i++) { + size += sqlStrings[i].length; + size++; // for the '?' + } + + StringBuilder buf = new StringBuilder(size); + + for (int i = 0; i < sqlStringsLength - 1; i++) { + buf.append(StringUtils.toString(sqlStrings[i], this.charEncoding)); + buf.append("?"); + } + + buf.append(StringUtils.toString(sqlStrings[sqlStringsLength - 1])); + + return buf.toString(); + } + + /** + * Builds a ParseInfo for the given batch size, without parsing. We use + * a visitor pattern here, because the if {}s make computing a size for the + * resultant byte[][] make this too complex, and we don't necessarily want to + * use a List for this, because the size can be dynamic, and thus we'll not be + * able to guess a good initial size for an array-based list, and it's not + * efficient to convert a LinkedList to an array. + * + * @param numBatch + * number of batched parameters + * @param visitor + * visitor + */ + private void buildInfoForBatch(int numBatch, BatchVisitor visitor) { + final byte[][] headStaticSql = this.batchHead.staticSql; + final int headStaticSqlLength = headStaticSql.length; + + if (headStaticSqlLength > 1) { + for (int i = 0; i < headStaticSqlLength - 1; i++) { + visitor.append(headStaticSql[i]).increment(); + } + } + + // merge end of head, with beginning of a value clause + byte[] endOfHead = headStaticSql[headStaticSqlLength - 1]; + final byte[][] valuesStaticSql = this.batchValues.staticSql; + byte[] beginOfValues = valuesStaticSql[0]; + + visitor.merge(endOfHead, beginOfValues).increment(); + + int numValueRepeats = numBatch - 1; // first one is in the "head" + + if (this.batchODKUClause != null) { + numValueRepeats--; // Last one is in the ODKU clause + } + + final int valuesStaticSqlLength = valuesStaticSql.length; + byte[] endOfValues = valuesStaticSql[valuesStaticSqlLength - 1]; + + for (int i = 0; i < numValueRepeats; i++) { + for (int j = 1; j < valuesStaticSqlLength - 1; j++) { + visitor.append(valuesStaticSql[j]).increment(); + } + visitor.merge(endOfValues, beginOfValues).increment(); + } + + if (this.batchODKUClause != null) { + final byte[][] batchOdkuStaticSql = this.batchODKUClause.staticSql; + byte[] beginOfOdku = batchOdkuStaticSql[0]; + visitor.decrement().merge(endOfValues, beginOfOdku).increment(); + + final int batchOdkuStaticSqlLength = batchOdkuStaticSql.length; + + if (numBatch > 1) { + for (int i = 1; i < batchOdkuStaticSqlLength; i++) { + visitor.append(batchOdkuStaticSql[i]).increment(); + } + } else { + visitor.decrement().append(batchOdkuStaticSql[(batchOdkuStaticSqlLength - 1)]); + } + } else { + // Everything after the values clause, but not ODKU, which today is nothing but a syntax error, but we should still not mangle the SQL! + visitor.decrement().append(this.staticSql[this.staticSql.length - 1]); + } + } + + protected static int findStartOfStatement(String sql) { + int statementStartPos = 0; + + if (StringUtils.startsWithIgnoreCaseAndWs(sql, "/*")) { + statementStartPos = sql.indexOf("*/"); + + if (statementStartPos == -1) { + statementStartPos = 0; + } else { + statementStartPos += 2; + } + } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "--") || StringUtils.startsWithIgnoreCaseAndWs(sql, "#")) { + statementStartPos = sql.indexOf('\n'); + + if (statementStartPos == -1) { + statementStartPos = sql.indexOf('\r'); + + if (statementStartPos == -1) { + statementStartPos = 0; + } + } + } + + return statementStartPos; + } + + public static int getOnDuplicateKeyLocation(String sql, boolean dontCheckOnDuplicateKeyUpdateInSQL, boolean rewriteBatchedStatements, + boolean noBackslashEscapes) { + return dontCheckOnDuplicateKeyUpdateInSQL && !rewriteBatchedStatements ? -1 : StringUtils.indexOfIgnoreCase(0, sql, ON_DUPLICATE_KEY_UPDATE_CLAUSE, + "\"'`", "\"'`", noBackslashEscapes ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + } + + protected static boolean canRewrite(String sql, boolean isOnDuplicateKeyUpdate, int locationOfOnDuplicateKeyUpdate, int statementStartPos) { + // Needs to be INSERT or REPLACE. + // Can't have INSERT ... SELECT or INSERT ... ON DUPLICATE KEY UPDATE with an id=LAST_INSERT_ID(...). + + if (StringUtils.startsWithIgnoreCaseAndWs(sql, "INSERT", statementStartPos)) { + if (StringUtils.indexOfIgnoreCase(statementStartPos, sql, "SELECT", "\"'`", "\"'`", StringUtils.SEARCH_MODE__MRK_COM_WS) != -1) { + return false; + } + if (isOnDuplicateKeyUpdate) { + int updateClausePos = StringUtils.indexOfIgnoreCase(locationOfOnDuplicateKeyUpdate, sql, " UPDATE "); + if (updateClausePos != -1) { + return StringUtils.indexOfIgnoreCase(updateClausePos, sql, "LAST_INSERT_ID", "\"'`", "\"'`", StringUtils.SEARCH_MODE__MRK_COM_WS) == -1; + } + } + return true; + } + + return StringUtils.startsWithIgnoreCaseAndWs(sql, "REPLACE", statementStartPos) + && StringUtils.indexOfIgnoreCase(statementStartPos, sql, "SELECT", "\"'`", "\"'`", StringUtils.SEARCH_MODE__MRK_COM_WS) == -1; + } + + public boolean isFoundLoadData() { + return this.foundLoadData; + } + + public char getFirstStmtChar() { + return this.firstStmtChar; + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/PerConnectionLRUFactory.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/PerConnectionLRUFactory.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/PerConnectionLRUFactory.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.util.Set; + +import com.mysql.cj.util.LRUCache; + +public class PerConnectionLRUFactory implements CacheAdapterFactory { + + public CacheAdapter getInstance(Object syncMutex, String url, int cacheMaxSize, int maxKeySize) { + + return new PerConnectionLRU(syncMutex, cacheMaxSize, maxKeySize); + } + + class PerConnectionLRU implements CacheAdapter { + private final int cacheSqlLimit; + private final LRUCache cache; + private final Object syncMutex; + + protected PerConnectionLRU(Object syncMutex, int cacheMaxSize, int maxKeySize) { + final int cacheSize = cacheMaxSize; + this.cacheSqlLimit = maxKeySize; + this.cache = new LRUCache<>(cacheSize); + this.syncMutex = syncMutex; + } + + public ParseInfo get(String key) { + if (key == null || key.length() > this.cacheSqlLimit) { + return null; + } + + synchronized (this.syncMutex) { + return this.cache.get(key); + } + } + + public void put(String key, ParseInfo value) { + if (key == null || key.length() > this.cacheSqlLimit) { + return; + } + + synchronized (this.syncMutex) { + this.cache.put(key, value); + } + } + + public void invalidate(String key) { + synchronized (this.syncMutex) { + this.cache.remove(key); + } + } + + public void invalidateAll(Set keys) { + synchronized (this.syncMutex) { + for (String key : keys) { + this.cache.remove(key); + } + } + + } + + public void invalidateAll() { + synchronized (this.syncMutex) { + this.cache.clear(); + } + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/PingTarget.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/PingTarget.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/PingTarget.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +public interface PingTarget { + + void doPing() throws Exception; + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/PreparedQuery.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/PreparedQuery.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/PreparedQuery.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import com.mysql.cj.protocol.Message; + +public interface PreparedQuery> extends Query { + + ParseInfo getParseInfo(); + + void setParseInfo(ParseInfo parseInfo); + + void checkNullOrEmptyQuery(String sql); + + String getOriginalSql(); + + void setOriginalSql(String originalSql); + + int getParameterCount(); + + void setParameterCount(int parameterCount); + + public T getQueryBindings(); + + public void setQueryBindings(T queryBindings); + + int computeBatchSize(int numBatchedArgs); + + int getBatchCommandIndex(); + + void setBatchCommandIndex(int batchCommandIndex); + + String asSql(); + + String asSql(boolean quoteStreamsAndUnknowns); + + M fillSendPacket(); + + M fillSendPacket(QueryBindings bindings); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/Query.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/Query.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/Query.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.mysql.cj.log.ProfilerEventHandler; +import com.mysql.cj.protocol.Message; +import com.mysql.cj.protocol.ProtocolEntityFactory; +import com.mysql.cj.protocol.Resultset; + +public interface Query { + + public enum CancelStatus { + NOT_CANCELED, CANCELED_BY_USER, CANCELED_BY_TIMEOUT; + } + + /** + * Returns the query id used when profiling + * + * @return id + */ + int getId(); + + void setCancelStatus(CancelStatus cs); + + void checkCancelTimeout(); + + ProtocolEntityFactory getResultSetFactory(); + + Session getSession(); + + Object getCancelTimeoutMutex(); + + void resetCancelledState(); + + void closeQuery(); + + void addBatch(Object batch); + + /** + * Get the batched args as added by the addBatch method(s). + * The list is unmodifiable and might contain any combination of String, + * ClientPreparedQueryBindings, or ServerPreparedQueryBindings depending on how the parameters were + * batched. + * + * @return an unmodifiable List of batched args + */ + List getBatchedArgs(); + + void clearBatchedArgs(); + + int getResultFetchSize(); + + void setResultFetchSize(int fetchSize); + + Resultset.Type getResultType(); + + void setResultType(Resultset.Type resultSetType); + + int getTimeoutInMillis(); + + void setTimeoutInMillis(int timeoutInMillis); + + CancelQueryTask startQueryTimer(Query stmtToCancel, int timeout); + + ProfilerEventHandler getEventSink(); + + void setEventSink(ProfilerEventHandler eventSink); + + AtomicBoolean getStatementExecuting(); + + String getCurrentCatalog(); + + void setCurrentCatalog(String currentCatalog); + + boolean isClearWarningsCalled(); + + void setClearWarningsCalled(boolean clearWarningsCalled); + + void statementBegins(); + + void stopQueryTimer(CancelQueryTask timeoutTask, boolean rethrowCancelReason, boolean checkCancelTimeout); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/QueryBindings.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/QueryBindings.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/QueryBindings.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.TimeZone; + +public interface QueryBindings { + + QueryBindings clone(); + + boolean isLoadDataQuery(); + + void setLoadDataQuery(boolean isLoadDataQuery); + + T[] getBindValues(); + + void setBindValues(T[] bindValues); + + /** + * + * @return true if bind values had long data + */ + boolean clearBindValues(); + + void checkParameterSet(int columnIndex); + + void checkAllParametersSet(); + + int getNumberOfExecutions(); + + void setNumberOfExecutions(int numberOfExecutions); + + void setValue(int paramIndex, byte[] val); + + void setValue(int paramIndex, byte[] val, MysqlType type); + + void setValue(int paramIndex, String val); + + void setValue(int paramIndex, String val, MysqlType type); + + // Array getArray(int parameterIndex); + + void setAsciiStream(int parameterIndex, InputStream x); + + void setAsciiStream(int parameterIndex, InputStream x, int length); + + void setAsciiStream(int parameterIndex, InputStream x, long length); + + // InputStream getAsciiStream(int parameterIndex); + + void setBigDecimal(int parameterIndex, BigDecimal x); + + // BigDecimal getBigDecimal(int parameterIndex); + + void setBigInteger(int parameterIndex, BigInteger x); + + // BigInteger getBigInteger(int parameterIndex); + + void setBinaryStream(int parameterIndex, InputStream x); + + void setBinaryStream(int parameterIndex, InputStream x, int length); + + void setBinaryStream(int parameterIndex, InputStream x, long length); + + // InputStream getBinaryStream(int parameterIndex); + + void setBlob(int parameterIndex, java.sql.Blob x); + + void setBlob(int parameterIndex, InputStream inputStream); + + void setBlob(int parameterIndex, InputStream inputStream, long length); + + // java.sql.Blob getBlob(int parameterIndex); + + void setBoolean(int parameterIndex, boolean x); + + // boolean getBoolean(int parameterIndex); + + void setByte(int parameterIndex, byte x); + + // byte getByte(int parameterIndex); + + void setBytes(int parameterIndex, byte[] x); + + void setBytes(int parameterIndex, byte[] x, boolean checkForIntroducer, boolean escapeForMBChars); + + void setBytesNoEscape(int parameterIndex, byte[] parameterAsBytes); + + void setBytesNoEscapeNoQuotes(int parameterIndex, byte[] parameterAsBytes); + + // byte[] getBytes(int parameterIndex); + + void setCharacterStream(int parameterIndex, Reader reader); + + void setCharacterStream(int parameterIndex, Reader reader, int length); + + void setCharacterStream(int parameterIndex, Reader reader, long length); + + // Reader getCharacterStream(int parameterIndex); + + void setClob(int i, Clob x); + + void setClob(int parameterIndex, Reader reader); + + void setClob(int parameterIndex, Reader reader, long length); + + // Clob getClob(int parameterIndex); + + void setDate(int parameterIndex, Date x); + + void setDate(int parameterIndex, Date x, Calendar cal); + + void setDate(int parameterIndex, Date x, TimeZone tz); + + // Date getDate(int parameterIndex); + + void setDouble(int parameterIndex, double x); + + // double getDouble(int parameterIndex) + + void setFloat(int parameterIndex, float x); + + // float getFloat(int parameterIndex); + + void setInt(int parameterIndex, int x); + + // int getInt(int parameterIndex); + + void setLong(int parameterIndex, long x); + + // long getLong(int parameterIndex); + + void setNCharacterStream(int parameterIndex, Reader value); + + void setNCharacterStream(int parameterIndex, Reader reader, long length); + + // Reader getNCharacterStream(int parameterIndex); + + void setNClob(int parameterIndex, Reader reader); + + void setNClob(int parameterIndex, Reader reader, long length); + + void setNClob(int parameterIndex, NClob value); + + // Reader getNClob(int parameterIndex); + + void setNString(int parameterIndex, String x); + + void setNull(int parameterIndex); + + // boolean isNull(int parameterIndex); + + void setObject(int parameterIndex, Object parameterObj); + + void setObject(int parameterIndex, Object parameterObj, MysqlType targetMysqlType); + + void setObject(int parameterIndex, Object parameterObj, MysqlType targetMysqlType, int scaleOrLength); + + // Object getObject(int parameterIndex); + + // Ref getRef(int parameterIndex); + + void setShort(int parameterIndex, short x); + + // short getShort(int parameterIndex); + + void setString(int parameterIndex, String x); + + // String getString(int parameterIndex); + + void setTime(int parameterIndex, Time x); + + void setTime(int parameterIndex, Time x, Calendar cal); + + void setTime(int parameterIndex, Time x, TimeZone tz); + + // Time getTime(int parameterIndex); + + void setTimestamp(int parameterIndex, Timestamp x, Calendar cal); + + void setTimestamp(int parameterIndex, Timestamp x); + + void setTimestamp(int parameterIndex, Timestamp x, TimeZone tz); + + // Timestamp getTimestamp(int parameterIndex); + + // URL getURL(int parameterIndex); + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/QueryResult.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/QueryResult.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/QueryResult.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +public interface QueryResult { + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/ServerPreparedQuery.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/ServerPreparedQuery.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/ServerPreparedQuery.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,797 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.ReadableProperty; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.log.ProfilerEvent; +import com.mysql.cj.log.ProfilerEventHandlerFactory; +import com.mysql.cj.log.ProfilerEventImpl; +import com.mysql.cj.protocol.ColumnDefinition; +import com.mysql.cj.protocol.Message; +import com.mysql.cj.protocol.ProtocolEntityFactory; +import com.mysql.cj.protocol.Resultset; +import com.mysql.cj.protocol.Resultset.Type; +import com.mysql.cj.protocol.a.ColumnDefinitionFactory; +import com.mysql.cj.protocol.a.NativeMessageBuilder; +import com.mysql.cj.protocol.a.NativeConstants; +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.NativePacketPayload; +import com.mysql.cj.result.Field; +import com.mysql.cj.util.LogUtils; +import com.mysql.cj.util.StringUtils; + +//TODO should not be protocol-specific + +public class ServerPreparedQuery extends AbstractPreparedQuery { + + public static final int BLOB_STREAM_READ_BUF_SIZE = 8192; + public static final byte OPEN_CURSOR_FLAG = 1; + + /** The ID that the server uses to identify this PreparedStatement */ + private long serverStatementId; + + /** Field-level metadata for parameters */ + private Field[] parameterFields; + + /** Field-level metadata for result sets. From statement prepare. */ + private ColumnDefinition resultFields; + + protected ReadableProperty gatherPerfMetrics; + + protected boolean logSlowQueries = false; + + private boolean useAutoSlowLog; + + protected ReadableProperty slowQueryThresholdMillis; + + protected ReadableProperty explainSlowQueries; + + protected boolean queryWasSlow = false; + + protected NativeMessageBuilder commandBuilder = new NativeMessageBuilder(); // TODO use shared builder + + public static ServerPreparedQuery getInstance(NativeSession sess) { + if (sess.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_autoGenerateTestcaseScript).getValue()) { + return new ServerPreparedQueryTestcaseGenerator(sess); + } + return new ServerPreparedQuery(sess); + } + + protected ServerPreparedQuery(NativeSession sess) { + super(sess); + this.gatherPerfMetrics = sess.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_gatherPerfMetrics); + this.logSlowQueries = sess.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_logSlowQueries).getValue(); + this.useAutoSlowLog = sess.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_autoSlowLog).getValue(); + this.slowQueryThresholdMillis = sess.getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_slowQueryThresholdMillis); + this.explainSlowQueries = sess.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_explainSlowQueries); + + } + + /** + * + * @param sql + * @throws IOException + */ + public void serverPrepare(String sql) throws IOException { + this.session.checkClosed(); + + synchronized (this.session) { + long begin = 0; + + if (this.profileSQL) { + begin = System.currentTimeMillis(); + } + + boolean loadDataQuery = StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA"); + + String characterEncoding = null; + String connectionEncoding = this.session.getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_characterEncoding).getValue(); + + if (!loadDataQuery && (connectionEncoding != null)) { + characterEncoding = connectionEncoding; + } + + NativePacketPayload prepareResultPacket = this.session + .sendCommand(this.commandBuilder.buildComStmtPrepare(this.session.getSharedSendPacket(), sql, characterEncoding), false, 0); + + // 4.1.1 and newer use the first byte as an 'ok' or 'error' flag, so move the buffer pointer past it to start reading the statement id. + prepareResultPacket.setPosition(1); + + this.serverStatementId = prepareResultPacket.readInteger(IntegerDataType.INT4); + int fieldCount = (int) prepareResultPacket.readInteger(IntegerDataType.INT2); + setParameterCount((int) prepareResultPacket.readInteger(IntegerDataType.INT2)); + + this.queryBindings = new ServerPreparedQueryBindings(this.parameterCount, this.session); + this.queryBindings.setLoadDataQuery(loadDataQuery); + + this.session.incrementNumberOfPrepares(); + + if (this.profileSQL) { + this.eventSink.consumeEvent(new ProfilerEventImpl(ProfilerEvent.TYPE_PREPARE, "", this.getCurrentCatalog(), this.session.getThreadId(), + this.statementId, -1, System.currentTimeMillis(), this.session.getCurrentTimeNanosOrMillis() - begin, + this.session.getQueryTimingUnits(), null, LogUtils.findCallingClassAndMethod(new Throwable()), truncateQueryToLog(sql))); + } + + boolean checkEOF = !this.session.getServerSession().isEOFDeprecated(); + + if (this.parameterCount > 0) { + if (checkEOF) { // Skip the following EOF packet. + this.session.getProtocol().skipPacket(); + } + + this.parameterFields = this.session.getProtocol().read(ColumnDefinition.class, new ColumnDefinitionFactory(this.parameterCount, null)) + .getFields(); + } + + // Read in the result set column information + if (fieldCount > 0) { + this.resultFields = this.session.getProtocol().read(ColumnDefinition.class, new ColumnDefinitionFactory(fieldCount, null)); + } + } + } + + @Override + public void statementBegins() { + super.statementBegins(); + this.queryWasSlow = false; + } + + /** + * + * @param maxRowsToRetrieve + * @param createStreamingResultSet + * @param metadata + * @return + */ + public T serverExecute(int maxRowsToRetrieve, boolean createStreamingResultSet, ColumnDefinition metadata, + ProtocolEntityFactory resultSetFactory) { + if (this.session.shouldIntercept()) { + T interceptedResults = this.session.invokeQueryInterceptorsPre(() -> { + return getOriginalSql(); + }, this, true); + + if (interceptedResults != null) { + return interceptedResults; + } + } + String queryAsString = ""; + if (this.profileSQL || this.logSlowQueries || this.gatherPerfMetrics.getValue()) { + queryAsString = asSql(true); + } + + NativePacketPayload packet = prepareExecutePacket(); + NativePacketPayload resPacket = sendExecutePacket(packet, queryAsString); + T rs = readExecuteResult(resPacket, maxRowsToRetrieve, createStreamingResultSet, metadata, resultSetFactory, queryAsString); + + return rs; + } + + public NativePacketPayload prepareExecutePacket() { + + ServerPreparedQueryBindValue[] parameterBindings = this.queryBindings.getBindValues(); + + if (this.queryBindings.isLongParameterSwitchDetected()) { + // Check when values were bound + boolean firstFound = false; + long boundTimeToCheck = 0; + + for (int i = 0; i < this.parameterCount - 1; i++) { + if (parameterBindings[i].isLongData) { + if (firstFound && boundTimeToCheck != parameterBindings[i].boundBeforeExecutionNum) { + throw ExceptionFactory.createException( + Messages.getString("ServerPreparedStatement.11") + Messages.getString("ServerPreparedStatement.12"), + MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, 0, true, null, this.session.getExceptionInterceptor()); + } + firstFound = true; + boundTimeToCheck = parameterBindings[i].boundBeforeExecutionNum; + } + } + + // Okay, we've got all "newly"-bound streams, so reset server-side state to clear out previous bindings + + serverResetStatement(); + } + + this.queryBindings.checkAllParametersSet(); + + // + // Send all long data + // + for (int i = 0; i < this.parameterCount; i++) { + if (parameterBindings[i].isLongData) { + serverLongData(i, parameterBindings[i]); + } + } + + // + // store the parameter values + // + + NativePacketPayload packet = this.session.getSharedSendPacket(); + packet.writeInteger(IntegerDataType.INT1, NativeConstants.COM_STMT_EXECUTE); + packet.writeInteger(IntegerDataType.INT4, this.serverStatementId); + + // we only create cursor-backed result sets if + // a) The query is a SELECT + // b) The server supports it + // c) We know it is forward-only (note this doesn't preclude updatable result sets) + // d) The user has set a fetch size + if (this.resultFields != null && this.resultFields.getFields() != null && this.useCursorFetch && this.resultSetType == Type.FORWARD_ONLY + && this.fetchSize > 0) { + packet.writeInteger(IntegerDataType.INT1, OPEN_CURSOR_FLAG); + } else { + packet.writeInteger(IntegerDataType.INT1, 0); // placeholder for flags + } + + packet.writeInteger(IntegerDataType.INT4, 1); // placeholder for parameter iterations + + /* Reserve place for null-marker bytes */ + int nullCount = (this.parameterCount + 7) / 8; + + int nullBitsPosition = packet.getPosition(); + + for (int i = 0; i < nullCount; i++) { + packet.writeInteger(IntegerDataType.INT1, 0); + } + + byte[] nullBitsBuffer = new byte[nullCount]; + + /* In case if buffers (type) altered, indicate to server */ + packet.writeInteger(IntegerDataType.INT1, this.queryBindings.getSendTypesToServer().get() ? (byte) 1 : (byte) 0); + + if (this.queryBindings.getSendTypesToServer().get()) { + /* + * Store types of parameters in the first package that is sent to the server. + */ + for (int i = 0; i < this.parameterCount; i++) { + packet.writeInteger(IntegerDataType.INT2, parameterBindings[i].bufferType); + } + } + + // + // store the parameter values + // + for (int i = 0; i < this.parameterCount; i++) { + if (!parameterBindings[i].isLongData) { + if (!parameterBindings[i].isNull()) { + parameterBindings[i].storeBinding(packet, this.queryBindings.isLoadDataQuery(), this.charEncoding, this.session.getExceptionInterceptor()); + } else { + nullBitsBuffer[i / 8] |= (1 << (i & 7)); + } + } + } + + // + // Go back and write the NULL flags to the beginning of the packet + // + int endPosition = packet.getPosition(); + packet.setPosition(nullBitsPosition); + packet.writeBytes(StringLengthDataType.STRING_FIXED, nullBitsBuffer); + packet.setPosition(endPosition); + + return packet; + } + + public NativePacketPayload sendExecutePacket(NativePacketPayload packet, String queryAsString) { // TODO queryAsString should be shared instead of passed + + long begin = 0; + + boolean gatherPerformanceMetrics = this.gatherPerfMetrics.getValue(); + + if (this.profileSQL || this.logSlowQueries || gatherPerformanceMetrics) { + begin = this.session.getCurrentTimeNanosOrMillis(); + } + + resetCancelledState(); + + CancelQueryTask timeoutTask = null; + + try { + // Get this before executing to avoid a shared packet pollution in the case some other query is issued internally, such as when using I_S. + + timeoutTask = startQueryTimer(this, this.timeoutInMillis); + + statementBegins(); + + NativePacketPayload resultPacket = this.session.sendCommand(packet, false, 0); + + long queryEndTime = 0L; + + if (this.logSlowQueries || gatherPerformanceMetrics || this.profileSQL) { + queryEndTime = this.session.getCurrentTimeNanosOrMillis(); + } + + if (timeoutTask != null) { + stopQueryTimer(timeoutTask, true, true); + timeoutTask = null; + } + + if (this.logSlowQueries || gatherPerformanceMetrics) { + long elapsedTime = queryEndTime - begin; + + if (this.logSlowQueries) { + if (this.useAutoSlowLog) { + this.queryWasSlow = elapsedTime > this.slowQueryThresholdMillis.getValue(); + } else { + this.queryWasSlow = this.session.getProtocol().getMetricsHolder().isAbonormallyLongQuery(elapsedTime); + + this.session.getProtocol().getMetricsHolder().reportQueryTime(elapsedTime); + } + } + + if (this.queryWasSlow) { + + StringBuilder mesgBuf = new StringBuilder(48 + this.originalSql.length()); + mesgBuf.append(Messages.getString("ServerPreparedStatement.15")); + mesgBuf.append(this.session.getSlowQueryThreshold()); + mesgBuf.append(Messages.getString("ServerPreparedStatement.15a")); + mesgBuf.append(elapsedTime); + mesgBuf.append(Messages.getString("ServerPreparedStatement.16")); + + mesgBuf.append("as prepared: "); + mesgBuf.append(this.originalSql); + mesgBuf.append("\n\n with parameters bound:\n\n"); + mesgBuf.append(queryAsString); + + this.eventSink.consumeEvent(new ProfilerEventImpl(ProfilerEvent.TYPE_SLOW_QUERY, "", getCurrentCatalog(), this.session.getThreadId(), + getId(), 0, System.currentTimeMillis(), elapsedTime, this.session.getQueryTimingUnits(), null, + LogUtils.findCallingClassAndMethod(new Throwable()), mesgBuf.toString())); + } + + if (gatherPerformanceMetrics) { + this.session.registerQueryExecutionTime(elapsedTime); + } + } + + this.session.incrementNumberOfPreparedExecutes(); + + if (this.profileSQL) { + this.setEventSink(ProfilerEventHandlerFactory.getInstance(this.session)); + + this.eventSink.consumeEvent(new ProfilerEventImpl(ProfilerEvent.TYPE_EXECUTE, "", getCurrentCatalog(), this.session.getThreadId(), + this.statementId, -1, System.currentTimeMillis(), this.session.getCurrentTimeNanosOrMillis() - begin, + this.session.getQueryTimingUnits(), null, LogUtils.findCallingClassAndMethod(new Throwable()), truncateQueryToLog(queryAsString))); + } + + return resultPacket; + + } catch (CJException sqlEx) { + if (this.session.shouldIntercept()) { + this.session.invokeQueryInterceptorsPost(() -> { + return getOriginalSql(); + }, this, null, true); + } + + throw sqlEx; + } finally { + this.statementExecuting.set(false); + stopQueryTimer(timeoutTask, false, false); + } + } + + public T readExecuteResult(NativePacketPayload resultPacket, int maxRowsToRetrieve, boolean createStreamingResultSet, + ColumnDefinition metadata, ProtocolEntityFactory resultSetFactory, String queryAsString) { // TODO queryAsString should be shared instead of passed + try { + long fetchStartTime = 0; + + if (this.profileSQL || this.logSlowQueries || this.gatherPerfMetrics.getValue()) { + fetchStartTime = this.session.getCurrentTimeNanosOrMillis(); + } + + T rs = this.session.getProtocol().readAllResults(maxRowsToRetrieve, createStreamingResultSet, resultPacket, true, + metadata != null ? metadata : this.resultFields, resultSetFactory); + + if (this.session.shouldIntercept()) { + T interceptedResults = this.session.invokeQueryInterceptorsPost(() -> { + return getOriginalSql(); + }, this, rs, true); + + if (interceptedResults != null) { + rs = interceptedResults; + } + } + + if (this.profileSQL) { + long fetchEndTime = this.session.getCurrentTimeNanosOrMillis(); + + this.eventSink.consumeEvent(new ProfilerEventImpl(ProfilerEvent.TYPE_FETCH, "", getCurrentCatalog(), this.session.getThreadId(), getId(), + rs.getResultId(), System.currentTimeMillis(), (fetchEndTime - fetchStartTime), this.session.getQueryTimingUnits(), null, + LogUtils.findCallingClassAndMethod(new Throwable()), null)); + } + + if (this.queryWasSlow && this.explainSlowQueries.getValue()) { + this.session.getProtocol().explainSlowQuery(queryAsString, queryAsString); + } + + this.queryBindings.getSendTypesToServer().set(false); + //this.results = rs; + + if (this.session.hadWarnings()) { + this.session.getProtocol().scanForAndThrowDataTruncation(); + } + + return rs; + } catch (IOException ioEx) { + throw ExceptionFactory.createCommunicationsException(this.session.getPropertySet(), this.session.getServerSession(), + this.session.getProtocol().getPacketSentTimeHolder().getLastPacketSentTime(), + this.session.getProtocol().getPacketReceivedTimeHolder().getLastPacketReceivedTime(), ioEx, this.session.getExceptionInterceptor()); + } catch (CJException sqlEx) { + if (this.session.shouldIntercept()) { + this.session.invokeQueryInterceptorsPost(() -> { + return getOriginalSql(); + }, this, null, true); + } + + throw sqlEx; + } + + } + + /** + * Sends stream-type data parameters to the server. + * + *
+     *  Long data handling:
+     * 
+     *  - Server gets the long data in pieces with command type 'COM_LONG_DATA'.
+     *  - The packet received will have the format:
+     *    [COM_LONG_DATA:     1][STMT_ID:4][parameter_number:2][type:2][data]
+     *  - Checks if the type is specified by client, and if yes reads the type,
+     *    and  stores the data in that format.
+     *  - It is up to the client to check for read data ended. The server does not
+     *    care; and also server does not notify to the client that it got the
+     *    data  or not; if there is any error; then during execute; the error
+     *    will  be returned
+     * 
+ * + * @param parameterIndex + * @param longData + * + */ + private void serverLongData(int parameterIndex, ServerPreparedQueryBindValue longData) { + synchronized (this) { + NativePacketPayload packet = this.session.getSharedSendPacket(); + + Object value = longData.value; + + if (value instanceof byte[]) { + this.session.sendCommand(this.commandBuilder.buildComStmtSendLongData(packet, this.serverStatementId, parameterIndex, (byte[]) value), true, 0); + } else if (value instanceof InputStream) { + storeStream(parameterIndex, packet, (InputStream) value); + } else if (value instanceof java.sql.Blob) { + try { + storeStream(parameterIndex, packet, ((java.sql.Blob) value).getBinaryStream()); + } catch (Throwable t) { + throw ExceptionFactory.createException(t.getMessage(), this.session.getExceptionInterceptor()); + } + } else if (value instanceof Reader) { + storeReader(parameterIndex, packet, (Reader) value); + } else { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("ServerPreparedStatement.18") + value.getClass().getName() + "'", this.session.getExceptionInterceptor()); + } + } + } + + @Override + public void closeQuery() { + this.queryBindings = null; + this.parameterFields = null; + this.resultFields = null; + super.closeQuery(); + } + + public long getServerStatementId() { + return this.serverStatementId; + } + + public void setServerStatementId(long serverStatementId) { + this.serverStatementId = serverStatementId; + } + + public Field[] getParameterFields() { + return this.parameterFields; + } + + public void setParameterFields(Field[] parameterFields) { + this.parameterFields = parameterFields; + } + + public ColumnDefinition getResultFields() { + return this.resultFields; + } + + public void setResultFields(ColumnDefinition resultFields) { + this.resultFields = resultFields; + } + + public void storeStream(int parameterIndex, NativePacketPayload packet, InputStream inStream) { + this.session.checkClosed(); + synchronized (this.session) { + byte[] buf = new byte[BLOB_STREAM_READ_BUF_SIZE]; + + int numRead = 0; + + try { + int bytesInPacket = 0; + int totalBytesRead = 0; + int bytesReadAtLastSend = 0; + int packetIsFullAt = this.session.getPropertySet().getMemorySizeReadableProperty(PropertyDefinitions.PNAME_blobSendChunkSize).getValue(); + + packet.setPosition(0); + packet.writeInteger(IntegerDataType.INT1, NativeConstants.COM_STMT_SEND_LONG_DATA); + packet.writeInteger(IntegerDataType.INT4, this.serverStatementId); + packet.writeInteger(IntegerDataType.INT2, parameterIndex); + + boolean readAny = false; + + while ((numRead = inStream.read(buf)) != -1) { + + readAny = true; + + packet.writeBytes(StringLengthDataType.STRING_FIXED, buf, 0, numRead); + bytesInPacket += numRead; + totalBytesRead += numRead; + + if (bytesInPacket >= packetIsFullAt) { + bytesReadAtLastSend = totalBytesRead; + + this.session.sendCommand(packet, true, 0); + + bytesInPacket = 0; + packet.setPosition(0); + packet.writeInteger(IntegerDataType.INT1, NativeConstants.COM_STMT_SEND_LONG_DATA); + packet.writeInteger(IntegerDataType.INT4, this.serverStatementId); + packet.writeInteger(IntegerDataType.INT2, parameterIndex); + } + } + + if (totalBytesRead != bytesReadAtLastSend) { + this.session.sendCommand(packet, true, 0); + } + + if (!readAny) { + this.session.sendCommand(packet, true, 0); + } + } catch (IOException ioEx) { + throw ExceptionFactory.createException(Messages.getString("ServerPreparedStatement.25") + ioEx.toString(), ioEx, + this.session.getExceptionInterceptor()); + } finally { + if (this.autoClosePStmtStreams.getValue()) { + if (inStream != null) { + try { + inStream.close(); + } catch (IOException ioEx) { + // ignore + } + } + } + } + } + } + + // TODO: Investigate using NIO to do this faster + public void storeReader(int parameterIndex, NativePacketPayload packet, Reader inStream) { + this.session.checkClosed(); + synchronized (this.session) { + String forcedEncoding = this.session.getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_clobCharacterEncoding).getStringValue(); + + String clobEncoding = (forcedEncoding == null + ? this.session.getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_characterEncoding).getValue() : forcedEncoding); + + int maxBytesChar = 2; + + if (clobEncoding != null) { + if (!clobEncoding.equals("UTF-16")) { + maxBytesChar = this.session.getServerSession().getMaxBytesPerChar(clobEncoding); + + if (maxBytesChar == 1) { + maxBytesChar = 2; // for safety + } + } else { + maxBytesChar = 4; + } + } + + char[] buf = new char[ServerPreparedQuery.BLOB_STREAM_READ_BUF_SIZE / maxBytesChar]; + + int numRead = 0; + + int bytesInPacket = 0; + int totalBytesRead = 0; + int bytesReadAtLastSend = 0; + int packetIsFullAt = this.session.getPropertySet().getMemorySizeReadableProperty(PropertyDefinitions.PNAME_blobSendChunkSize).getValue(); + + try { + packet.setPosition(0); + packet.writeInteger(IntegerDataType.INT1, NativeConstants.COM_STMT_SEND_LONG_DATA); + packet.writeInteger(IntegerDataType.INT4, this.serverStatementId); + packet.writeInteger(IntegerDataType.INT2, parameterIndex); + + boolean readAny = false; + + while ((numRead = inStream.read(buf)) != -1) { + readAny = true; + + byte[] valueAsBytes = StringUtils.getBytes(buf, 0, numRead, clobEncoding); + + packet.writeBytes(StringSelfDataType.STRING_EOF, valueAsBytes); + + bytesInPacket += valueAsBytes.length; + totalBytesRead += valueAsBytes.length; + + if (bytesInPacket >= packetIsFullAt) { + bytesReadAtLastSend = totalBytesRead; + + this.session.sendCommand(packet, true, 0); + + bytesInPacket = 0; + packet.setPosition(0); + packet.writeInteger(IntegerDataType.INT1, NativeConstants.COM_STMT_SEND_LONG_DATA); + packet.writeInteger(IntegerDataType.INT4, this.serverStatementId); + packet.writeInteger(IntegerDataType.INT2, parameterIndex); + } + } + + if (totalBytesRead != bytesReadAtLastSend) { + this.session.sendCommand(packet, true, 0); + } + + if (!readAny) { + this.session.sendCommand(packet, true, 0); + } + } catch (IOException ioEx) { + throw ExceptionFactory.createException(Messages.getString("ServerPreparedStatement.24") + ioEx.toString(), ioEx, + this.session.getExceptionInterceptor()); + } finally { + if (this.autoClosePStmtStreams.getValue()) { + if (inStream != null) { + try { + inStream.close(); + } catch (IOException ioEx) { + // ignore + } + } + } + } + } + } + + /** + * + * @param clearServerParameters + * @return Whether or not the long parameters have been 'switched' back to normal parameters. + * We cannot execute() if clearParameters() has not been called in this case. + */ + public void clearParameters(boolean clearServerParameters) { + boolean hadLongData = false; + + if (this.queryBindings != null) { + hadLongData = this.queryBindings.clearBindValues(); + this.queryBindings.setLongParameterSwitchDetected(clearServerParameters && hadLongData ? false : true); + } + + if (clearServerParameters && hadLongData) { + serverResetStatement(); + } + } + + public void serverResetStatement() { + this.session.checkClosed(); + synchronized (this.session) { + try { + this.session.sendCommand(this.commandBuilder.buildComStmtReset(this.session.getSharedSendPacket(), this.serverStatementId), false, 0); + } finally { + this.session.clearInputStream(); + } + } + } + + /** + * Computes the maximum parameter set size and the size of the entire batch given + * the number of arguments in the batch. + */ + @Override + protected long[] computeMaxParameterSetSizeAndBatchSize(int numBatchedArgs) { + + long sizeOfEntireBatch = 1 + /* com_execute */+4 /* stmt id */ + 1 /* flags */ + 4 /* batch count padding */; + long maxSizeOfParameterSet = 0; + + for (int i = 0; i < numBatchedArgs; i++) { + ServerPreparedQueryBindValue[] paramArg = ((ServerPreparedQueryBindings) this.batchedArgs.get(i)).getBindValues(); + + long sizeOfParameterSet = (this.parameterCount + 7) / 8; // for isNull + sizeOfParameterSet += this.parameterCount * 2; // have to send types + + ServerPreparedQueryBindValue[] parameterBindings = this.queryBindings.getBindValues(); + for (int j = 0; j < parameterBindings.length; j++) { + if (!paramArg[j].isNull()) { + + long size = paramArg[j].getBoundLength(); + + if (paramArg[j].isLongData) { + if (size != -1) { + sizeOfParameterSet += size; + } + } else { + sizeOfParameterSet += size; + } + } + } + + sizeOfEntireBatch += sizeOfParameterSet; + + if (sizeOfParameterSet > maxSizeOfParameterSet) { + maxSizeOfParameterSet = sizeOfParameterSet; + } + } + + return new long[] { maxSizeOfParameterSet, sizeOfEntireBatch }; + } + + private String truncateQueryToLog(String sql) { + String queryStr = null; + + int maxQuerySizeToLog = this.session.getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_maxQuerySizeToLog).getValue(); + if (sql.length() > maxQuerySizeToLog) { + StringBuilder queryBuf = new StringBuilder(maxQuerySizeToLog + 12); + queryBuf.append(sql.substring(0, maxQuerySizeToLog)); + queryBuf.append(Messages.getString("MysqlIO.25")); + + queryStr = queryBuf.toString(); + } else { + queryStr = sql; + } + + return queryStr; + } + + @Override + public M fillSendPacket() { + return null; // we don't use this type of packet + } + + @Override + public M fillSendPacket(QueryBindings bindings) { + return null; // we don't use this type of packet + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/ServerPreparedQueryBindValue.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/ServerPreparedQueryBindValue.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/ServerPreparedQueryBindValue.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.util.Calendar; +import java.util.TimeZone; + +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.protocol.a.NativeConstants.IntegerDataType; +import com.mysql.cj.protocol.a.NativeConstants.StringSelfDataType; +import com.mysql.cj.protocol.a.NativePacketPayload; +import com.mysql.cj.util.StringUtils; + +//TODO should not be protocol-specific + +public class ServerPreparedQueryBindValue extends ClientPreparedQueryBindValue implements BindValue { + + public long boundBeforeExecutionNum = 0; + + /** Default length of data */ + public long bindLength; + + public int bufferType; + + public double doubleBinding; + + public float floatBinding; + + public boolean isLongData; + + /** has this parameter been set? */ + private boolean isSet = false; + + /** all integral values are stored here */ + public long longBinding; + + /** The value to store */ + public Object value; + + /** The TimeZone for date/time types */ + public TimeZone tz; + + public ServerPreparedQueryBindValue() { + } + + @Override + public ServerPreparedQueryBindValue clone() { + return new ServerPreparedQueryBindValue(this); + } + + private ServerPreparedQueryBindValue(ServerPreparedQueryBindValue copyMe) { + super(copyMe); + + this.value = copyMe.value; + this.isSet = copyMe.isSet; + this.isLongData = copyMe.isLongData; + this.bufferType = copyMe.bufferType; + this.bindLength = copyMe.bindLength; + this.longBinding = copyMe.longBinding; + this.floatBinding = copyMe.floatBinding; + this.doubleBinding = copyMe.doubleBinding; + this.tz = copyMe.tz; + } + + @Override + public void reset() { + super.reset(); + + this.isSet = false; + this.value = null; + this.isLongData = false; + + this.longBinding = 0L; + this.floatBinding = 0; + this.doubleBinding = 0D; + this.tz = null; + } + + /** + * Reset a bind value to be used for a new value of the given type. + * + * @param bufType + * @param numberOfExecutions + * @return true if we need to send/resend types to the server + */ + public boolean resetToType(int bufType, long numberOfExecutions) { + boolean sendTypesToServer = false; + + // clear any possible old value + reset(); + + if (bufType == MysqlType.FIELD_TYPE_NULL && this.bufferType != 0) { + // preserve the previous type to (possibly) avoid sending types at execution time + } else if (this.bufferType != bufType) { + sendTypesToServer = true; + this.bufferType = bufType; + } + + // setup bind value for use + this.isSet = true; + this.boundBeforeExecutionNum = numberOfExecutions; + return sendTypesToServer; + } + + @Override + public String toString() { + return toString(false); + } + + public String toString(boolean quoteIfNeeded) { + if (this.isLongData) { + return "' STREAM DATA '"; + } + + if (this.isNull) { + return "NULL"; + } + + switch (this.bufferType) { + case MysqlType.FIELD_TYPE_TINY: + case MysqlType.FIELD_TYPE_SHORT: + case MysqlType.FIELD_TYPE_LONG: + case MysqlType.FIELD_TYPE_LONGLONG: + return String.valueOf(this.longBinding); + case MysqlType.FIELD_TYPE_FLOAT: + return String.valueOf(this.floatBinding); + case MysqlType.FIELD_TYPE_DOUBLE: + return String.valueOf(this.doubleBinding); + case MysqlType.FIELD_TYPE_TIME: + case MysqlType.FIELD_TYPE_DATE: + case MysqlType.FIELD_TYPE_DATETIME: + case MysqlType.FIELD_TYPE_TIMESTAMP: + case MysqlType.FIELD_TYPE_VAR_STRING: + case MysqlType.FIELD_TYPE_STRING: + case MysqlType.FIELD_TYPE_VARCHAR: + if (quoteIfNeeded) { + return "'" + String.valueOf(this.value) + "'"; + } + return String.valueOf(this.value); + + default: + if (this.value instanceof byte[]) { + return "byte data"; + } + if (quoteIfNeeded) { + return "'" + String.valueOf(this.value) + "'"; + } + return String.valueOf(this.value); + } + } + + public long getBoundLength() { + if (this.isNull) { + return 0; + } + + if (this.isLongData) { + return this.bindLength; + } + + switch (this.bufferType) { + + case MysqlType.FIELD_TYPE_TINY: + return 1; + case MysqlType.FIELD_TYPE_SHORT: + return 2; + case MysqlType.FIELD_TYPE_LONG: + return 4; + case MysqlType.FIELD_TYPE_LONGLONG: + return 8; + case MysqlType.FIELD_TYPE_FLOAT: + return 4; + case MysqlType.FIELD_TYPE_DOUBLE: + return 8; + case MysqlType.FIELD_TYPE_TIME: + return 9; + case MysqlType.FIELD_TYPE_DATE: + return 7; + case MysqlType.FIELD_TYPE_DATETIME: + case MysqlType.FIELD_TYPE_TIMESTAMP: + return 11; + case MysqlType.FIELD_TYPE_VAR_STRING: + case MysqlType.FIELD_TYPE_STRING: + case MysqlType.FIELD_TYPE_VARCHAR: + case MysqlType.FIELD_TYPE_DECIMAL: + case MysqlType.FIELD_TYPE_NEWDECIMAL: + if (this.value instanceof byte[]) { + return ((byte[]) this.value).length; + } + return ((String) this.value).length(); + + default: + return 0; + } + } + + @Override + public boolean isSet() { + return this.isSet; + } + + public void storeBinding(NativePacketPayload intoPacket, boolean isLoadDataQuery, String characterEncoding, ExceptionInterceptor interceptor) { + synchronized (this) { + try { + // Handle primitives first + switch (this.bufferType) { + + case MysqlType.FIELD_TYPE_TINY: + intoPacket.writeInteger(IntegerDataType.INT1, this.longBinding); + return; + case MysqlType.FIELD_TYPE_SHORT: + intoPacket.writeInteger(IntegerDataType.INT2, this.longBinding); + return; + case MysqlType.FIELD_TYPE_LONG: + intoPacket.writeInteger(IntegerDataType.INT4, this.longBinding); + return; + case MysqlType.FIELD_TYPE_LONGLONG: + intoPacket.writeInteger(IntegerDataType.INT8, this.longBinding); + return; + case MysqlType.FIELD_TYPE_FLOAT: + intoPacket.writeInteger(IntegerDataType.INT4, Float.floatToIntBits(this.floatBinding)); + return; + case MysqlType.FIELD_TYPE_DOUBLE: + intoPacket.writeInteger(IntegerDataType.INT8, Double.doubleToLongBits(this.doubleBinding)); + return; + case MysqlType.FIELD_TYPE_TIME: + storeTime(intoPacket); + return; + case MysqlType.FIELD_TYPE_DATE: + case MysqlType.FIELD_TYPE_DATETIME: + case MysqlType.FIELD_TYPE_TIMESTAMP: + storeDateTime(intoPacket); + return; + case MysqlType.FIELD_TYPE_VAR_STRING: + case MysqlType.FIELD_TYPE_STRING: + case MysqlType.FIELD_TYPE_VARCHAR: + case MysqlType.FIELD_TYPE_DECIMAL: + case MysqlType.FIELD_TYPE_NEWDECIMAL: + if (this.value instanceof byte[]) { + intoPacket.writeBytes(StringSelfDataType.STRING_LENENC, (byte[]) this.value); + } else if (!isLoadDataQuery) { + intoPacket.writeBytes(StringSelfDataType.STRING_LENENC, StringUtils.getBytes((String) this.value, characterEncoding)); + } else { + intoPacket.writeBytes(StringSelfDataType.STRING_LENENC, StringUtils.getBytes((String) this.value)); + } + + return; + } + + } catch (CJException uEE) { + throw ExceptionFactory.createException(Messages.getString("ServerPreparedStatement.22") + characterEncoding + "'", uEE, interceptor); + } + } + } + + private void storeTime(NativePacketPayload intoPacket) { + + intoPacket.ensureCapacity(9); + intoPacket.writeInteger(IntegerDataType.INT1, 8); // length + intoPacket.writeInteger(IntegerDataType.INT1, 0); // neg flag + intoPacket.writeInteger(IntegerDataType.INT4, 0); // tm->day, not used + + Calendar cal = Calendar.getInstance(this.tz); + + cal.setTime((java.util.Date) this.value); + intoPacket.writeInteger(IntegerDataType.INT1, cal.get(Calendar.HOUR_OF_DAY)); + intoPacket.writeInteger(IntegerDataType.INT1, cal.get(Calendar.MINUTE)); + intoPacket.writeInteger(IntegerDataType.INT1, cal.get(Calendar.SECOND)); + } + + /** + * @param intoPacket + * @param dt + * @param mysql + */ + private void storeDateTime(NativePacketPayload intoPacket) { + synchronized (this) { + Calendar cal = Calendar.getInstance(this.tz); + + cal.setTime((java.util.Date) this.value); + + if (this.value instanceof java.sql.Date) { + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + } + + byte length = (byte) 7; + + if (this.value instanceof java.sql.Timestamp) { + length = (byte) 11; + } + + intoPacket.ensureCapacity(length); + + intoPacket.writeInteger(IntegerDataType.INT1, length); // length + + int year = cal.get(Calendar.YEAR); + int month = cal.get(Calendar.MONTH) + 1; + int date = cal.get(Calendar.DAY_OF_MONTH); + + intoPacket.writeInteger(IntegerDataType.INT2, year); + intoPacket.writeInteger(IntegerDataType.INT1, month); + intoPacket.writeInteger(IntegerDataType.INT1, date); + + if (this.value instanceof java.sql.Date) { + intoPacket.writeInteger(IntegerDataType.INT1, 0); + intoPacket.writeInteger(IntegerDataType.INT1, 0); + intoPacket.writeInteger(IntegerDataType.INT1, 0); + } else { + intoPacket.writeInteger(IntegerDataType.INT1, cal.get(Calendar.HOUR_OF_DAY)); + intoPacket.writeInteger(IntegerDataType.INT1, cal.get(Calendar.MINUTE)); + intoPacket.writeInteger(IntegerDataType.INT1, cal.get(Calendar.SECOND)); + } + + if (length == 11) { + // MySQL expects microseconds, not nanos + intoPacket.writeInteger(IntegerDataType.INT4, ((java.sql.Timestamp) this.value).getNanos() / 1000); + } + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/ServerPreparedQueryBindings.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/ServerPreparedQueryBindings.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/ServerPreparedQueryBindings.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.TimeZone; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.util.StringUtils; +import com.mysql.cj.util.TimeUtil; + +//TODO should not be protocol-specific + +public class ServerPreparedQueryBindings extends AbstractQueryBindings { + + /** Do we need to send/resend types to the server? */ + private AtomicBoolean sendTypesToServer = new AtomicBoolean(false); + + /** + * Flag indicating whether or not the long parameters have been 'switched' back to normal parameters. + * We cannot execute() if clearParameters() has not been called in this case. + */ + private boolean longParameterSwitchDetected = false; + + public ServerPreparedQueryBindings(int parameterCount, Session sess) { + super(parameterCount, sess); + } + + @Override + protected void initBindValues(int parameterCount) { + this.bindValues = new ServerPreparedQueryBindValue[parameterCount]; + for (int i = 0; i < parameterCount; i++) { + this.bindValues[i] = new ServerPreparedQueryBindValue(); + } + } + + @Override + public ServerPreparedQueryBindings clone() { + ServerPreparedQueryBindings newBindings = new ServerPreparedQueryBindings(this.bindValues.length, this.session); + ServerPreparedQueryBindValue[] bvs = new ServerPreparedQueryBindValue[this.bindValues.length]; + for (int i = 0; i < this.bindValues.length; i++) { + bvs[i] = this.bindValues[i].clone(); + } + newBindings.bindValues = bvs; + newBindings.sendTypesToServer = this.sendTypesToServer; + newBindings.longParameterSwitchDetected = this.longParameterSwitchDetected; + newBindings.isLoadDataQuery = this.isLoadDataQuery; + return newBindings; + } + + /** + * Returns the structure representing the value that (can be)/(is) + * bound at the given parameter index. + * + * @param parameterIndex + * 0-based + * @param forLongData + * is this for a stream? + */ + public ServerPreparedQueryBindValue getBinding(int parameterIndex, boolean forLongData) { + + if (this.bindValues[parameterIndex] == null) { + // this.bindValues[parameterIndex] = new ServerPreparedQueryBindValue(); + } else { + if (this.bindValues[parameterIndex].isLongData && !forLongData) { + this.longParameterSwitchDetected = true; + } + } + + return this.bindValues[parameterIndex]; + } + + @Override + public void checkParameterSet(int columnIndex) { + if (!this.bindValues[columnIndex].isSet()) { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("ServerPreparedStatement.13") + (columnIndex + 1) + Messages.getString("ServerPreparedStatement.14")); + } + } + + public AtomicBoolean getSendTypesToServer() { + return this.sendTypesToServer; + } + + public boolean isLongParameterSwitchDetected() { + return this.longParameterSwitchDetected; + } + + public void setLongParameterSwitchDetected(boolean longParameterSwitchDetected) { + this.longParameterSwitchDetected = longParameterSwitchDetected; + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x) { + setAsciiStream(parameterIndex, x, -1); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) { + if (x == null) { + setNull(parameterIndex); + } else { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, true); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_BLOB, this.numberOfExecutions)); + binding.value = x; + binding.isLongData = true; + binding.bindLength = this.useStreamLengthsInPrepStmts.getValue() ? length : -1; + } + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, long length) { + setAsciiStream(parameterIndex, x, (int) length); + this.bindValues[parameterIndex].setMysqlType(MysqlType.TEXT); // TODO was Types.CLOB, check; use length to find right TEXT type + } + + @Override + public void setBigDecimal(int parameterIndex, BigDecimal x) { + if (x == null) { + setNull(parameterIndex); + } else { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_NEWDECIMAL, this.numberOfExecutions)); + binding.value = StringUtils.fixDecimalExponent(x.toPlainString()); + } + } + + @Override + public void setBigInteger(int parameterIndex, BigInteger x) { + setValue(parameterIndex, x.toString(), MysqlType.BIGINT_UNSIGNED); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x) { + setBinaryStream(parameterIndex, x, -1); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) { + if (x == null) { + setNull(parameterIndex); + } else { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, true); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_BLOB, this.numberOfExecutions)); + binding.value = x; + binding.isLongData = true; + binding.bindLength = this.useStreamLengthsInPrepStmts.getValue() ? length : -1; + } + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, long length) { + setBinaryStream(parameterIndex, x, (int) length); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream) { + setBinaryStream(parameterIndex, inputStream); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream, long length) { + setBinaryStream(parameterIndex, inputStream, (int) length); + } + + @Override + public void setBlob(int parameterIndex, Blob x) { + + if (x == null) { + setNull(parameterIndex); + } else { + try { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, true); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_BLOB, this.numberOfExecutions)); + binding.value = x; + binding.isLongData = true; + binding.bindLength = this.useStreamLengthsInPrepStmts.getValue() ? x.length() : -1; + } catch (Throwable t) { + throw ExceptionFactory.createException(t.getMessage(), t); + } + } + } + + @Override + public void setBoolean(int parameterIndex, boolean x) { + setByte(parameterIndex, (x ? (byte) 1 : (byte) 0)); + } + + @Override + public void setByte(int parameterIndex, byte x) { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_TINY, this.numberOfExecutions)); + binding.longBinding = x; + } + + @Override + public void setBytes(int parameterIndex, byte[] x) { + if (x == null) { + setNull(parameterIndex); + } else { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_VAR_STRING, this.numberOfExecutions)); + binding.value = x; + } + } + + @Override + public void setBytes(int parameterIndex, byte[] x, boolean checkForIntroducer, boolean escapeForMBChars) { + setBytes(parameterIndex, x); + } + + @Override + public void setBytesNoEscape(int parameterIndex, byte[] parameterAsBytes) { + byte[] parameterWithQuotes = new byte[parameterAsBytes.length + 2]; + parameterWithQuotes[0] = '\''; + System.arraycopy(parameterAsBytes, 0, parameterWithQuotes, 1, parameterAsBytes.length); + parameterWithQuotes[parameterAsBytes.length + 1] = '\''; + + setValue(parameterIndex, parameterWithQuotes); + } + + @Override + public void setBytesNoEscapeNoQuotes(int parameterIndex, byte[] parameterAsBytes) { + setBytes(parameterIndex, parameterAsBytes); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader) { + setCharacterStream(parameterIndex, reader, -1); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) { + if (reader == null) { + setNull(parameterIndex); + } else { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, true); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_BLOB, this.numberOfExecutions)); + binding.value = reader; + binding.isLongData = true; + binding.bindLength = this.useStreamLengthsInPrepStmts.getValue() ? length : -1; + } + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, long length) { + setCharacterStream(parameterIndex, reader, (int) length); + } + + @Override + public void setClob(int parameterIndex, Reader reader) { + setCharacterStream(parameterIndex, reader); + } + + @Override + public void setClob(int parameterIndex, Reader reader, long length) { + setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setClob(int parameterIndex, Clob x) { + if (x == null) { + setNull(parameterIndex); + } else { + try { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, true); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_BLOB, this.numberOfExecutions)); + binding.value = x.getCharacterStream(); + binding.isLongData = true; + binding.bindLength = this.useStreamLengthsInPrepStmts.getValue() ? x.length() : -1; + } catch (Throwable t) { + throw ExceptionFactory.createException(t.getMessage(), t); + } + } + } + + @Override + public void setDate(int parameterIndex, Date x) { + setDate(parameterIndex, x, this.session.getServerSession().getDefaultTimeZone()); + } + + @Override + public void setDate(int parameterIndex, Date x, Calendar cal) { + setDate(parameterIndex, x, cal.getTimeZone()); + } + + @Override + public void setDate(int parameterIndex, Date x, TimeZone tz) { + if (x == null) { + setNull(parameterIndex); + } else { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_DATE, this.numberOfExecutions)); + binding.value = x; + binding.tz = tz; + } + } + + @Override + public void setDouble(int parameterIndex, double x) { + if (!this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_allowNanAndInf).getValue() + && (x == Double.POSITIVE_INFINITY || x == Double.NEGATIVE_INFINITY || Double.isNaN(x))) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("PreparedStatement.64", new Object[] { x }), + this.session.getExceptionInterceptor()); + } + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_DOUBLE, this.numberOfExecutions)); + binding.doubleBinding = x; + } + + @Override + public void setFloat(int parameterIndex, float x) { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_FLOAT, this.numberOfExecutions)); + binding.floatBinding = x; + } + + @Override + public void setInt(int parameterIndex, int x) { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_LONG, this.numberOfExecutions)); + binding.longBinding = x; + } + + @Override + public void setLong(int parameterIndex, long x) { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_LONGLONG, this.numberOfExecutions)); + binding.longBinding = x; + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value) { + setNCharacterStream(parameterIndex, value, -1); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader reader, long length) { + if (!this.charEncoding.equalsIgnoreCase("UTF-8") && !this.charEncoding.equalsIgnoreCase("utf8")) { + throw ExceptionFactory.createException(Messages.getString("ServerPreparedStatement.28"), this.session.getExceptionInterceptor()); + } + + if (reader == null) { + setNull(parameterIndex); + } else { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, true); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_BLOB, this.numberOfExecutions)); + binding.value = reader; + binding.isLongData = true; + binding.bindLength = this.useStreamLengthsInPrepStmts.getValue() ? length : -1; + } + } + + @Override + public void setNClob(int parameterIndex, Reader reader) { + setNCharacterStream(parameterIndex, reader); + } + + @Override + public void setNClob(int parameterIndex, Reader reader, long length) { + if (!this.charEncoding.equalsIgnoreCase("UTF-8") && !this.charEncoding.equalsIgnoreCase("utf8")) { + throw ExceptionFactory.createException(Messages.getString("ServerPreparedStatement.29"), this.session.getExceptionInterceptor()); + } + setNCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setNClob(int parameterIndex, NClob value) { + try { + setNClob(parameterIndex, value.getCharacterStream(), this.useStreamLengthsInPrepStmts.getValue() ? value.length() : -1); + } catch (Throwable t) { + throw ExceptionFactory.createException(t.getMessage(), t, this.session.getExceptionInterceptor()); + } + } + + @Override + public void setNString(int parameterIndex, String x) { + if (this.charEncoding.equalsIgnoreCase("UTF-8") || this.charEncoding.equalsIgnoreCase("utf8")) { + setString(parameterIndex, x); + } else { + throw ExceptionFactory.createException(Messages.getString("ServerPreparedStatement.30"), this.session.getExceptionInterceptor()); + } + } + + @Override + public void setNull(int parameterIndex) { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_NULL, this.numberOfExecutions)); + binding.setNull(true); + } + + @Override + public void setShort(int parameterIndex, short x) { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_SHORT, this.numberOfExecutions)); + binding.longBinding = x; + } + + @Override + public void setString(int parameterIndex, String x) { + if (x == null) { + setNull(parameterIndex); + } else { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_VAR_STRING, this.numberOfExecutions)); + binding.value = x; + } + } + + public void setTime(int parameterIndex, Time x, Calendar cal) { + setTime(parameterIndex, x, cal.getTimeZone()); + } + + public void setTime(int parameterIndex, Time x) { + setTime(parameterIndex, x, this.session.getServerSession().getDefaultTimeZone()); + } + + @Override + public void setTime(int parameterIndex, Time x, TimeZone tz) { + if (x == null) { + setNull(parameterIndex); + } else { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_TIME, this.numberOfExecutions)); + binding.value = x; + binding.tz = tz; + } + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x) { + setTimestamp(parameterIndex, x, this.session.getServerSession().getDefaultTimeZone()); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) { + setTimestamp(parameterIndex, x, cal.getTimeZone()); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x, TimeZone tz) { + if (x == null) { + setNull(parameterIndex); + } else { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_DATETIME, this.numberOfExecutions)); + + if (!this.sendFractionalSeconds.getValue()) { + x = TimeUtil.truncateFractionalSeconds(x); + } + + binding.value = x; + binding.tz = tz; + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/ServerPreparedQueryTestcaseGenerator.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/ServerPreparedQueryTestcaseGenerator.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/ServerPreparedQueryTestcaseGenerator.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.io.IOException; + +import com.mysql.cj.protocol.ColumnDefinition; +import com.mysql.cj.protocol.ProtocolEntityFactory; +import com.mysql.cj.protocol.Resultset; +import com.mysql.cj.protocol.a.NativePacketPayload; +import com.mysql.cj.util.TestUtils; + +//TODO should not be protocol-specific + +public class ServerPreparedQueryTestcaseGenerator extends ServerPreparedQuery { + + public ServerPreparedQueryTestcaseGenerator(NativeSession sess) { + super(sess); + } + + @Override + public void closeQuery() { + dumpCloseForTestcase(); + super.closeQuery(); + } + + private void dumpCloseForTestcase() { + StringBuilder buf = new StringBuilder(); + this.session.getProtocol().generateQueryCommentBlock(buf); + buf.append("DEALLOCATE PREPARE debug_stmt_"); + buf.append(this.statementId); + buf.append(";\n"); + + TestUtils.dumpTestcaseQuery(buf.toString()); + } + + @Override + public void serverPrepare(String sql) throws IOException { + dumpPrepareForTestcase(); + super.serverPrepare(sql); + } + + private void dumpPrepareForTestcase() { + StringBuilder buf = new StringBuilder(this.getOriginalSql().length() + 64); + + this.session.getProtocol().generateQueryCommentBlock(buf); + + buf.append("PREPARE debug_stmt_"); + buf.append(this.statementId); + buf.append(" FROM \""); + buf.append(this.getOriginalSql()); + buf.append("\";\n"); + + TestUtils.dumpTestcaseQuery(buf.toString()); + } + + @Override + public T serverExecute(int maxRowsToRetrieve, boolean createStreamingResultSet, ColumnDefinition metadata, + ProtocolEntityFactory resultSetFactory) { + dumpExecuteForTestcase(); + return super.serverExecute(maxRowsToRetrieve, createStreamingResultSet, metadata, resultSetFactory); + } + + private void dumpExecuteForTestcase() { + StringBuilder buf = new StringBuilder(); + + for (int i = 0; i < this.getParameterCount(); i++) { + this.session.getProtocol().generateQueryCommentBlock(buf); + + buf.append("SET @debug_stmt_param"); + buf.append(this.statementId); + buf.append("_"); + buf.append(i); + buf.append("="); + + ServerPreparedQueryBindValue bv = this.queryBindings.getBindValues()[i]; + buf.append(bv.isNull() ? "NULL" : bv.toString(true)); + + buf.append(";\n"); + } + + this.session.getProtocol().generateQueryCommentBlock(buf); + + buf.append("EXECUTE debug_stmt_"); + buf.append(this.statementId); + + if (this.getParameterCount() > 0) { + buf.append(" USING "); + for (int i = 0; i < this.getParameterCount(); i++) { + if (i > 0) { + buf.append(", "); + } + + buf.append("@debug_stmt_param"); + buf.append(this.statementId); + buf.append("_"); + buf.append(i); + + } + } + + buf.append(";\n"); + + TestUtils.dumpTestcaseQuery(buf.toString()); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/ServerVersion.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/ServerVersion.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/ServerVersion.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +/** + * A server version. + */ +public class ServerVersion implements Comparable { + private String completeVersion; + private Integer major; + private Integer minor; + private Integer subminor; + + public ServerVersion(String completeVersion, int major, int minor, int subminor) { + this.completeVersion = completeVersion; + this.major = major; + this.minor = minor; + this.subminor = subminor; + } + + public ServerVersion(int major, int minor, int subminor) { + this(null, major, minor, subminor); + } + + public int getMajor() { + return this.major; + } + + public int getMinor() { + return this.minor; + } + + public int getSubminor() { + return this.subminor; + } + + /** + * A string representation of this version. If this version was parsed from, or provided with, a "complete" string which may contain more than just the + * version number, this string is returned verbatim. Otherwise, a string representation of the version numbers is given. + * + * @return string version representation + */ + @Override + public String toString() { + if (this.completeVersion != null) { + return this.completeVersion; + } + return String.format("%d.%d.%d", this.major, this.minor, this.subminor); + } + + public int compareTo(ServerVersion other) { + int c; + if ((c = this.major.compareTo(other.getMajor())) != 0) { + return c; + } else if ((c = this.minor.compareTo(other.getMinor())) != 0) { + return c; + } + return this.subminor.compareTo(other.getSubminor()); + } + + /** + * Does this version meet the minimum specified by `min'? + * + * @param min + * The minimum version to compare against. + * @return true if version meets the minimum specified by `min' + */ + public boolean meetsMinimum(ServerVersion min) { + return compareTo(min) >= 0; + } + + /** + * Parse the server version into major/minor/subminor. + * + * @param versionString + * string version representation + * @return {@link ServerVersion} + */ + public static ServerVersion parseVersion(final String versionString) { + int point = versionString.indexOf('.'); + + if (point != -1) { + try { + int serverMajorVersion = Integer.parseInt(versionString.substring(0, point)); + + String remaining = versionString.substring(point + 1, versionString.length()); + point = remaining.indexOf('.'); + + if (point != -1) { + int serverMinorVersion = Integer.parseInt(remaining.substring(0, point)); + + remaining = remaining.substring(point + 1, remaining.length()); + + int pos = 0; + + while (pos < remaining.length()) { + if ((remaining.charAt(pos) < '0') || (remaining.charAt(pos) > '9')) { + break; + } + + pos++; + } + + int serverSubminorVersion = Integer.parseInt(remaining.substring(0, pos)); + + return new ServerVersion(versionString, serverMajorVersion, serverMinorVersion, serverSubminorVersion); + } + } catch (NumberFormatException NFE1) { + } + } + + // can't parse the server version + return new ServerVersion(0, 0, 0); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/Session.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/Session.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/Session.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +import java.net.SocketAddress; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collector; + +import com.mysql.cj.conf.PropertySet; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.log.Log; +import com.mysql.cj.log.ProfilerEventHandler; +import com.mysql.cj.protocol.Message; +import com.mysql.cj.protocol.Protocol; +import com.mysql.cj.protocol.ServerSession; +import com.mysql.cj.result.Row; + +/** + * {@link Session} exposes logical level which user API uses internally to call {@link Protocol} methods. + * It's a higher-level abstraction than MySQL server session ({@link ServerSession}). {@link Protocol} and {@link ServerSession} methods + * should never be used directly from user API. + * + */ +public interface Session { + + PropertySet getPropertySet(); + + MessageBuilder getMessageBuilder(); + + /** + * Re-authenticates as the given user and password + * + * @param userName + * DB user name + * @param password + * DB user password + * @param database + * database name + * + */ + void changeUser(String userName, String password, String database); + + ExceptionInterceptor getExceptionInterceptor(); + + void setExceptionInterceptor(ExceptionInterceptor exceptionInterceptor); + + /** + * Log-off of the MySQL server and close the socket. + * + */ + void quit(); + + /** + * Clobbers the physical network connection and marks this session as closed. + */ + void forceClose(); + + /** + * Does the version of the MySQL server we are connected to meet the given + * minimums? + * + * @param major + * major version number + * @param minor + * minor version number + * @param subminor + * sub-minor version number + * @return true if current server version equal or higher than provided one + */ + boolean versionMeetsMinimum(int major, int minor, int subminor); + + long getThreadId(); + + boolean isSetNeededForAutoCommitMode(boolean autoCommitFlag); + + Log getLog(); + + ProfilerEventHandler getProfilerEventHandler(); + + void setProfilerEventHandler(ProfilerEventHandler h); + + ServerSession getServerSession(); + + boolean isSSLEstablished(); + + SocketAddress getRemoteSocketAddress(); + + String getProcessHost(); + + /** + * Add listener for this session status changes. + * + * @param l + * {@link SessionEventListener} instance. + */ + void addListener(SessionEventListener l); + + /** + * Remove session listener. + * + * @param l + * {@link SessionEventListener} instance. + */ + void removeListener(SessionEventListener l); + + public static interface SessionEventListener { + void handleNormalClose(); + + void handleReconnect(); + + void handleCleanup(Throwable whyCleanedUp); + } + + boolean isClosed(); + + String getIdentifierQuoteString(); + + DataStoreMetadata getDataStoreMetadata(); + + RES_T query(M message, Predicate filterRow, Function mapRow, Collector collector); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/SimpleQuery.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/SimpleQuery.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/SimpleQuery.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +//TODO should not be protocol-specific + +public class SimpleQuery extends AbstractQuery { + + public SimpleQuery(NativeSession sess) { + super(sess); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/TransactionEventHandler.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/TransactionEventHandler.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/TransactionEventHandler.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +public interface TransactionEventHandler { + + void transactionBegun(); + + void transactionCompleted(); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/WarningListener.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/WarningListener.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/WarningListener.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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; + +/** + * A warning listener is notified of warnings as they happen throughout the driver. They can be queued for consumption by JDBC clients, thrown as exceptions, or + * ignored. + */ +public interface WarningListener { + void warningEncountered(String warning); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/admin/ServerController.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/admin/ServerController.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/admin/ServerController.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.admin; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; +import java.util.Properties; + +import com.mysql.cj.Constants; +import com.mysql.cj.util.StringUtils; + +/** + * Controls a MySQL server using Java RunTime methods + */ +public class ServerController { + + /** + * Where is the server installed? + */ + public static final String BASEDIR_KEY = "basedir"; + + /** + * Where are the databases installed? + */ + public static final String DATADIR_KEY = "datadir"; + + /** + * Where is the config file located? + */ + + public static final String DEFAULTS_FILE_KEY = "defaults-file"; + + /** + * What is the name of the executable to run? + */ + + public static final String EXECUTABLE_NAME_KEY = "executable"; + + /** + * What is the path to the mysql server executable (if not standard?) + */ + + public static final String EXECUTABLE_PATH_KEY = "executablePath"; + + /** + * The default executable to run + */ + + /** + * The process representing the MySQL server + */ + private Process serverProcess = null; + + /** + * The list of properties for this server + */ + private Properties serverProps = null; + + /** + * The system properties + */ + //private Properties systemProps = null; + + /** + * Creates a ServerController with the directory for the MySQL server. + * + * The 'datadir' is set to the same directory. + * + * @param baseDir + * the base directory for the MySQL server. + */ + public ServerController(String baseDir) { + setBaseDir(baseDir); + } + + /** + * Creates a server controller for the MySQL server with the given basedir + * and datadir. + * + * @param basedir + * the basedir to use when starting MySQL. + * @param datadir + * the datadir to use when starting MySQL. + */ + public ServerController(String basedir, String datadir) { + } + + /** + * Sets the basedir to use when starting MySQL. + * + * @param baseDir + * the basedir to use when starting MySQL. + */ + public void setBaseDir(String baseDir) { + getServerProps().setProperty(BASEDIR_KEY, baseDir); + } + + /** + * Sets the data to use when starting MySQL. + * + * @param dataDir + * the basedir to use when starting MySQL. + */ + public void setDataDir(String dataDir) { + getServerProps().setProperty(DATADIR_KEY, dataDir); + } + + /** + * Starts the server, returning a java.lang.Process instance that represents + * the mysql server. + * + * @return Process a java.lang.Process instance representing the mysql + * server process. + * @throws IOException + * if an error occurs while starting the mysql server. + */ + public Process start() throws IOException { + if (this.serverProcess != null) { + throw new IllegalArgumentException("Server already started"); + } + this.serverProcess = Runtime.getRuntime().exec(getCommandLine()); + + return this.serverProcess; + } + + /** + * Stops the server (if started) + * + * @param forceIfNecessary + * use forceStop if mysqladmin doesn't shut the server down + * + * @throws IOException + * if an error occurs while stopping the server + */ + public void stop(boolean forceIfNecessary) throws IOException { + if (this.serverProcess != null) { + + String basedir = getServerProps().getProperty(BASEDIR_KEY); + + StringBuilder pathBuf = new StringBuilder(basedir); + + if (!basedir.endsWith(File.separator)) { + pathBuf.append(File.separator); + } + + //String defaultsFilePath = getServerProps().getProperty(DEFAULTS_FILE_KEY); + + pathBuf.append("bin"); + pathBuf.append(File.separator); + pathBuf.append("mysqladmin shutdown"); + + System.out.println(pathBuf.toString()); + + Process mysqladmin = Runtime.getRuntime().exec(pathBuf.toString()); + + int exitStatus = -1; + + try { + exitStatus = mysqladmin.waitFor(); + } catch (InterruptedException ie) { + // ignore + } + + // + // Terminate the process if mysqladmin couldn't do it, and the user requested a force stop. + // + if (exitStatus != 0 && forceIfNecessary) { + forceStop(); + } + } + } + + /** + * Forcefully terminates the server process (if started). + */ + public void forceStop() { + if (this.serverProcess != null) { + this.serverProcess.destroy(); + this.serverProcess = null; + } + } + + /** + * Returns the list of properties that will be used to start/control the + * server. + * + * @return Properties the list of properties. + */ + public synchronized Properties getServerProps() { + if (this.serverProps == null) { + this.serverProps = new Properties(); + } + + return this.serverProps; + } + + /** + * Returns the full commandline used to start the mysql server, including + * and arguments to be passed to the server process. + * + * @return String the commandline used to start the mysql server. + */ + private String getCommandLine() { + StringBuilder commandLine = new StringBuilder(getFullExecutablePath()); + commandLine.append(buildOptionalCommandLine()); + + return commandLine.toString(); + } + + /** + * Returns the fully-qualifed path to the 'mysqld' executable + * + * @return String the path to the server executable. + */ + private String getFullExecutablePath() { + StringBuilder pathBuf = new StringBuilder(); + + String optionalExecutablePath = getServerProps().getProperty(EXECUTABLE_PATH_KEY); + + if (optionalExecutablePath == null) { + // build the path using the defaults + String basedir = getServerProps().getProperty(BASEDIR_KEY); + pathBuf.append(basedir); + + if (!basedir.endsWith(File.separator)) { + pathBuf.append(File.separatorChar); + } + + if (runningOnWindows()) { + pathBuf.append("bin"); + } else { + pathBuf.append("libexec"); + } + + pathBuf.append(File.separatorChar); + } else { + pathBuf.append(optionalExecutablePath); + + if (!optionalExecutablePath.endsWith(File.separator)) { + pathBuf.append(File.separatorChar); + } + } + + String executableName = getServerProps().getProperty(EXECUTABLE_NAME_KEY, "mysqld"); + + pathBuf.append(executableName); + + return pathBuf.toString(); + } + + /** + * Builds the list of command-line arguments that will be passed to the + * mysql server to be started. + * + * @return String the list of command-line arguments. + */ + private String buildOptionalCommandLine() { + StringBuilder commandLineBuf = new StringBuilder(); + + if (this.serverProps != null) { + + for (Iterator iter = this.serverProps.keySet().iterator(); iter.hasNext();) { + String key = (String) iter.next(); + String value = this.serverProps.getProperty(key); + + if (!isNonCommandLineArgument(key)) { + if (value != null && value.length() > 0) { + commandLineBuf.append(" \""); + commandLineBuf.append("--"); + commandLineBuf.append(key); + commandLineBuf.append("="); + commandLineBuf.append(value); + commandLineBuf.append("\""); + } else { + commandLineBuf.append(" --"); + commandLineBuf.append(key); + } + } + } + } + + return commandLineBuf.toString(); + } + + /** + * Returns true if the property does not belong as a command-line argument + * + * @return boolean if the property should not be a command-line argument. + */ + private boolean isNonCommandLineArgument(String propName) { + return propName.equals(EXECUTABLE_NAME_KEY) || propName.equals(EXECUTABLE_PATH_KEY); + } + + /** + * Is this ServerController running on a Windows operating system? + * + * @return boolean if this ServerController is running on Windows + */ + private boolean runningOnWindows() { + return StringUtils.indexOfIgnoreCase(Constants.OS_NAME, "WINDOWS") != -1; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/AbstractPropertyDefinition.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/AbstractPropertyDefinition.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/AbstractPropertyDefinition.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.io.Serializable; + +import com.mysql.cj.exceptions.ExceptionInterceptor; + +public abstract class AbstractPropertyDefinition implements PropertyDefinition, Serializable { + + private static final long serialVersionUID = 2696624840927848766L; + + private String name; + private String ccAlias; + private T defaultValue; + private boolean isRuntimeModifiable; + private String description; + private String sinceVersion; + private String category; + private int order; + + private int lowerBound; + private int upperBound; + + public AbstractPropertyDefinition(String name, String camelCaseAlias, T defaultValue, boolean isRuntimeModifiable, String description, String sinceVersion, + String category, int orderInCategory) { + + this.name = name; + this.ccAlias = camelCaseAlias; + this.setDefaultValue(defaultValue); + this.setRuntimeModifiable(isRuntimeModifiable); + this.setDescription(description); + this.setSinceVersion(sinceVersion); + this.setCategory(category); + this.setOrder(orderInCategory); + } + + public AbstractPropertyDefinition(String name, String alias, T defaultValue, boolean isRuntimeModifiable, String description, String sinceVersion, + String category, int orderInCategory, int lowerBound, int upperBound) { + this(name, alias, defaultValue, isRuntimeModifiable, description, sinceVersion, category, orderInCategory); + this.setLowerBound(lowerBound); + this.setUpperBound(upperBound); + } + + public boolean hasValueConstraints() { + return (getAllowableValues() != null) && (getAllowableValues().length > 0); + } + + public boolean isRangeBased() { + return false; + } + + public String getName() { + return this.name; + } + + @Override + public String getCcAlias() { + return this.ccAlias; + } + + @Override + public boolean hasCcAlias() { + return this.ccAlias != null && this.ccAlias.length() > 0; + } + + public T getDefaultValue() { + return this.defaultValue; + } + + public void setDefaultValue(T defaultValue) { + this.defaultValue = defaultValue; + } + + public boolean isRuntimeModifiable() { + return this.isRuntimeModifiable; + } + + public void setRuntimeModifiable(boolean isRuntimeModifiable) { + this.isRuntimeModifiable = isRuntimeModifiable; + } + + public String getDescription() { + return this.description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getSinceVersion() { + return this.sinceVersion; + } + + public void setSinceVersion(String sinceVersion) { + this.sinceVersion = sinceVersion; + } + + public String getCategory() { + return this.category; + } + + public void setCategory(String category) { + this.category = category; + } + + public int getOrder() { + return this.order; + } + + public void setOrder(int order) { + this.order = order; + } + + public String[] getAllowableValues() { + return null; + } + + public int getLowerBound() { + return this.lowerBound; + } + + public void setLowerBound(int lowerBound) { + this.lowerBound = lowerBound; + } + + public int getUpperBound() { + return this.upperBound; + } + + public void setUpperBound(int upperBound) { + this.upperBound = upperBound; + } + + public abstract T parseObject(String value, ExceptionInterceptor exceptionInterceptor); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/AbstractReadableProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/AbstractReadableProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/AbstractReadableProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.io.Serializable; + +public abstract class AbstractReadableProperty extends AbstractRuntimeProperty implements ReadableProperty, Serializable { + + private static final long serialVersionUID = -3424722534876438236L; + + public AbstractReadableProperty() { + super(); + } + + protected AbstractReadableProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + } + + @Override + public T getValue() { + return this.value; + } + + @Override + public T getInitialValue() { + return this.initialValue; + } + + @Override + public String getStringValue() { + if (this.value != null) { + return this.value.toString(); + } + return null; + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/AbstractRuntimeProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/AbstractRuntimeProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/AbstractRuntimeProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.io.Serializable; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import javax.naming.RefAddr; +import javax.naming.Reference; + +import com.mysql.cj.exceptions.ExceptionInterceptor; + +public abstract class AbstractRuntimeProperty implements RuntimeProperty, Serializable { + + private static final long serialVersionUID = -3424722534876438236L; + + private PropertyDefinition propertyDefinition; + + protected T value; + + protected T initialValue; + + protected boolean wasExplicitlySet = false; + + private List> listeners; + + public AbstractRuntimeProperty() { + } + + protected AbstractRuntimeProperty(PropertyDefinition propertyDefinition) { + this.propertyDefinition = propertyDefinition; + this.value = propertyDefinition.getDefaultValue(); + this.initialValue = propertyDefinition.getDefaultValue(); + } + + @Override + public PropertyDefinition getPropertyDefinition() { + return this.propertyDefinition; + } + + @Override + public void initializeFrom(Properties extractFrom, ExceptionInterceptor exceptionInterceptor) { + String extractedValue = extractFrom.getProperty(getPropertyDefinition().getName()); + extractFrom.remove(getPropertyDefinition().getName()); + initializeFrom(extractedValue, exceptionInterceptor); + } + + @Override + public void initializeFrom(Reference ref, ExceptionInterceptor exceptionInterceptor) { + RefAddr refAddr = ref.get(getPropertyDefinition().getName()); + + if (refAddr != null) { + String refContentAsString = (String) refAddr.getContent(); + + initializeFrom(refContentAsString, exceptionInterceptor); + } + } + + protected void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterceptor) { + if (extractedValue != null) { + setFromString(extractedValue, exceptionInterceptor); + } + } + + public void setFromString(String value, ExceptionInterceptor exceptionInterceptor) { + this.value = getPropertyDefinition().parseObject(value, exceptionInterceptor); + this.wasExplicitlySet = true; + } + + @Override + public void resetValue() { + // no-op for readable properties + } + + public boolean isExplicitlySet() { + return this.wasExplicitlySet; + } + + @Override + public void addListener(RuntimePropertyListener l) { + if (this.listeners == null) { + this.listeners = new ArrayList<>(); + } + if (!this.listeners.contains(l)) { + this.listeners.add(new WeakReference<>(l)); + } + } + + @Override + public void removeListener(RuntimePropertyListener listener) { + if (this.listeners != null) { + for (WeakReference wr : this.listeners) { + RuntimePropertyListener l = wr.get(); + if (l == listener) { + this.listeners.remove(wr); + break; + } + } + } + } + + protected void invokeListeners() { + if (this.listeners != null) { + for (WeakReference wr : this.listeners) { + RuntimePropertyListener l = wr.get(); + if (l != null) { + l.handlePropertyChange(this); + } else { + this.listeners.remove(wr); + } + } + } + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/BooleanPropertyDefinition.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/BooleanPropertyDefinition.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/BooleanPropertyDefinition.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.util.Arrays; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.util.StringUtils; + +public class BooleanPropertyDefinition extends AbstractPropertyDefinition { + + private static final long serialVersionUID = -7288366734350231540L; + + public enum AllowableValues { + TRUE(true), FALSE(false), YES(true), NO(false); + + private boolean asBoolean; + + private AllowableValues(boolean booleanValue) { + this.asBoolean = booleanValue; + } + + public boolean asBoolean() { + return this.asBoolean; + } + } + + public BooleanPropertyDefinition(String name, String alias, Boolean defaultValue, boolean isRuntimeModifiable, String description, String sinceVersion, + String category, int orderInCategory) { + super(name, alias, defaultValue, isRuntimeModifiable, description, sinceVersion, category, orderInCategory); + } + + @Override + public String[] getAllowableValues() { + return Arrays.stream(AllowableValues.values()).map(AllowableValues::toString).toArray(String[]::new); + } + + @Override + public Boolean parseObject(String value, ExceptionInterceptor exceptionInterceptor) { + try { + return AllowableValues.valueOf(value.toUpperCase()).asBoolean(); + } catch (Exception e) { + throw ExceptionFactory.createException( + Messages.getString("PropertyDefinition.1", + new Object[] { getName(), StringUtils.stringArrayToString(getAllowableValues(), "'", "', '", "' or '", "'"), value }), + e, exceptionInterceptor); + } + } + + /** + * Creates instance of ReadableBooleanProperty or ModifiableBooleanProperty depending on isRuntimeModifiable() result. + * + * @return + */ + @Override + public RuntimeProperty createRuntimeProperty() { + return isRuntimeModifiable() ? new ModifiableBooleanProperty(this) : new ReadableBooleanProperty(this); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ConnectionPropertiesTransform.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ConnectionPropertiesTransform.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ConnectionPropertiesTransform.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.util.Properties; + +/** + * Implement this interface, and pass the class name as the 'propertiesTransform' property in your URL, and the driver will pass the properties it has + * parsed to your transform implementation so that you can modify/substitute/add any that you desire. + */ +public interface ConnectionPropertiesTransform { + /** + * The driver will call this method if the user has loaded your + * implementation of this interface by specifying the 'propertiesTransform' + * property in their URL. + * + * @param props + * the properties as passed by the driver (never null) + * + * @return the same properties with any transformations that your + * implementation has made + * + */ + Properties transformProperties(Properties props); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ConnectionUrl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ConnectionUrl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ConnectionUrl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,738 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import static com.mysql.cj.util.StringUtils.isNullOrEmpty; + +import java.io.IOException; +import java.io.InputStream; +import java.sql.DriverManager; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.stream.Collectors; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.InvalidConnectionAttributeException; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.util.LRUCache; +import com.mysql.cj.util.Util; + +/** + * A container for a database URL and a collection of given connection arguments. + * The connection string is parsed and split by its components, each of which is then processed and fixed according to the needs of the connection type. + * This abstract class holds all common behavior to all connection string types. Its subclasses must implement their own specifics such as classifying hosts by + * type or apply validation rules. + */ +public abstract class ConnectionUrl implements DatabaseUrlContainer { + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = 3306; + + private static final LRUCache connectionUrlCache = new LRUCache<>(100); + private static final ReadWriteLock rwLock = new ReentrantReadWriteLock(); + + /** + * The rules describing the number of hosts a database URL may contain. + */ + public enum HostsCardinality { + SINGLE { + @Override + public boolean assertSize(int n) { + return n == 1; + } + }, + MULTIPLE { + @Override + public boolean assertSize(int n) { + return n > 1; + } + }, + ONE_OR_MORE { + @Override + public boolean assertSize(int n) { + return n >= 1; + } + }; + + public abstract boolean assertSize(int n); + } + + /** + * The database URL type which is determined by the protocol section of the connection string. + */ + public enum Type { + SINGLE_CONNECTION("jdbc:mysql:", HostsCardinality.SINGLE), // + FAILOVER_CONNECTION("jdbc:mysql:", HostsCardinality.MULTIPLE), // + LOADBALANCE_CONNECTION("jdbc:mysql:loadbalance:", HostsCardinality.ONE_OR_MORE), // + REPLICATION_CONNECTION("jdbc:mysql:replication:", HostsCardinality.ONE_OR_MORE), // + XDEVAPI_SESSION("mysqlx:", HostsCardinality.ONE_OR_MORE); + + private String protocol; + private HostsCardinality cardinality; + + private Type(String protocol, HostsCardinality cardinality) { + this.protocol = protocol; + this.cardinality = cardinality; + } + + public String getProtocol() { + return this.protocol; + } + + public HostsCardinality getCardinality() { + return this.cardinality; + } + + /** + * Returns the {@link Type} corresponding to the given protocol and number of hosts, if any. Otherwise throws an {@link IllegalArgumentException}. + * Calling this method with the argument n lower than 0 skips the hosts cardinality validation. This should be used for URL protocol validation only as + * the returned {@link Type} won't won't reliable represent the database URL type. + * + * @param protocol + * the protocol + * @param n + * the number of hosts in the database URL + * @return the {@link Type} corresponding to the given protocol and number of hosts + */ + public static Type fromValue(String protocol, int n) { + for (Type t : values()) { + if (t.getProtocol().equalsIgnoreCase(protocol) && (n < 0 || t.getCardinality().assertSize(n))) { + return t; + } + } + if (n < 0) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.5", new Object[] { protocol })); + } + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.6", new Object[] { protocol, n })); + } + } + + protected Type type; + protected String originalConnStr; + protected String originalDatabase; + protected List hosts = new ArrayList<>(); + protected Map properties = new HashMap<>(); + ConnectionPropertiesTransform propertiesTransformer; + + /** + * Static factory method that returns either a new instance of a {@link ConnectionUrl} or a cached one. + * Returns "null" it can't handle the connection string. + * + * @param connString + * the connection string + * @param info + * the connection arguments map + * @return an instance of a {@link ConnectionUrl} or "null" if isn't able to handle the connection string + */ + public static ConnectionUrl getConnectionUrlInstance(String connString, Properties info) { + if (connString == null) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.0")); + } + String connStringCacheKey = buildConnectionStringCacheKey(connString, info); + ConnectionUrl connectionString; + + rwLock.readLock().lock(); + connectionString = connectionUrlCache.get(connStringCacheKey); + if (connectionString == null) { + rwLock.readLock().unlock(); + rwLock.writeLock().lock(); + try { + // Check again, in the meantime it could have been cached by another thread. + connectionString = connectionUrlCache.get(connStringCacheKey); + if (connectionString == null) { + ConnectionUrlParser connStrParser = ConnectionUrlParser.parseConnectionString(connString); + try { + Type.fromValue(connStrParser.getScheme(), -1); + } catch (WrongArgumentException e) { + return new ConnectionUrl(connString) { + }; + } + + switch (Type.fromValue(connStrParser.getScheme(), connStrParser.getHosts().size())) { + case SINGLE_CONNECTION: + connectionString = (ConnectionUrl) Util.getInstance("com.mysql.cj.conf.url.SingleConnectionUrl", + new Class[] { ConnectionUrlParser.class, Properties.class }, new Object[] { connStrParser, info }, null); + break; + case FAILOVER_CONNECTION: + connectionString = (ConnectionUrl) Util.getInstance("com.mysql.cj.conf.url.FailoverConnectionUrl", + new Class[] { ConnectionUrlParser.class, Properties.class }, new Object[] { connStrParser, info }, null); + break; + case LOADBALANCE_CONNECTION: + connectionString = (ConnectionUrl) Util.getInstance("com.mysql.cj.conf.url.LoadbalanceConnectionUrl", + new Class[] { ConnectionUrlParser.class, Properties.class }, new Object[] { connStrParser, info }, null); + break; + case REPLICATION_CONNECTION: + connectionString = (ConnectionUrl) Util.getInstance("com.mysql.cj.conf.url.ReplicationConnectionUrl", + new Class[] { ConnectionUrlParser.class, Properties.class }, new Object[] { connStrParser, info }, null); + break; + case XDEVAPI_SESSION: + connectionString = (ConnectionUrl) Util.getInstance("com.mysql.cj.conf.url.XDevAPIConnectionUrl", + new Class[] { ConnectionUrlParser.class, Properties.class }, new Object[] { connStrParser, info }, null); + break; + default: + return new ConnectionUrl(connString) { + }; + } + connectionUrlCache.put(connStringCacheKey, connectionString); + } + rwLock.readLock().lock(); + } finally { + rwLock.writeLock().unlock(); + } + } + rwLock.readLock().unlock(); + return connectionString; + } + + /** + * Builds a connection URL cache map key based on the connection string itself plus the string representation of the given connection properties. + * + * @param connString + * the connection string + * @param info + * the connection arguments map + * @return a connection string cache map key + */ + private static String buildConnectionStringCacheKey(String connString, Properties info) { + StringBuilder sbKey = new StringBuilder(connString); + sbKey.append("§"); + sbKey.append( + info == null ? null : info.stringPropertyNames().stream().map(k -> k + "=" + info.getProperty(k)).collect(Collectors.joining(", ", "{", "}"))); + return sbKey.toString(); + } + + /** + * Checks if this {@link ConnectionUrl} is able to process the given database URL. + * + * @param connString + * the connection string + * @return true if this class is able to process the given URL, false otherwise + */ + public static boolean acceptsUrl(String connString) { + if (connString == null) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.0")); + } + try { + ConnectionUrlParser connStringParser = ConnectionUrlParser.parseConnectionString(connString); + Type.fromValue(connStringParser.getScheme(), -1); + } catch (Throwable t) { + return false; + } + return true; + } + + /** + * Empty constructor. Required for subclasses initialization. + */ + protected ConnectionUrl() { + } + + /** + * Constructor for unsupported URLs + * + * @param origUrl + * URLs + */ + public ConnectionUrl(String origUrl) { + this.originalConnStr = origUrl; + } + + /** + * Constructs an instance of {@link ConnectionUrl}, performing all the required initializations. + * + * @param connStrParser + * a {@link ConnectionUrlParser} instance containing the parsed version of the original connection string + * @param info + * the connection arguments map + */ + protected ConnectionUrl(ConnectionUrlParser connStrParser, Properties info) { + this.originalConnStr = connStrParser.getDatabaseUrl(); + this.originalDatabase = connStrParser.getPath() == null ? "" : connStrParser.getPath(); + collectProperties(connStrParser, info); // Fill properties before filling hosts info. + collectHostsInfo(connStrParser); + } + + /** + * Joins the connection arguments from the connection string with the ones from the given connection arguments map collecting them in a single map. + * Additionally may also collect other connection arguments from configuration files. + * + * @param connStrParser + * the {@link ConnectionUrlParser} from where to collect the properties + * @param info + * the connection arguments map + */ + protected void collectProperties(ConnectionUrlParser connStrParser, Properties info) { + // Fill in the properties from the connection string. + this.properties.putAll(connStrParser.getProperties()); + + // Properties passed in override the ones from the connection string. + if (info != null) { + info.stringPropertyNames().stream().forEach(k -> this.properties.put(k, info.getProperty(k))); + } + + // Collect properties from additional sources. + processColdFusionAutoConfiguration(); + setupPropertiesTransformer(); + expandPropertiesFromConfigFiles(this.properties); + injectPerTypeProperties(this.properties); + } + + /** + * Checks if the conditions for the Cold Fusion auto configuration file are met. If so, adds a reference to its configuration file so that it can be loaded + * afterwards. + */ + protected void processColdFusionAutoConfiguration() { + if (Util.isColdFusion()) { + String autoConfigCf = this.properties.get(PropertyDefinitions.PNAME_autoConfigureForColdFusion); + if (autoConfigCf == null || autoConfigCf.equalsIgnoreCase("TRUE") || autoConfigCf.equalsIgnoreCase("YES")) { + String currentConfigFiles = this.properties.get(PropertyDefinitions.PNAME_useConfigs); + StringBuilder newConfigFiles = new StringBuilder(); + if (currentConfigFiles != null) { + newConfigFiles.append(currentConfigFiles).append(","); + } + newConfigFiles.append("coldFusion"); + this.properties.put(PropertyDefinitions.PNAME_useConfigs, newConfigFiles.toString()); + } + } + } + + /** + * Sets up the {@link ConnectionPropertiesTransform} if one was provided. + */ + protected void setupPropertiesTransformer() { + String propertiesTransformClassName = this.properties.get(PropertyDefinitions.PNAME_propertiesTransform); + if (!isNullOrEmpty(propertiesTransformClassName)) { + try { + this.propertiesTransformer = (ConnectionPropertiesTransform) Class.forName(propertiesTransformClassName).newInstance(); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | CJException e) { + throw ExceptionFactory.createException(InvalidConnectionAttributeException.class, + Messages.getString("ConnectionString.9", new Object[] { propertiesTransformClassName, e.toString() }), e); + } + } + } + + /** + * Expands the connection argument "useConfig" by reading the mentioned configuration files. + * + * @param props + * a connection arguments map from where to read the "useConfig" property and where to save the loaded properties. + */ + protected void expandPropertiesFromConfigFiles(Map props) { + // Properties from config files should not override the existing ones. + String configFiles = props.get(PropertyDefinitions.PNAME_useConfigs); + if (!isNullOrEmpty(configFiles)) { + Properties configProps = getPropertiesFromConfigFiles(configFiles); + configProps.stringPropertyNames().stream().filter(k -> !props.containsKey(k)).forEach(k -> props.put(k, configProps.getProperty(k))); + } + } + + /** + * Returns a map containing the properties read from the given configuration files. Multiple files can be referenced using a comma as separator. + * + * @param configFiles + * the list of the configuration files to read + * @return the map containing all the properties read + */ + public static Properties getPropertiesFromConfigFiles(String configFiles) { + Properties configProps = new Properties(); + for (String configFile : configFiles.split(",")) { + try (InputStream configAsStream = ConnectionUrl.class.getResourceAsStream("/com/mysql/cj/configurations/" + configFile + ".properties")) { + if (configAsStream == null) { + throw ExceptionFactory.createException(InvalidConnectionAttributeException.class, + Messages.getString("ConnectionString.10", new Object[] { configFile })); + } + configProps.load(configAsStream); + } catch (IOException e) { + throw ExceptionFactory.createException(InvalidConnectionAttributeException.class, + Messages.getString("ConnectionString.11", new Object[] { configFile }), e); + } + } + return configProps; + } + + /** + * Subclasses must override this method if they need to inject additional properties in the connection arguments map while it's being constructed. + * + * @param props + * the properties already containing all known connection arguments + */ + protected void injectPerTypeProperties(Map props) { + return; + } + + /** + * Collects the hosts information from the {@link ConnectionUrlParser}. + * + * @param connStrParser + * the {@link ConnectionUrlParser} from where to collect the hosts information + */ + protected void collectHostsInfo(ConnectionUrlParser connStrParser) { + connStrParser.getHosts().stream().map(this::fixHostInfo).forEach(this.hosts::add); + } + + /** + * Fixes the host information by moving data around and filling in missing data. + * Applies properties transformations to the collected properties if {@link ConnectionPropertiesTransform} was declared in the connection arguments. + * + * @param hi + * the host information data to fix + * @return a new {@link HostInfo} with all required data + */ + protected HostInfo fixHostInfo(HostInfo hi) { + Map hostProps = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + + hostProps.putAll(this.properties); // Add global connection arguments. + hostProps.putAll(hi.getHostProperties()); // Add/override host specific connection arguments. + hostProps.put(PropertyDefinitions.DBNAME_PROPERTY_KEY, getDatabase()); // Add the database name + + hostProps = preprocessPerTypeHostProperties(hostProps); + + String host = hostProps.remove(PropertyDefinitions.HOST_PROPERTY_KEY); + if (!isNullOrEmpty(hi.getHost())) { + host = hi.getHost(); + } else if (isNullOrEmpty(host)) { + host = getDefaultHost(); + } + + String portAsString = hostProps.remove(PropertyDefinitions.PORT_PROPERTY_KEY); + int port = hi.getPort(); + if (port == -1 && !isNullOrEmpty(portAsString)) { + try { + port = Integer.valueOf(portAsString); + } catch (NumberFormatException e) { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("ConnectionString.7", new Object[] { hostProps.get(PropertyDefinitions.PORT_PROPERTY_KEY) }), e); + } + } + if (port == -1) { + port = getDefaultPort(); + } + + String user = hostProps.remove(PropertyDefinitions.PNAME_user); + if (!isNullOrEmpty(hi.getUser())) { + user = hi.getUser(); + } else if (isNullOrEmpty(user)) { + user = getDefaultUser(); + } + + boolean isPasswordless = hi.isPasswordless(); + String password = hostProps.remove(PropertyDefinitions.PNAME_password); + if (!isPasswordless) { + password = hi.getPassword(); + } else if (password == null) { + password = getDefaultPassword(); + isPasswordless = true; + } else { + isPasswordless = false; + } + + expandPropertiesFromConfigFiles(hostProps); + fixKeysCase(hostProps); + fixProtocolDependencies(hostProps); + + return buildHostInfo(host, port, user, password, isPasswordless, hostProps); + } + + /** + * Subclasses should override this to perform any required preprocessing on the host information properties. + * + * @param hostProps + * the host properties map to process + * @return + * the processed host properties map + */ + protected Map preprocessPerTypeHostProperties(Map hostProps) { + return hostProps; + } + + /** + * Returns the default host. Subclasses must override this method if they have different default host value. + * + * @return the default host + */ + public String getDefaultHost() { + return DEFAULT_HOST; + } + + /** + * Returns the default port. Subclasses must override this method if they have different default port value. + * + * @return the default port + */ + public int getDefaultPort() { + return DEFAULT_PORT; + } + + /** + * Returns the default user. Usually the one provided in the method {@link DriverManager#getConnection(String, String, String)} or as connection argument. + * + * @return the default user + */ + public String getDefaultUser() { + String user = this.properties.get(PropertyDefinitions.PNAME_user); + return isNullOrEmpty(user) ? "" : user; + } + + /** + * Returns the default password. Usually the one provided in the method {@link DriverManager#getConnection(String, String, String)} or as connection + * argument. + * + * @return the default password + */ + public String getDefaultPassword() { + String password = this.properties.get(PropertyDefinitions.PNAME_password); + return isNullOrEmpty(password) ? "" : password; + } + + /** + * Fixes the case for alternate host syntax main properties. + * + * @param hostProps + * the host properties map to fix + */ + protected void fixKeysCase(Map hostProps) { + for (String key : Arrays.asList(PropertyDefinitions.PROTOCOL_PROPERTY_KEY, PropertyDefinitions.PATH_PROPERTY_KEY, PropertyDefinitions.TYPE_PROPERTY_KEY, + PropertyDefinitions.ADDRESS_PROPERTY_KEY, PropertyDefinitions.PRIORITY_PROPERTY_KEY)) { + if (hostProps.containsKey(key)) { + hostProps.put(key, hostProps.remove(key)); + } + } + } + + /** + * Fixes the protocol (TCP vs PIPE) dependencies for the given host properties map. + * + * @param hostProps + * the host properties map to fix + */ + protected void fixProtocolDependencies(Map hostProps) { + String protocol = hostProps.get(PropertyDefinitions.PROTOCOL_PROPERTY_KEY); + if (!isNullOrEmpty(protocol) && protocol.equalsIgnoreCase("PIPE")) { + if (!hostProps.containsKey(PropertyDefinitions.PNAME_socketFactory)) { + hostProps.put(PropertyDefinitions.PNAME_socketFactory, "com.mysql.cj.protocol.NamedPipeSocketFactory"); + } + if (hostProps.containsKey(PropertyDefinitions.PATH_PROPERTY_KEY) && !hostProps.containsKey(PropertyDefinitions.NAMED_PIPE_PROP_NAME)) { + hostProps.put(PropertyDefinitions.NAMED_PIPE_PROP_NAME, hostProps.get(PropertyDefinitions.PATH_PROPERTY_KEY)); + } + } + } + + /** + * Returns this connection URL type. + * + * @return the connection URL type + */ + public Type getType() { + return this.type; + } + + /** + * Returns the original database URL that produced this connection string. + * + * @return the original database URL + */ + @Override + public String getDatabaseUrl() { + return this.originalConnStr; + } + + /** + * Returns the database from this connection URL. Note that a "DBNAME" property overrides the database identified in the connection string. + * + * @return the database name + */ + public String getDatabase() { + return this.properties.containsKey(PropertyDefinitions.DBNAME_PROPERTY_KEY) ? this.properties.get(PropertyDefinitions.DBNAME_PROPERTY_KEY) + : this.originalDatabase; + } + + /** + * Returns the number of hosts in this connection URL. + * + * @return the number of hosts + */ + public int hostsCount() { + return this.hosts.size(); + } + + /** + * Returns the single or first host info structure. + * + * @return the first host info structure + */ + public HostInfo getMainHost() { + return this.hosts.isEmpty() ? null : this.hosts.get(0); + } + + /** + * Returns a list of the hosts in this connection URL. + * + * @return the hosts list from this connection URL + */ + public List getHostsList() { + return Collections.unmodifiableList(this.hosts); + } + + /** + * Returns an existing host info with the same host:port part or spawns a new isolated host info based on this connection URL if none was found. + * + * @param hostPortPair + * the host:port part to search for + * @return the existing host info or a new independent one + */ + public HostInfo getHostOrSpawnIsolated(String hostPortPair) { + return getHostOrSpawnIsolated(hostPortPair, this.hosts); + } + + /** + * Returns an existing host info with the same host:port part or spawns a new isolated host info based on this connection URL if none was found. + * + * @param hostPortPair + * the host:port part to search for + * @param hostsList + * the hosts list from where to search the host list + * @return the existing host info or a new independent one + */ + public HostInfo getHostOrSpawnIsolated(String hostPortPair, List hostsList) { + for (HostInfo hi : hostsList) { + if (hostPortPair.equals(hi.getHostPortPair())) { + return hi; + } + } + + ConnectionUrlParser.Pair hostAndPort = ConnectionUrlParser.parseHostPortPair(hostPortPair); + String host = hostAndPort.left; + Integer port = hostAndPort.right; + String user = getDefaultUser(); + String password = getDefaultPassword(); + + return buildHostInfo(host, port, user, password, true, this.properties); + } + + /** + * Creates a new {@link HostInfo} structure with the given components, passing through the properties transformer if there is one defined in this connection + * string; + * + * @param host + * the host + * @param port + * the port + * @param user + * the user name + * @param password + * the password + * @param isDefaultPwd + * no password was provided in the connection URL or arguments? + * @param hostProps + * the host properties map + * @return a new instance of {@link HostInfo} + */ + private HostInfo buildHostInfo(String host, int port, String user, String password, boolean isDefaultPwd, Map hostProps) { + // Apply properties transformations if needed. + if (this.propertiesTransformer != null) { + Properties props = new Properties(); + props.putAll(hostProps); + + props.setProperty(PropertyDefinitions.HOST_PROPERTY_KEY, host); + props.setProperty(PropertyDefinitions.PORT_PROPERTY_KEY, String.valueOf(port)); + props.setProperty(PropertyDefinitions.PNAME_user, user); + props.setProperty(PropertyDefinitions.PNAME_password, password); + + Properties transformedProps = this.propertiesTransformer.transformProperties(props); + + host = transformedProps.getProperty(PropertyDefinitions.HOST_PROPERTY_KEY); + try { + port = Integer.parseInt(transformedProps.getProperty(PropertyDefinitions.PORT_PROPERTY_KEY)); + } catch (NumberFormatException e) { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("ConnectionString.8", + new Object[] { PropertyDefinitions.PORT_PROPERTY_KEY, transformedProps.getProperty(PropertyDefinitions.PORT_PROPERTY_KEY) }), + e); + } + user = transformedProps.getProperty(PropertyDefinitions.PNAME_user); + password = transformedProps.getProperty(PropertyDefinitions.PNAME_password); + + List surplusKeys = Arrays.asList(PropertyDefinitions.HOST_PROPERTY_KEY, PropertyDefinitions.PORT_PROPERTY_KEY, + PropertyDefinitions.PNAME_user, PropertyDefinitions.PNAME_password); + Map transformedHostProps = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + transformedProps.stringPropertyNames().stream().filter(k -> !surplusKeys.contains(k)) + .forEach(k -> transformedHostProps.put(k, transformedProps.getProperty(k))); + hostProps = transformedHostProps; + } + + return new HostInfo(this, host, port, user, password, isDefaultPwd, hostProps); + } + + /** + * Returns the original (common to all hosts) connection arguments as provided in the connection string query section. + * + * @return the original (common to all hosts) connection arguments + */ + public Map getOriginalProperties() { + return Collections.unmodifiableMap(this.properties); + } + + /** + * Returns a {@link Properties} instance containing the connection arguments extracted from the URL query section, i.e., per host attributes are excluded. + * Applies properties transformations to the collected properties if {@link ConnectionPropertiesTransform} was declared in the connection arguments. + * + * @return a {@link Properties} instance containing the common connection arguments. + */ + public Properties getConnectionArgumentsAsProperties() { + Properties props = new Properties(); + if (this.properties != null) { + props.putAll(this.properties); + } + + return this.propertiesTransformer != null ? this.propertiesTransformer.transformProperties(props) : props; + } + + /** + * Returns a string representation of this object. + * + * @return a string representation of this object + */ + @Override + public String toString() { + StringBuilder asStr = new StringBuilder(super.toString()); + asStr.append(String.format(" :: {type: \"%s\", hosts: %s, database: \"%s\", properties: %s, propertiesTransformer: %s}", this.type, this.hosts, + this.originalDatabase, this.properties, this.propertiesTransformer)); + return asStr.toString(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ConnectionUrlParser.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ConnectionUrlParser.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ConnectionUrlParser.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,658 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import static com.mysql.cj.util.StringUtils.isNullOrEmpty; +import static com.mysql.cj.util.StringUtils.safeTrim; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.util.StringUtils; + +/** + * This class parses a connection string using the general URI structure defined in RFC 3986. Instead of using a URI instance to ensure the correct syntax of + * the connection string, this implementation uses regular expressions which is faster but also less strict in terms of validations. This actually works better + * because database URLs don't exactly stick to the RFC 3986 rules. + *

+ * scheme://authority/path?query#fragment + *

+ * This results in splitting the connection string URL and processing its internal parts: + *

+ *
scheme
+ *
The protocol and subprotocol identification. Usually "jdbc:mysql:" or "mysqlx:".
+ *
authority
+ *
Contains information about the user credentials and/or the host and port information. Unlike its definition in the RFC 3986 specification, there can be + * multiple authority sections separated by a single comma (,) in a connection string. It is also possible to use an alternative syntax for the user and/or host + * identification, that also allows setting per host connection properties, in the form of + * "[user[:password]@]address=(key1=value)[(key2=value)]...[,address=(key3=value)[(key4=value)]...]...".
+ *
path
+ *
Corresponds to the database identification.
+ *
query
+ *
The connection properties, written as "propertyName1[=[propertyValue1]][&propertyName2[=[propertyValue2]]]..."
+ *
fragment
+ *
The fragment section is ignored in Connector/J connection strings.
+ *
+ */ +public class ConnectionUrlParser implements DatabaseUrlContainer { + private static final String DUMMY_SCHEMA = "cj://"; + private static final String USER_PASS_SEPARATOR = ":"; + private static final String USER_HOST_SEPARATOR = "@"; + private static final String HOSTS_SEPARATOR = ","; + private static final String KEY_VALUE_HOST_INFO_OPENING_MARKER = "("; + private static final String KEY_VALUE_HOST_INFO_CLOSING_MARKER = ")"; + private static final String HOSTS_LIST_OPENING_MARKERS = "[("; + private static final String HOSTS_LIST_CLOSING_MARKERS = "])"; + private static final String ADDRESS_EQUALS_HOST_INFO_PREFIX = "ADDRESS="; + + private static final Pattern CONNECTION_STRING_PTRN = Pattern.compile("(?[\\w:%]+)\\s*" // scheme: required; alphanumeric, colon or percent + + "(?://(?[^/?#]*))?\\s*" // authority: optional; starts with "//" followed by any char except "/", "?" and "#" + + "(?:/(?!\\s*/)(?[^?#]*))?" // path: optional; starts with "/" but not followed by "/", and then followed by by any char except "?" and "#" + + "(?:\\?(?!\\s*\\?)(?[^#]*))?" // query: optional; starts with "?" but not followed by "?", and then followed by by any char except "#" + + "(?:\\s*#(?.*))?"); // fragment: optional; starts with "#", and then followed by anything + private static final Pattern HOST_LIST_PTRN = Pattern.compile("^\\[(?.*)\\]$"); + private static final Pattern GENERIC_HOST_PTRN = Pattern.compile("^(?.*?)(?::(?[^:]*))?$"); + private static final Pattern KEY_VALUE_HOST_PTRN = Pattern.compile("[,\\s]*(?[\\w\\.\\-\\s%]*)(?:=(?[^,=]*))?"); + private static final Pattern ADDRESS_EQUALS_HOST_PTRN = Pattern.compile("\\s*\\(\\s*(?[\\w\\.\\-%]+)?\\s*(?:=(?[^)]*))?\\)\\s*"); + private static final Pattern PROPERTIES_PTRN = Pattern.compile("[&\\s]*(?[\\w\\.\\-\\s%]*)(?:=(?[^&=]*))?"); + + private final String baseConnectionString; + private String scheme; + private String authority; + private String path; + private String query; + + private List parsedHosts = null; + private Map parsedProperties = null; + + /** + * Static factory method for constructing instances of this class. + * + * @param connString + * The connection string to parse. + * @return an instance of {@link ConnectionUrlParser} + */ + public static ConnectionUrlParser parseConnectionString(String connString) { + if (connString == null) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.0")); + } + return new ConnectionUrlParser(connString); + } + + /** + * Constructs a connection string parser for the given connection string. + * + * @param connString + * the connection string to parse + */ + private ConnectionUrlParser(String connString) { + this.baseConnectionString = connString; + parseConnectionString(); + } + + /** + * Splits the connection string in its main sections. + */ + private void parseConnectionString() { + String connString = this.baseConnectionString; + Matcher matcher = CONNECTION_STRING_PTRN.matcher(connString); + if (!matcher.matches()) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.1")); + } + this.scheme = decode(matcher.group("scheme")); + this.authority = matcher.group("authority"); // Don't decode just yet. + this.path = matcher.group("path") == null ? null : decode(matcher.group("path")).trim(); + this.query = matcher.group("query"); // Don't decode just yet. + } + + /** + * Parses the authority section (user and/or host identification) of the connection string URI. + */ + private void parseAuthoritySection() { + if (isNullOrEmpty(this.authority)) { + // Add an empty, default, host. + this.parsedHosts.add(new HostInfo()); + return; + } + + List authoritySegments = StringUtils.split(this.authority, HOSTS_SEPARATOR, HOSTS_LIST_OPENING_MARKERS, HOSTS_LIST_CLOSING_MARKERS, true, + StringUtils.SEARCH_MODE__MRK_WS); + for (String hi : authoritySegments) { + parseAuthoritySegment(hi); + } + } + + /** + * Parses the given sub authority segment, which can take one of the following syntaxes: + *
    + *
  • _user_:_password_@_host_:_port_ + *
  • _user_:_password_@(key1=value1,key2=value2,...) + *
  • _user_:_password_@address=(key1=value1)(key2=value2)... + *
  • _user_:_password_@[_any_of_the_above_1_,_any_of_the_above_2_,...] + *
+ * Most of the above placeholders can be omitted, representing a null, empty, or default value. + * The placeholder _host_, can be a host name, IPv4 or IPv6. This parser doesn't check IP syntax. IPv6 addresses are enclosed by square brackets ([::1]). + * The placeholder _any_of_the_above_?_ can be any of the above except for the user information part (_user_:_password_@). + * When the symbol ":" is not used, it means an null/empty password or a default (-1) port, respectively. + * When the symbol "@" is not used, it means that the authority part doesn't contain user information (depending on the scheme type can still be provided + * via key=value pairs). + * + * @param authSegment + * the string containing the authority segment + */ + private void parseAuthoritySegment(String authSegment) { + /* + * Start by splitting the user and host information parts from the authority segment and process the user information, if any. + */ + Pair userHostInfoSplit = splitByUserInfoAndHostInfo(authSegment); + String userInfo = safeTrim(userHostInfoSplit.left); + String user = null; + String password = null; + if (!isNullOrEmpty(userInfo)) { + Pair userInfoPair = parseUserInfo(userInfo); + user = decode(safeTrim(userInfoPair.left)); + password = decode(safeTrim(userInfoPair.right)); + } + String hostInfo = safeTrim(userHostInfoSplit.right); + + /* + * Handle an authority part without host information. + */ + HostInfo hi = buildHostInfoForEmptyHost(user, password, hostInfo); + if (hi != null) { + this.parsedHosts.add(hi); + return; + } + + /* + * Try using a java.net.URI instance to parse the host information. This helps dealing with the IPv6 syntax. + */ + hi = buildHostInfoResortingToUriParser(user, password, authSegment); + if (hi != null) { + this.parsedHosts.add(hi); + return; + } + + /* + * Using a URI didn't work, now check if the host part is composed by a sub list of hosts and process them, one by one if so. + */ + List hiList = buildHostInfoResortingToSubHostsListParser(user, password, hostInfo); + if (hiList != null) { + this.parsedHosts.addAll(hiList); + return; + } + + /* + * The hosts list syntax didn't work, now check if the host information is written in the alternate syntax "(Key1=value1,key2=value2)". + */ + hi = buildHostInfoResortingToKeyValueSyntaxParser(user, password, hostInfo); + if (hi != null) { + this.parsedHosts.add(hi); + return; + } + + /* + * Key/value syntax didn't work either, now check if the host information is written in the alternate syntax "address=(...)". + * This parser needs to run after the key/value one because a key named "address" could invalidate it. + */ + hi = buildHostInfoResortingToAddressEqualsSyntaxParser(user, password, hostInfo); + if (hi != null) { + this.parsedHosts.add(hi); + return; + } + + /* + * Alternate syntax also failed, let's wind up the corner cases the URI couldn't handle. + */ + hi = buildHostInfoResortingToGenericSyntaxParser(user, password, hostInfo); + if (hi != null) { + this.parsedHosts.add(hi); + return; + } + + /* + * Failed parsing the authority segment. + */ + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.2", new Object[] { authSegment })); + } + + /** + * Builds an {@link HostInfo} instance for empty host authority segments. + * + * @param user + * the user to include in the final {@link HostInfo} + * @param password + * the password to include in the final {@link HostInfo} + * @param hostInfo + * the string containing the host information part + * @return the {@link HostInfo} instance containing the parsed information or null if the host part is not empty + */ + private HostInfo buildHostInfoForEmptyHost(String user, String password, String hostInfo) { + if (isNullOrEmpty(hostInfo)) { + if (isNullOrEmpty(user) && isNullOrEmpty(password)) { + return new HostInfo(); + } + return new HostInfo(this, null, -1, user, password); + } + return null; + } + + /** + * Parses the host information resorting to a URI object. This process handles most single-host well formed addresses. + * + * @param user + * the user to include in the final {@link HostInfo} + * @param password + * the password to include in the final {@link HostInfo} + * @param hostInfo + * the string containing the host information part + * + * @return the {@link HostInfo} instance containing the parsed information or null if unable to parse the host information + */ + private HostInfo buildHostInfoResortingToUriParser(String user, String password, String hostInfo) { + String host = null; + int port = -1; + + try { + URI uri = URI.create(DUMMY_SCHEMA + hostInfo); + if (uri.getHost() != null) { + host = decode(uri.getHost()); + } + if (uri.getPort() != -1) { + port = uri.getPort(); + } + if (uri.getUserInfo() != null) { + // Can't have another one. The user information should have been handled already. + return null; + } + } catch (IllegalArgumentException e) { + // The URI failed to parse the host information. + return null; + } + if (host != null || port != -1) { + // The host info parsing succeeded. + return new HostInfo(this, host, port, user, password); + } + return null; + } + + /** + * Parses the host information using the alternate sub hosts lists syntax "[host1, host2, ...]". + * + * @param user + * the user to include in all the resulting {@link HostInfo} + * @param password + * the password to include in all the resulting {@link HostInfo} + * @param hostInfo + * the string containing the host information part + * @return a list with all {@link HostInfo} instances containing the parsed information or null if unable to parse the host information + */ + private List buildHostInfoResortingToSubHostsListParser(String user, String password, String hostInfo) { + Matcher matcher = HOST_LIST_PTRN.matcher(hostInfo); + if (matcher.matches()) { + String hosts = matcher.group("hosts"); + List hostsList = StringUtils.split(hosts, HOSTS_SEPARATOR, HOSTS_LIST_OPENING_MARKERS, HOSTS_LIST_CLOSING_MARKERS, true, + StringUtils.SEARCH_MODE__MRK_WS); + // One single element could, in fact, be an IPv6 stripped from its delimiters. + boolean maybeIPv6 = hostsList.size() == 1 && hostsList.get(0).matches("(?i)^[\\dabcdef:]+$"); + List hostInfoList = new ArrayList<>(); + for (String h : hostsList) { + HostInfo hi; + if ((hi = buildHostInfoForEmptyHost(user, password, h)) != null) { + hostInfoList.add(hi); + } else if ((hi = buildHostInfoResortingToUriParser(user, password, h)) != null + || (maybeIPv6 && (hi = buildHostInfoResortingToUriParser(user, password, "[" + h + "]")) != null)) { + hostInfoList.add(hi); + } else if ((hi = buildHostInfoResortingToKeyValueSyntaxParser(user, password, h)) != null) { + hostInfoList.add(hi); + } else if ((hi = buildHostInfoResortingToAddressEqualsSyntaxParser(user, password, h)) != null) { + hostInfoList.add(hi); + } else if ((hi = buildHostInfoResortingToGenericSyntaxParser(user, password, h)) != null) { + hostInfoList.add(hi); + } else { + return null; + } + } + return hostInfoList; + } + return null; + } + + /** + * Parses the host information using the alternate syntax "(key1=value1, key2=value2, ...)". + * + * @param user + * the user to include in the resulting {@link HostInfo} + * @param password + * the password to include in the resulting {@link HostInfo} + * @param hostInfo + * the string containing the host information part + * @return the {@link HostInfo} instance containing the parsed information or null if unable to parse the host information + */ + private HostInfo buildHostInfoResortingToKeyValueSyntaxParser(String user, String password, String hostInfo) { + if (!hostInfo.startsWith(KEY_VALUE_HOST_INFO_OPENING_MARKER) || !hostInfo.endsWith(KEY_VALUE_HOST_INFO_CLOSING_MARKER)) { + // This pattern won't work. + return null; + } + hostInfo = hostInfo.substring(KEY_VALUE_HOST_INFO_OPENING_MARKER.length(), hostInfo.length() - KEY_VALUE_HOST_INFO_CLOSING_MARKER.length()); + return new HostInfo(this, null, -1, user, password, processKeyValuePattern(KEY_VALUE_HOST_PTRN, hostInfo)); + } + + /** + * Parses the host information using the alternate syntax "address=(key1=value1)(key2=value2)...". + * + * @param user + * the user to include in the resulting {@link HostInfo} + * @param password + * the password to include in the resulting {@link HostInfo} + * @param hostInfo + * the string containing the host information part + * @return the {@link HostInfo} instance containing the parsed information or null if unable to parse the host information + */ + private HostInfo buildHostInfoResortingToAddressEqualsSyntaxParser(String user, String password, String hostInfo) { + int p = StringUtils.indexOfIgnoreCase(hostInfo, ADDRESS_EQUALS_HOST_INFO_PREFIX); + if (p != 0) { + // This pattern won't work. + return null; + } + hostInfo = hostInfo.substring(p + ADDRESS_EQUALS_HOST_INFO_PREFIX.length()).trim(); + return new HostInfo(this, null, -1, user, password, processKeyValuePattern(ADDRESS_EQUALS_HOST_PTRN, hostInfo)); + } + + /** + * Parses the host information using the generic syntax "host:port". + * + * @param user + * the user to include in the resulting {@link HostInfo} + * @param password + * the password to include in the resulting {@link HostInfo} + * @param hostInfo + * the string containing the host information part + * @return the {@link HostInfo} instance containing the parsed information or null if unable to parse the host information + */ + private HostInfo buildHostInfoResortingToGenericSyntaxParser(String user, String password, String hostInfo) { + if (splitByUserInfoAndHostInfo(hostInfo).left != null) { + // This host information is invalid if contains another user information part. + return null; + } + Pair hostPortPair = parseHostPortPair(hostInfo); + String host = decode(safeTrim(hostPortPair.left)); + Integer port = hostPortPair.right; + return new HostInfo(this, isNullOrEmpty(host) ? null : host, port, user, password); + } + + /** + * Splits the given authority segment in the user information part and the host part. + * + * @param authSegment + * the string containing the authority segment, i.e., the user and host information parts + * @return + * a {@link Pair} containing the user information in the left side and the host information in the right + */ + private Pair splitByUserInfoAndHostInfo(String authSegment) { + String userInfoPart = null; + String hostInfoPart = authSegment; + int p = authSegment.indexOf(USER_HOST_SEPARATOR); + if (p >= 0) { + userInfoPart = authSegment.substring(0, p); + hostInfoPart = authSegment.substring(p + USER_HOST_SEPARATOR.length()); + } + return new Pair<>(userInfoPart, hostInfoPart); + } + + /** + * Parses the given user information which is formed by the parts [user][:password]. + * + * @param userInfo + * the string containing the user information + * @return a {@link Pair} containing the user and password information or null if the user information can't be parsed + */ + public static Pair parseUserInfo(String userInfo) { + if (isNullOrEmpty(userInfo)) { + return null; + } + String[] userInfoParts = userInfo.split(USER_PASS_SEPARATOR, 2); + String userName = userInfoParts[0]; + String password = userInfoParts.length > 1 ? userInfoParts[1] : null; + return new Pair<>(userName, password); + } + + /** + * Parses a host:port pair and returns the two elements in a {@link Pair} + * + * @param hostInfo + * the host:pair to parse + * @return a {@link Pair} containing the host and port information or null if the host information can't be parsed + */ + public static Pair parseHostPortPair(String hostInfo) { + if (isNullOrEmpty(hostInfo)) { + return null; + } + Matcher matcher = GENERIC_HOST_PTRN.matcher(hostInfo); + if (matcher.matches()) { + String host = matcher.group("host"); + String portAsString = decode(safeTrim(matcher.group("port"))); + Integer portAsInteger = -1; + if (!isNullOrEmpty(portAsString)) { + try { + portAsInteger = Integer.parseInt(portAsString); + } catch (NumberFormatException e) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.3", new Object[] { hostInfo }), + e); + } + } + return new Pair<>(host, portAsInteger); + } + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.3", new Object[] { hostInfo })); + } + + /** + * Parses the connection properties section and stores the extracted key/value pairs into a local map. + */ + private void parseQuerySection() { + if (isNullOrEmpty(this.query)) { + this.parsedProperties = new HashMap<>(); + return; + } + this.parsedProperties = processKeyValuePattern(PROPERTIES_PTRN, this.query); + } + + /** + * Takes a two-matching-groups (respectively named "key" and "value") pattern which is successively tested against the given string and produces a key/value + * map with the matched values. The given pattern must ensure that there are no leftovers between successive tests, i.e., the end of the previous match must + * coincide with the beginning of the next. + * + * @param pattern + * the regular expression pattern to match against to + * @param input + * the input string + * @return a key/value map containing the matched values + */ + private Map processKeyValuePattern(Pattern pattern, String input) { + Matcher matcher = pattern.matcher(input); + int p = 0; + Map kvMap = new HashMap<>(); + while (matcher.find()) { + if (matcher.start() != p) { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("ConnectionString.4", new Object[] { input.substring(p) })); + } + String key = decode(safeTrim(matcher.group("key"))); + String value = decode(safeTrim(matcher.group("value"))); + if (!isNullOrEmpty(key)) { + kvMap.put(key, value); + } else if (!isNullOrEmpty(value)) { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("ConnectionString.4", new Object[] { input.substring(p) })); + } + p = matcher.end(); + } + if (p != input.length()) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionString.4", new Object[] { input.substring(p) })); + } + return kvMap; + } + + /** + * URL-decode the given string. + * + * @param text + * the string to decode + * @return + * the decoded string + */ + private static String decode(String text) { + if (isNullOrEmpty(text)) { + return text; + } + try { + return URLDecoder.decode(text, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + // Won't happen. + } + return ""; + } + + /** + * Returns the original database URL that produced this connection string parser. + * + * @return the original database URL + */ + @Override + public String getDatabaseUrl() { + return this.baseConnectionString; + } + + /** + * Returns the scheme section. + * + * @return the scheme section + */ + public String getScheme() { + return this.scheme; + } + + /** + * Returns the authority section. + * + * @return the authority section + */ + public String getAuthority() { + return this.authority; + } + + /** + * Returns the path section. + * + * @return the path section + */ + public String getPath() { + return this.path; + } + + /** + * Returns the query section. + * + * @return the query section + */ + public String getQuery() { + return this.query; + } + + /** + * Returns the hosts information. + * + * @return the hosts information + */ + public List getHosts() { + if (this.parsedHosts == null) { + this.parsedHosts = new ArrayList<>(); + parseAuthoritySection(); + } + return this.parsedHosts; + } + + /** + * Returns the properties map contained in this connection string. + * + * @return the properties map + */ + public Map getProperties() { + if (this.parsedProperties == null) { + parseQuerySection(); + } + return Collections.unmodifiableMap(this.parsedProperties); + } + + /** + * Returns a string representation of this object. + * + * @return a string representation of this object + */ + @Override + public String toString() { + StringBuilder asStr = new StringBuilder(super.toString()); + asStr.append(String.format(" :: {scheme: \"%s\", authority: \"%s\", path: \"%s\", query: \"%s\", parsedHosts: %s, parsedProperties: %s}", this.scheme, + this.authority, this.path, this.query, this.parsedHosts, this.parsedProperties)); + return asStr.toString(); + } + + /** + * This class is a simple container for two elements. + */ + public static class Pair { + public final T left; + public final U right; + + public Pair(T left, U right) { + this.left = left; + this.right = right; + } + + @Override + public String toString() { + StringBuilder asStr = new StringBuilder(super.toString()); + asStr.append(String.format(" :: { left: %s, right: %s }", this.left, this.right)); + return asStr.toString(); + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/DatabaseUrlContainer.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/DatabaseUrlContainer.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/DatabaseUrlContainer.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +/** + * Implementors of this class must be able to provide a database URL. + */ +public interface DatabaseUrlContainer { + /** + * Returns the original database URL that produced this connection string. + * + * @return the original database URL + */ + String getDatabaseUrl(); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/DefaultPropertySet.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/DefaultPropertySet.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/DefaultPropertySet.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.PropertyNotModifiableException; +import com.mysql.cj.exceptions.WrongArgumentException; + +public class DefaultPropertySet implements PropertySet, Serializable { + + private static final long serialVersionUID = -5156024634430650528L; + + private final Map> PROPERTY_NAME_TO_RUNTIME_PROPERTY = new HashMap<>(); + + public DefaultPropertySet() { + + for (PropertyDefinition pdef : PropertyDefinitions.PROPERTY_NAME_TO_PROPERTY_DEFINITION.values()) { + addProperty(pdef.createRuntimeProperty()); + } + + } + + @Override + public void addProperty(RuntimeProperty prop) { + PropertyDefinition def = prop.getPropertyDefinition(); + this.PROPERTY_NAME_TO_RUNTIME_PROPERTY.put(def.getName(), prop); + if (def.hasCcAlias()) { + this.PROPERTY_NAME_TO_RUNTIME_PROPERTY.put(def.getCcAlias(), prop); + } + } + + @Override + public void removeProperty(String name) { + RuntimeProperty prop = this.PROPERTY_NAME_TO_RUNTIME_PROPERTY.remove(name); + if (prop != null) { + if (!name.equals(prop.getPropertyDefinition().getName())) { + this.PROPERTY_NAME_TO_RUNTIME_PROPERTY.remove(prop.getPropertyDefinition().getName()); + } else if (prop.getPropertyDefinition().hasCcAlias()) { + this.PROPERTY_NAME_TO_RUNTIME_PROPERTY.remove(prop.getPropertyDefinition().getCcAlias()); + } + } + } + + @SuppressWarnings("unchecked") + @Override + public ReadableProperty getReadableProperty(String name) { + try { + ReadableProperty prop = (ReadableProperty) this.PROPERTY_NAME_TO_RUNTIME_PROPERTY.get(name); + if (prop != null) { + return prop; + } + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionProperties.notFound", new Object[] { name })); + + } catch (ClassCastException ex) { + // TODO improve message + throw ExceptionFactory.createException(WrongArgumentException.class, ex.getMessage(), ex); + } + } + + @Override + public ReadableProperty getBooleanReadableProperty(String name) { + return getReadableProperty(name); + } + + @Override + public ReadableProperty getIntegerReadableProperty(String name) { + return getReadableProperty(name); + } + + @Override + public ReadableProperty getLongReadableProperty(String name) { + return getReadableProperty(name); + } + + @Override + public ReadableProperty getMemorySizeReadableProperty(String name) { + return getReadableProperty(name); + } + + @Override + public ReadableProperty getStringReadableProperty(String name) { + return getReadableProperty(name); + } + + @Override + public > ReadableProperty getEnumReadableProperty(String name) { + return getReadableProperty(name); + } + + @SuppressWarnings("unchecked") + @Override + public ModifiableProperty getModifiableProperty(String name) { + RuntimeProperty prop = this.PROPERTY_NAME_TO_RUNTIME_PROPERTY.get(name); + + if (prop != null) { + if (ModifiableProperty.class.isAssignableFrom(prop.getClass())) { + try { + return (ModifiableProperty) this.PROPERTY_NAME_TO_RUNTIME_PROPERTY.get(name); + + } catch (ClassCastException ex) { + // TODO improve message + throw ExceptionFactory.createException(WrongArgumentException.class, ex.getMessage(), ex); + } + } + throw ExceptionFactory.createException(PropertyNotModifiableException.class, + Messages.getString("ConnectionProperties.dynamicChangeIsNotAllowed", new Object[] { "'" + prop.getPropertyDefinition().getName() + "'" })); + } + + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ConnectionProperties.notFound", new Object[] { name })); + + } + + @Override + public ModifiableProperty getBooleanModifiableProperty(String name) { + return getModifiableProperty(name); + } + + @Override + public ModifiableProperty getIntegerModifiableProperty(String name) { + return getModifiableProperty(name); + } + + @Override + public ModifiableProperty getLongModifiableProperty(String name) { + return getModifiableProperty(name); + } + + @Override + public ModifiableProperty getMemorySizeModifiableProperty(String name) { + return getModifiableProperty(name); + } + + @Override + public ModifiableProperty getStringModifiableProperty(String name) { + return getModifiableProperty(name); + } + + @Override + public > ModifiableProperty getEnumModifiableProperty(String name) { + return getModifiableProperty(name); + } + + public void initializeProperties(Properties props) { + if (props != null) { + Properties infoCopy = (Properties) props.clone(); + + // TODO do we need to remove next properties (as it was before)? + infoCopy.remove(PropertyDefinitions.HOST_PROPERTY_KEY); + infoCopy.remove(PropertyDefinitions.PNAME_user); + infoCopy.remove(PropertyDefinitions.PNAME_password); + infoCopy.remove(PropertyDefinitions.DBNAME_PROPERTY_KEY); + infoCopy.remove(PropertyDefinitions.PORT_PROPERTY_KEY); + + for (String propName : PropertyDefinitions.PROPERTY_NAME_TO_PROPERTY_DEFINITION.keySet()) { + try { + ReadableProperty propToSet = getReadableProperty(propName); + propToSet.initializeFrom(infoCopy, null); + + } catch (CJException e) { + throw ExceptionFactory.createException(WrongArgumentException.class, e.getMessage(), e); + } + } + + // add user-defined properties + for (Object key : infoCopy.keySet()) { + String val = infoCopy.getProperty((String) key); + PropertyDefinition def = new StringPropertyDefinition((String) key, null, val, PropertyDefinitions.RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.unknown"), "8.0.10", PropertyDefinitions.CATEGORY_USER_DEFINED, Integer.MIN_VALUE); + RuntimeProperty p = new ModifiableStringProperty(def); + addProperty(p); + } + postInitialization(); + } + } + + @Override + public void postInitialization() { + // no-op + } + + @Override + public Properties exposeAsProperties() { + Properties props = new Properties(); + + for (String propName : this.PROPERTY_NAME_TO_RUNTIME_PROPERTY.keySet()) { + if (!props.containsKey(propName)) { + ReadableProperty propToGet = getReadableProperty(propName); + String propValue = propToGet.getStringValue(); + if (propValue != null) { + props.setProperty(propToGet.getPropertyDefinition().getName(), propValue); + } + } + } + return props; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/EnumPropertyDefinition.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/EnumPropertyDefinition.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/EnumPropertyDefinition.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.util.Arrays; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.util.StringUtils; + +public class EnumPropertyDefinition> extends AbstractPropertyDefinition { + + private static final long serialVersionUID = -3297521968759540444L; + + private Class enumType; + + public EnumPropertyDefinition(String name, String alias, T defaultValue, boolean isRuntimeModifiable, String description, String sinceVersion, + String category, int orderInCategory) { + super(name, alias, defaultValue, isRuntimeModifiable, description, sinceVersion, category, orderInCategory); + if (defaultValue == null) { + throw ExceptionFactory.createException("Enum property '" + name + "' cannot be initialized with null."); + } + this.enumType = defaultValue.getDeclaringClass(); + } + + @Override + public String[] getAllowableValues() { + return Arrays.stream(this.enumType.getEnumConstants()).map(T::toString).sorted().toArray(String[]::new); + } + + @Override + public T parseObject(String value, ExceptionInterceptor exceptionInterceptor) { + try { + return Enum.valueOf(this.enumType, value.toUpperCase()); + } catch (Exception e) { + throw ExceptionFactory.createException( + Messages.getString("PropertyDefinition.1", + new Object[] { getName(), StringUtils.stringArrayToString(getAllowableValues(), "'", "', '", "' or '", "'"), value }), + e, exceptionInterceptor); + } + } + + /** + * Creates an instance of ReadableEnumProperty or ModifiableEnumProperty depending on isRuntimeModifiable() result. + * + * @return + */ + @Override + public RuntimeProperty createRuntimeProperty() { + return isRuntimeModifiable() ? new ModifiableEnumProperty<>(this) : new ReadableEnumProperty<>(this); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/HostInfo.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/HostInfo.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/HostInfo.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import static com.mysql.cj.conf.PropertyDefinitions.DBNAME_PROPERTY_KEY; +import static com.mysql.cj.util.StringUtils.isNullOrEmpty; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * This class holds the following MySQL host information: + *
    + *
  • host: an IP or host name. + *
  • port: the port number or 0 if not known. + *
  • user info: a structure containing the user name and password. + *
  • host properties: host specific connection arguments. + *
+ */ +public class HostInfo implements DatabaseUrlContainer { + private static final String HOST_PORT_SEPARATOR = ":"; + + private final DatabaseUrlContainer originalUrl; + private final String host; + private final int port; + private final String user; + private final String password; + private final boolean isPasswordless; + private final Map hostProperties = new HashMap<>(); + + /** + * Constructs an empty {@link HostInfo} instance. + */ + public HostInfo() { + this(null, null, -1, null, null, true, null); + } + + /** + * Constructs a {@link HostInfo} instance initialized with the provided host, port and user info. + * + * @param url + * a reference to the original database URL that produced this host info + * @param host + * the host ip or name + * @param port + * the port + * @param user + * the user name + * @param password + * the user's password + */ + public HostInfo(DatabaseUrlContainer url, String host, int port, String user, String password) { + this(url, host, port, user, password, password == null, null); + } + + /** + * Constructs a {@link HostInfo} instance initialized with the provided host, port, user, password and connection arguments. + * + * @param url + * a reference to the original database URL that produced this host info + * @param host + * the host ip or name + * @param port + * the port + * @param user + * the user name + * @param password + * this user's password + * @param properties + * a connection arguments map. + */ + public HostInfo(DatabaseUrlContainer url, String host, int port, String user, String password, Map properties) { + this(url, host, port, user, password, password == null, properties); + } + + /** + * Constructs a {@link HostInfo} instance initialized with the provided host, port, user, password and connection arguments. + * + * @param url + * a reference to the original database URL that produced this host info + * @param host + * the host ip or name + * @param port + * the port + * @param user + * the user name + * @param password + * this user's password + * @param isPasswordless + * no password was provided in the connection URL or arguments? + * @param properties + * a connection arguments map. + */ + public HostInfo(DatabaseUrlContainer url, String host, int port, String user, String password, boolean isPasswordless, Map properties) { + this.originalUrl = url; + this.host = host; + this.port = port; + this.user = user; + this.password = password; + this.isPasswordless = isPasswordless; + if (properties != null) { + this.hostProperties.putAll(properties); + } + } + + public HostInfo(Properties props) { + this.originalUrl = null; + this.host = props.getProperty(PropertyDefinitions.HOST_PROPERTY_KEY); + this.port = Integer.parseInt(props.getProperty(PropertyDefinitions.PORT_PROPERTY_KEY)); + this.user = props.getProperty(PropertyDefinitions.PNAME_user); + this.password = props.getProperty(PropertyDefinitions.PNAME_password); + this.isPasswordless = this.password == null; + Enumeration keyEnum = props.keys(); + while (keyEnum.hasMoreElements()) { + String key = (String) keyEnum.nextElement(); + this.hostProperties.put(key, props.getProperty(key)); + } + } + + /** + * Returns the host. + * + * @return the host + */ + public String getHost() { + return this.host; + } + + /** + * Returns the port. + * + * @return the port + */ + public int getPort() { + return this.port; + } + + /** + * Returns a host:port representation of this host. + * + * @return the host:port representation of this host + */ + public String getHostPortPair() { + return this.host + HOST_PORT_SEPARATOR + this.port; + } + + /** + * Returns the user name. + * + * @return the user name + */ + public String getUser() { + return this.user; + } + + /** + * Returns the password. + * + * @return the password + */ + public String getPassword() { + return this.password; + } + + /** + * Returns true if the is the default one, i.e., no password was provided in the connection URL or arguments. + * + * @return + * true if no password was provided in the connection URL or arguments. + */ + public boolean isPasswordless() { + return this.isPasswordless; + } + + /** + * Returns the properties specific to this host. + * + * @return this host specific properties + */ + public Map getHostProperties() { + return Collections.unmodifiableMap(this.hostProperties); + } + + /** + * Returns the connection argument for the given key. + * + * @param key + * key + * + * @return the connection argument for the given key + */ + public String getProperty(String key) { + return this.hostProperties.get(key); + } + + /** + * Shortcut to the database connection argument. + * + * @return the database name + */ + public String getDatabase() { + String database = this.hostProperties.get(DBNAME_PROPERTY_KEY); + return isNullOrEmpty(database) ? "" : database; + } + + /** + * Exposes this host info as a single properties instance. The values for host, port, user and password are added to the properties map with their standard + * keys. + * + * @return a {@link Properties} instance containing the full host information. + */ + public Properties exposeAsProperties() { + Properties props = new Properties(); + this.hostProperties.entrySet().stream().forEach(e -> props.setProperty(e.getKey(), e.getValue() == null ? "" : e.getValue())); + props.setProperty(PropertyDefinitions.HOST_PROPERTY_KEY, getHost()); + props.setProperty(PropertyDefinitions.PORT_PROPERTY_KEY, String.valueOf(getPort())); + props.setProperty(PropertyDefinitions.PNAME_user, getUser()); + props.setProperty(PropertyDefinitions.PNAME_password, getPassword()); + return props; + } + + /** + * Returns the original database URL that produced this host info. + * + * @return the original database URL + */ + @Override + public String getDatabaseUrl() { + return this.originalUrl != null ? this.originalUrl.getDatabaseUrl() : ""; + } + + /** + * Returns a string representation of this object. + * + * @return a string representation of this object + */ + @Override + public String toString() { + StringBuilder asStr = new StringBuilder(super.toString()); + asStr.append(String.format(" :: {host: \"%s\", port: %d, user: %s, password: %s, hostProperties: %s}", this.host, this.port, this.user, this.password, + this.hostProperties)); + return asStr.toString(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/IntegerPropertyDefinition.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/IntegerPropertyDefinition.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/IntegerPropertyDefinition.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.WrongArgumentException; + +public class IntegerPropertyDefinition extends AbstractPropertyDefinition { + + private static final long serialVersionUID = 4151893695173946081L; + + protected int multiplier = 1; + + public IntegerPropertyDefinition(String name, String alias, int defaultValue, boolean isRuntimeModifiable, String description, String sinceVersion, + String category, int orderInCategory) { + super(name, alias, Integer.valueOf(defaultValue), isRuntimeModifiable, description, sinceVersion, category, orderInCategory); + } + + public IntegerPropertyDefinition(String name, String alias, int defaultValue, boolean isRuntimeModifiable, String description, String sinceVersion, + String category, int orderInCategory, int lowerBound, int upperBound) { + super(name, alias, Integer.valueOf(defaultValue), isRuntimeModifiable, description, sinceVersion, category, orderInCategory, lowerBound, upperBound); + } + + @Override + public boolean isRangeBased() { + return getUpperBound() != getLowerBound(); + } + + @Override + public Integer parseObject(String value, ExceptionInterceptor exceptionInterceptor) { + try { + // Parse decimals, too + int intValue = (int) (Double.valueOf(value).doubleValue() * this.multiplier); + + return intValue; + + } catch (NumberFormatException nfe) { + throw ExceptionFactory.createException(WrongArgumentException.class, + "The connection property '" + getName() + "' only accepts integer values. The value '" + value + "' can not be converted to an integer.", + exceptionInterceptor); + } + } + + /** + * Creates instance of ReadableIntegerProperty or ModifiableIntegerProperty depending on isRuntimeModifiable() result. + * + * @return + */ + @Override + public RuntimeProperty createRuntimeProperty() { + return isRuntimeModifiable() ? new ModifiableIntegerProperty(this) : new ReadableIntegerProperty(this); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/LongPropertyDefinition.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/LongPropertyDefinition.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/LongPropertyDefinition.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.WrongArgumentException; + +public class LongPropertyDefinition extends AbstractPropertyDefinition { + + private static final long serialVersionUID = -5264490959206230852L; + + public LongPropertyDefinition(String name, String alias, long defaultValue, boolean isRuntimeModifiable, String description, String sinceVersion, + String category, int orderInCategory) { + super(name, alias, Long.valueOf(defaultValue), isRuntimeModifiable, description, sinceVersion, category, orderInCategory); + } + + public LongPropertyDefinition(String name, String alias, long defaultValue, boolean isRuntimeModifiable, String description, String sinceVersion, + String category, int orderInCategory, long lowerBound, long upperBound) { + super(name, alias, Long.valueOf(defaultValue), isRuntimeModifiable, description, sinceVersion, category, orderInCategory, (int) lowerBound, + (int) upperBound); + } + + @Override + public Long parseObject(String value, ExceptionInterceptor exceptionInterceptor) { + try { + // Parse decimals, too + return Double.valueOf(value).longValue(); + + } catch (NumberFormatException nfe) { + throw ExceptionFactory.createException(WrongArgumentException.class, "The connection property '" + getName() + + "' only accepts long integer values. The value '" + value + "' can not be converted to a long integer.", exceptionInterceptor); + } + } + + @Override + public boolean isRangeBased() { + return getUpperBound() != getLowerBound(); + } + + /** + * Creates instance of ReadableLongProperty or ModifiableLongProperty depending on isRuntimeModifiable() result. + * + * @return + */ + @Override + public RuntimeProperty createRuntimeProperty() { + return isRuntimeModifiable() ? new ModifiableLongProperty(this) : new ReadableLongProperty(this); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/MemorySizePropertyDefinition.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/MemorySizePropertyDefinition.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/MemorySizePropertyDefinition.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.util.StringUtils; + +public class MemorySizePropertyDefinition extends IntegerPropertyDefinition { + + private static final long serialVersionUID = -6878680905514177949L; + + public MemorySizePropertyDefinition(String name, String alias, int defaultValue, boolean isRuntimeModifiable, String description, String sinceVersion, + String category, int orderInCategory) { + super(name, alias, defaultValue, isRuntimeModifiable, description, sinceVersion, category, orderInCategory); + } + + public MemorySizePropertyDefinition(String name, String alias, int defaultValue, boolean isRuntimeModifiable, String description, String sinceVersion, + String category, int orderInCategory, int lowerBound, int upperBound) { + super(name, alias, defaultValue, isRuntimeModifiable, description, sinceVersion, category, orderInCategory, lowerBound, upperBound); + } + + @Override + public Integer parseObject(String value, ExceptionInterceptor exceptionInterceptor) { + this.multiplier = 1; + + if (value.endsWith("k") || value.endsWith("K") || value.endsWith("kb") || value.endsWith("Kb") || value.endsWith("kB") || value.endsWith("KB")) { + this.multiplier = 1024; + int indexOfK = StringUtils.indexOfIgnoreCase(value, "k"); + value = value.substring(0, indexOfK); + } else if (value.endsWith("m") || value.endsWith("M") || value.endsWith("mb") || value.endsWith("Mb") || value.endsWith("mB") || value.endsWith("MB")) { + this.multiplier = 1024 * 1024; + int indexOfM = StringUtils.indexOfIgnoreCase(value, "m"); + value = value.substring(0, indexOfM); + } else if (value.endsWith("g") || value.endsWith("G") || value.endsWith("gb") || value.endsWith("Gb") || value.endsWith("gB") || value.endsWith("GB")) { + this.multiplier = 1024 * 1024 * 1024; + int indexOfG = StringUtils.indexOfIgnoreCase(value, "g"); + value = value.substring(0, indexOfG); + } + + return super.parseObject(value, exceptionInterceptor); + } + + /** + * Creates instance of ReadableMemorySizeProperty or ModifiableMemorySizeProperty depending on isRuntimeModifiable() result. + * + * @return + */ + @Override + public RuntimeProperty createRuntimeProperty() { + return isRuntimeModifiable() ? new ModifiableMemorySizeProperty(this) : new ReadableMemorySizeProperty(this); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableBooleanProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableBooleanProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableBooleanProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.io.Serializable; + +import com.mysql.cj.exceptions.ExceptionInterceptor; + +public class ModifiableBooleanProperty extends ReadableBooleanProperty implements ModifiableProperty, Serializable { + + private static final long serialVersionUID = 7810312684423192133L; + + protected ModifiableBooleanProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + } + + @Override + protected void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterceptor) { + super.initializeFrom(extractedValue, exceptionInterceptor); + this.initialValue = this.value; + } + + @Override + public void setValue(Boolean value) { + setValue(value, null); + } + + @Override + public void setValue(Boolean value, ExceptionInterceptor exceptionInterceptor) { + this.value = value; + this.wasExplicitlySet = true; + invokeListeners(); + } + + @Override + public void resetValue() { + this.value = this.initialValue; + invokeListeners(); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableEnumProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableEnumProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableEnumProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import com.mysql.cj.exceptions.ExceptionInterceptor; + +public class ModifiableEnumProperty> extends ReadableEnumProperty implements ModifiableProperty { + private static final long serialVersionUID = -7498397533757779213L; + + public ModifiableEnumProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + } + + @Override + public void setValue(T value) { + setValue(value, null); + } + + @Override + public void setValue(T value, ExceptionInterceptor exceptionInterceptor) { + this.value = value; + this.wasExplicitlySet = true; + invokeListeners(); + } + + @Override + public void resetValue() { + this.value = this.initialValue; + invokeListeners(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableIntegerProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableIntegerProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableIntegerProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.io.Serializable; + +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.WrongArgumentException; + +public class ModifiableIntegerProperty extends ReadableIntegerProperty implements ModifiableProperty, Serializable { + + private static final long serialVersionUID = 1954410331604145901L; + + protected ModifiableIntegerProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + } + + @Override + protected void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterceptor) { + super.initializeFrom(extractedValue, exceptionInterceptor); + this.initialValue = this.value; + } + + @Override + public void setFromString(String value, ExceptionInterceptor exceptionInterceptor) { + setValue(getPropertyDefinition().parseObject(value, exceptionInterceptor), value, exceptionInterceptor); + } + + @Override + public void setValue(Integer value) { + setValue(value, null, null); + } + + @Override + public void setValue(Integer value, ExceptionInterceptor exceptionInterceptor) { + setValue(value, null, exceptionInterceptor); + } + + private void setValue(int intValue, String valueAsString, ExceptionInterceptor exceptionInterceptor) { + if (getPropertyDefinition().isRangeBased()) { + if ((intValue < getPropertyDefinition().getLowerBound()) || (intValue > getPropertyDefinition().getUpperBound())) { + throw ExceptionFactory.createException(WrongArgumentException.class, + "The connection property '" + getPropertyDefinition().getName() + "' only accepts integer values in the range of " + + getPropertyDefinition().getLowerBound() + " - " + getPropertyDefinition().getUpperBound() + ", the value '" + + (valueAsString == null ? intValue : valueAsString) + "' exceeds this range.", + exceptionInterceptor); + } + } + + this.value = Integer.valueOf(intValue); + this.wasExplicitlySet = true; + invokeListeners(); + } + + @Override + public void resetValue() { + this.value = this.initialValue; + invokeListeners(); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableLongProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableLongProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableLongProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.io.Serializable; + +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.WrongArgumentException; + +public class ModifiableLongProperty extends ReadableLongProperty implements ModifiableProperty, Serializable { + + private static final long serialVersionUID = 2870949628194348648L; + + protected ModifiableLongProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + } + + @Override + protected void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterceptor) { + super.initializeFrom(extractedValue, exceptionInterceptor); + this.initialValue = this.value; + } + + @Override + public void setFromString(String value, ExceptionInterceptor exceptionInterceptor) { + setValue(getPropertyDefinition().parseObject(value, exceptionInterceptor), value, exceptionInterceptor); + } + + @Override + public void setValue(Long longValue) { + setValue(longValue, null, null); + } + + @Override + public void setValue(Long longValue, ExceptionInterceptor exceptionInterceptor) { + setValue(longValue, null, exceptionInterceptor); + } + + void setValue(long longValue, String valueAsString, ExceptionInterceptor exceptionInterceptor) { + if (getPropertyDefinition().isRangeBased()) { + if ((longValue < getPropertyDefinition().getLowerBound()) || (longValue > getPropertyDefinition().getUpperBound())) { + throw ExceptionFactory.createException(WrongArgumentException.class, + "The connection property '" + getPropertyDefinition().getName() + "' only accepts long integer values in the range of " + + getPropertyDefinition().getLowerBound() + " - " + getPropertyDefinition().getUpperBound() + ", the value '" + + (valueAsString == null ? longValue : valueAsString) + "' exceeds this range.", + exceptionInterceptor); + } + } + this.value = Long.valueOf(longValue); + this.wasExplicitlySet = true; + invokeListeners(); + } + + @Override + public void resetValue() { + this.value = this.initialValue; + invokeListeners(); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableMemorySizeProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableMemorySizeProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableMemorySizeProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.io.Serializable; + +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.WrongArgumentException; + +public class ModifiableMemorySizeProperty extends ReadableMemorySizeProperty implements ModifiableProperty, Serializable { + + private static final long serialVersionUID = -8018059699460539279L; + + private String initialValueAsString; + + protected ModifiableMemorySizeProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + } + + @Override + public void setFromString(String value, ExceptionInterceptor exceptionInterceptor) { + setValue(((MemorySizePropertyDefinition) getPropertyDefinition()).parseObject(value, exceptionInterceptor), value, exceptionInterceptor); + this.valueAsString = value; + } + + @Override + protected void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterceptor) { + super.initializeFrom(extractedValue, exceptionInterceptor); + this.initialValue = this.value; + this.initialValueAsString = this.valueAsString; + } + + @Override + public void setValue(Integer value) { + setValue(value, null, null); + } + + @Override + public void setValue(Integer value, ExceptionInterceptor exceptionInterceptor) { + setValue(value, null, exceptionInterceptor); + } + + void setValue(int intValue, String valueAsString, ExceptionInterceptor exceptionInterceptor) { + if (getPropertyDefinition().isRangeBased()) { + if ((intValue < getPropertyDefinition().getLowerBound()) || (intValue > getPropertyDefinition().getUpperBound())) { + throw ExceptionFactory.createException(WrongArgumentException.class, + "The connection property '" + getPropertyDefinition().getName() + "' only accepts integer values in the range of " + + getPropertyDefinition().getLowerBound() + " - " + getPropertyDefinition().getUpperBound() + ", the value '" + + (valueAsString == null ? intValue : valueAsString) + "' exceeds this range.", + exceptionInterceptor); + } + } + + this.value = Integer.valueOf(intValue); + this.valueAsString = valueAsString == null ? String.valueOf(intValue) : valueAsString; + this.wasExplicitlySet = true; + invokeListeners(); + } + + @Override + public void resetValue() { + this.value = this.initialValue; + this.valueAsString = this.initialValueAsString; + invokeListeners(); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import com.mysql.cj.exceptions.ExceptionInterceptor; + +public interface ModifiableProperty extends ReadableProperty { + + /** + * Set the value of a property from a string value. + * It involves the {@link PropertyDefinition#parseObject(String, ExceptionInterceptor)} to validate and parse the string. + * + * @param value + * value + * @param exceptionInterceptor + * exception interceptor + */ + void setFromString(String value, ExceptionInterceptor exceptionInterceptor); + + /** + * Set the object value of a property directly. Validation against allowable values will be performed. + * + * @param value + * value + */ + void setValue(T value); + + /** + * Set the object value of a property directly. Validation against allowable values will be performed. + * + * @param value + * value + * @param exceptionInterceptor + * exception interceptor + */ + void setValue(T value, ExceptionInterceptor exceptionInterceptor); + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableStringProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableStringProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ModifiableStringProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import com.mysql.cj.exceptions.ExceptionInterceptor; + +public class ModifiableStringProperty extends ReadableStringProperty implements ModifiableProperty { + + private static final long serialVersionUID = -3956001600419271415L; + + protected ModifiableStringProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + } + + @Override + protected void initializeFrom(String extractedValue, ExceptionInterceptor exceptionInterceptor) { + super.initializeFrom(extractedValue, exceptionInterceptor); + this.initialValue = this.value; + } + + @Override + public void setValue(String value) { + setValue(value, null); + } + + @Override + public void setValue(String value, ExceptionInterceptor exceptionInterceptor) { + setFromString(value, exceptionInterceptor); + invokeListeners(); + } + + @Override + public void resetValue() { + this.value = this.initialValue; + invokeListeners(); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/PropertyDefinition.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/PropertyDefinition.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/PropertyDefinition.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import com.mysql.cj.exceptions.ExceptionInterceptor; + +public interface PropertyDefinition { + + /** + * Does the property have fixed values based constraints. + * + * @return true if property has fixed values based constraints. + */ + boolean hasValueConstraints(); + + /** + * Returns true if property has range-based constraints + * + * @return true if property has range-based constraints + */ + boolean isRangeBased(); + + /** + * Returns the property name. + * + * @return the property name + */ + String getName(); + + /** + * Returns the property camel-case alias. + * + * @return the property camel-case alias. + */ + String getCcAlias(); + + /** + * Returns true if property has a camel-case alias. + * + * @return true if property has a camel-case alias. + */ + boolean hasCcAlias(); + + /** + * Returns the default value. + * + * @return default value + */ + T getDefaultValue(); + + /** + * May the property be changed after initialization. + * + * @return true if the property value may be changed after initialization. + */ + boolean isRuntimeModifiable(); + + /** + * Returns the property description. Used for documentation. + * + * @return property description + */ + String getDescription(); + + /** + * Returns the driver version where the property was introduced first. Used for documentation. + * + * @return the driver version where the property was introduced first + */ + String getSinceVersion(); + + /** + * Returns the property category. + * + * @return property category + */ + String getCategory(); + + /** + * Returns the property order. Used as preferred property position in properties table in documentation. + * + * @return property order + */ + int getOrder(); + + /** + * Returns the list of allowable values. + * + * @return the list of allowable values + */ + String[] getAllowableValues(); + + /** + * The lowest possible value of range-based property + * + * @return the lowest possible value of range-based property + */ + int getLowerBound(); + + /** + * The highest possible value of range-based property + * + * @return the highest possible value of range-based property + */ + int getUpperBound(); + + /** + * Returns the value object parsed from it's string representation and checked against allowable values. + * + * @param value + * value + * @param exceptionInterceptor + * exception interceptor + * + * @return the value object + */ + T parseObject(String value, ExceptionInterceptor exceptionInterceptor); + + /** + * Creates instance of ReadableProperty or ModifiableProperty depending on isRuntimeModifiable() result. + * + * @return {@link RuntimeProperty} instance + */ + RuntimeProperty createRuntimeProperty(); + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/PropertyDefinitions.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/PropertyDefinitions.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/PropertyDefinitions.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,1097 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import com.mysql.cj.Messages; +import com.mysql.cj.PerConnectionLRUFactory; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.log.Log; +import com.mysql.cj.log.StandardLogger; +import com.mysql.cj.protocol.SocketFactory; +import com.mysql.cj.util.PerVmServerConfigCacheFactory; + +public class PropertyDefinitions { + /* + * Built-in system properties + */ + public static final String SYSP_line_separator = "line.separator"; + public static final String SYSP_java_vendor = "java.vendor"; + public static final String SYSP_java_version = "java.version"; + public static final String SYSP_java_vm_vendor = "java.vm.vendor"; + public static final String SYSP_os_name = "os.name"; + public static final String SYSP_os_arch = "os.arch"; + public static final String SYSP_os_version = "os.version"; + public static final String SYSP_file_encoding = "file.encoding"; + + /* + * Custom system properties + */ + public static final String SYSP_testsuite_url /* */ = "com.mysql.cj.testsuite.url"; + public final static String SYSP_testsuite_url_admin /* */ = "com.mysql.cj.testsuite.url.admin"; + public static final String SYSP_testsuite_url_cluster /* */ = "com.mysql.cj.testsuite.url.cluster"; + /** Connection string to server compiled with OpenSSL */ + public static final String SYSP_testsuite_url_openssl /* */ = "com.mysql.cj.testsuite.url.openssl"; + + public static final String SYSP_testsuite_url_mysqlx /* */ = "com.mysql.cj.testsuite.mysqlx.url"; + public static final String SYSP_testsuite_url_mysqlx_openssl /* */ = "com.mysql.cj.testsuite.mysqlx.url.openssl"; + + public static final String SYSP_testsuite_cantGrant /* */ = "com.mysql.cj.testsuite.cantGrant"; + public static final String SYSP_testsuite_disable_multihost_tests /* */ = "com.mysql.cj.testsuite.disable.multihost.tests"; // TODO should be more specific for different types of multi-host configs + + /** For testsuite.regression.DataSourceRegressionTest */ + public static final String SYSP_testsuite_ds_host /* */ = "com.mysql.cj.testsuite.ds.host"; + /** For testsuite.regression.DataSourceRegressionTest */ + public static final String SYSP_testsuite_ds_port /* */ = "com.mysql.cj.testsuite.ds.port"; + /** For testsuite.regression.DataSourceRegressionTest */ + public static final String SYSP_testsuite_ds_db /* */ = "com.mysql.cj.testsuite.ds.db"; + /** For testsuite.regression.DataSourceRegressionTest */ + public static final String SYSP_testsuite_ds_user /* */ = "com.mysql.cj.testsuite.ds.user"; + /** For testsuite.regression.DataSourceRegressionTest */ + public static final String SYSP_testsuite_ds_password /* */ = "com.mysql.cj.testsuite.ds.password"; + + /** For testsuite.perf.LoadStorePerfTest */ + public static final String SYSP_testsuite_loadstoreperf_tabletype /* */ = "com.mysql.cj.testsuite.loadstoreperf.tabletype"; // TODO document allowed types + /** For testsuite.perf.LoadStorePerfTest */ + public static final String SYSP_testsuite_loadstoreperf_useBigResults /* */ = "com.mysql.cj.testsuite.loadstoreperf.useBigResults"; + + /** The system property that must exist to run the shutdown test in testsuite.simple.MiniAdminTest */ + public static final String SYSP_testsuite_miniAdminTest_runShutdown /* */ = "com.mysql.cj.testsuite.miniAdminTest.runShutdown"; + + /** Suppress debug output when running testsuite */ + public static final String SYSP_testsuite_noDebugOutput /* */ = "com.mysql.cj.testsuite.noDebugOutput"; + /** Don't remove database object created by tests */ + public static final String SYSP_testsuite_retainArtifacts /* */ = "com.mysql.cj.testsuite.retainArtifacts"; + public static final String SYSP_testsuite_runLongTests /* */ = "com.mysql.cj.testsuite.runLongTests"; + public static final String SYSP_testsuite_serverController_basedir /* */ = "com.mysql.cj.testsuite.serverController.basedir"; + + /* + * Properties added internally after parsing connection string + */ + public static final String PROTOCOL_PROPERTY_KEY = "PROTOCOL"; + public static final String PATH_PROPERTY_KEY = "PATH"; + public final static String TYPE_PROPERTY_KEY = "TYPE"; + /** Key used to retrieve the hostname value from the properties instance passed to the driver. */ + public static final String HOST_PROPERTY_KEY = "HOST"; + /** Key used to retrieve the port number value from the properties instance passed to the driver. */ + public static final String PORT_PROPERTY_KEY = "PORT"; + /** Key used to retrieve the database value from the properties instance passed to the driver. */ + public static final String DBNAME_PROPERTY_KEY = "DBNAME"; + /** Key used to retrieve the address value ("host:port") from the properties instance passed to the driver. */ + public static final String ADDRESS_PROPERTY_KEY = "ADDRESS"; + /** Key used to retried the host priority in a list of hosts. */ + public static final String PRIORITY_PROPERTY_KEY = "PRIORITY"; + + public static final String NAMED_PIPE_PROP_NAME = "namedPipePath"; + + /* + * Categories of connection properties + */ + public static final String CATEGORY_AUTH = Messages.getString("ConnectionProperties.categoryAuthentication"); + public static final String CATEGORY_CONNECTION = Messages.getString("ConnectionProperties.categoryConnection"); + public static final String CATEGORY_SESSION = Messages.getString("ConnectionProperties.categorySession"); + public static final String CATEGORY_NETWORK = Messages.getString("ConnectionProperties.categoryNetworking"); + public static final String CATEGORY_SECURITY = Messages.getString("ConnectionProperties.categorySecurity"); + public static final String CATEGORY_STATEMENTS = Messages.getString("ConnectionProperties.categoryStatements"); + public static final String CATEGORY_PREPARED_STATEMENTS = Messages.getString("ConnectionProperties.categoryPreparedStatements"); + public static final String CATEGORY_RESULT_SETS = Messages.getString("ConnectionProperties.categoryResultSets"); + public static final String CATEGORY_METADATA = Messages.getString("ConnectionProperties.categoryMetadata"); + public static final String CATEGORY_BLOBS = Messages.getString("ConnectionProperties.categoryBlobs"); + public static final String CATEGORY_DATETIMES = Messages.getString("ConnectionProperties.categoryDatetimes"); + public static final String CATEGORY_HA = Messages.getString("ConnectionProperties.categoryHA"); + public static final String CATEGORY_PERFORMANCE = Messages.getString("ConnectionProperties.categoryPerformance"); + public static final String CATEGORY_DEBUGING_PROFILING = Messages.getString("ConnectionProperties.categoryDebuggingProfiling"); + public static final String CATEGORY_EXCEPTIONS = Messages.getString("ConnectionProperties.categoryExceptions"); + public static final String CATEGORY_INTEGRATION = Messages.getString("ConnectionProperties.categoryIntegration"); + public static final String CATEGORY_JDBC = Messages.getString("ConnectionProperties.categoryJDBC"); + public static final String CATEGORY_XDEVAPI = Messages.getString("ConnectionProperties.categoryXDevAPI"); + public static final String CATEGORY_USER_DEFINED = Messages.getString("ConnectionProperties.categoryUserDefined"); + + public static final String[] PROPERTY_CATEGORIES = new String[] { CATEGORY_AUTH, CATEGORY_CONNECTION, CATEGORY_SESSION, CATEGORY_NETWORK, CATEGORY_SECURITY, + CATEGORY_STATEMENTS, CATEGORY_PREPARED_STATEMENTS, CATEGORY_RESULT_SETS, CATEGORY_METADATA, CATEGORY_BLOBS, CATEGORY_DATETIMES, CATEGORY_HA, + CATEGORY_PERFORMANCE, CATEGORY_DEBUGING_PROFILING, CATEGORY_EXCEPTIONS, CATEGORY_INTEGRATION, CATEGORY_JDBC, CATEGORY_XDEVAPI }; + + /* + * Property modifiers + */ + public static final boolean DEFAULT_VALUE_TRUE = true; + public static final boolean DEFAULT_VALUE_FALSE = false; + public static final String DEFAULT_VALUE_NULL_STRING = null; + public static final String NO_ALIAS = null; + + /** is modifiable in run-time */ + public static final boolean RUNTIME_MODIFIABLE = true; + + /** is not modifiable in run-time (will allow to set not-null value only once) */ + public static final boolean RUNTIME_NOT_MODIFIABLE = false; + + /* + * Property enums + */ + public enum ZeroDatetimeBehavior { // zeroDateTimeBehavior + CONVERT_TO_NULL, EXCEPTION, ROUND; + } + + public enum SslMode { // xdevapi.ssl-mode + REQUIRED, VERIFY_CA, VERIFY_IDENTITY, DISABLED; + } + + public enum AuthMech { // xdevapi.auth + PLAIN, MYSQL41, SHA256_MEMORY, EXTERNAL; + } + + /* + * Connection properties names + */ + public static final Map> PROPERTY_NAME_TO_PROPERTY_DEFINITION; + + public static final String PNAME_paranoid = "paranoid"; + public static final String PNAME_passwordCharacterEncoding = "passwordCharacterEncoding"; + public static final String PNAME_serverRSAPublicKeyFile = "serverRSAPublicKeyFile"; + public static final String PNAME_allowPublicKeyRetrieval = "allowPublicKeyRetrieval"; + public static final String PNAME_clientCertificateKeyStoreUrl = "clientCertificateKeyStoreUrl"; + public static final String PNAME_trustCertificateKeyStoreUrl = "trustCertificateKeyStoreUrl"; + public static final String PNAME_clientCertificateKeyStoreType = "clientCertificateKeyStoreType"; + public static final String PNAME_clientCertificateKeyStorePassword = "clientCertificateKeyStorePassword"; + public static final String PNAME_trustCertificateKeyStoreType = "trustCertificateKeyStoreType"; + public static final String PNAME_trustCertificateKeyStorePassword = "trustCertificateKeyStorePassword"; + public static final String PNAME_verifyServerCertificate = "verifyServerCertificate"; + public static final String PNAME_enabledSSLCipherSuites = "enabledSSLCipherSuites"; + public static final String PNAME_enabledTLSProtocols = "enabledTLSProtocols"; + public static final String PNAME_useUnbufferedInput = "useUnbufferedInput"; + public static final String PNAME_profilerEventHandler = "profilerEventHandler"; + public static final String PNAME_allowLoadLocalInfile = "allowLoadLocalInfile"; + public static final String PNAME_allowMultiQueries = "allowMultiQueries"; + public static final String PNAME_allowNanAndInf = "allowNanAndInf"; + public static final String PNAME_allowUrlInLocalInfile = "allowUrlInLocalInfile"; + public static final String PNAME_alwaysSendSetIsolation = "alwaysSendSetIsolation"; + public static final String PNAME_autoClosePStmtStreams = "autoClosePStmtStreams"; + public static final String PNAME_allowMasterDownConnections = "allowMasterDownConnections"; + public static final String PNAME_allowSlaveDownConnections = "allowSlaveDownConnections"; + public static final String PNAME_readFromMasterWhenNoSlaves = "readFromMasterWhenNoSlaves"; + public static final String PNAME_autoDeserialize = "autoDeserialize"; + public static final String PNAME_autoGenerateTestcaseScript = "autoGenerateTestcaseScript"; + public static final String PNAME_autoReconnect = "autoReconnect"; + public static final String PNAME_autoReconnectForPools = "autoReconnectForPools"; + public static final String PNAME_blobSendChunkSize = "blobSendChunkSize"; + public static final String PNAME_autoSlowLog = "autoSlowLog"; + public static final String PNAME_blobsAreStrings = "blobsAreStrings"; + public static final String PNAME_functionsNeverReturnBlobs = "functionsNeverReturnBlobs"; + public static final String PNAME_cacheCallableStmts = "cacheCallableStmts"; + public static final String PNAME_cachePrepStmts = "cachePrepStmts"; + public static final String PNAME_cacheResultSetMetadata = "cacheResultSetMetadata"; + public static final String PNAME_serverConfigCacheFactory = "serverConfigCacheFactory"; + public static final String PNAME_cacheServerConfiguration = "cacheServerConfiguration"; + public static final String PNAME_callableStmtCacheSize = "callableStmtCacheSize"; + public static final String PNAME_characterEncoding = "characterEncoding"; + public static final String PNAME_characterSetResults = "characterSetResults"; + public static final String PNAME_connectionAttributes = "connectionAttributes"; + public static final String PNAME_clientInfoProvider = "clientInfoProvider"; + public static final String PNAME_clobberStreamingResults = "clobberStreamingResults"; + public static final String PNAME_clobCharacterEncoding = "clobCharacterEncoding"; + public static final String PNAME_compensateOnDuplicateKeyUpdateCounts = "compensateOnDuplicateKeyUpdateCounts"; + public static final String PNAME_connectionCollation = "connectionCollation"; + public static final String PNAME_connectionLifecycleInterceptors = "connectionLifecycleInterceptors"; + public static final String PNAME_connectTimeout = "connectTimeout"; + public static final String PNAME_continueBatchOnError = "continueBatchOnError"; + public static final String PNAME_createDatabaseIfNotExist = "createDatabaseIfNotExist"; + public static final String PNAME_defaultFetchSize = "defaultFetchSize"; + public static final String PNAME_useServerPrepStmts = "useServerPrepStmts"; + public static final String PNAME_dontTrackOpenResources = "dontTrackOpenResources"; + public static final String PNAME_dumpQueriesOnException = "dumpQueriesOnException"; + public static final String PNAME_elideSetAutoCommits = "elideSetAutoCommits"; + public static final String PNAME_emptyStringsConvertToZero = "emptyStringsConvertToZero"; + public static final String PNAME_emulateLocators = "emulateLocators"; + public static final String PNAME_emulateUnsupportedPstmts = "emulateUnsupportedPstmts"; + public static final String PNAME_enablePacketDebug = "enablePacketDebug"; + public static final String PNAME_enableQueryTimeouts = "enableQueryTimeouts"; + public static final String PNAME_explainSlowQueries = "explainSlowQueries"; + public static final String PNAME_exceptionInterceptors = "exceptionInterceptors"; + public static final String PNAME_failOverReadOnly = "failOverReadOnly"; + + public static final String PNAME_gatherPerfMetrics = "gatherPerfMetrics"; + public static final String PNAME_generateSimpleParameterMetadata = "generateSimpleParameterMetadata"; + public static final String PNAME_holdResultsOpenOverStatementClose = "holdResultsOpenOverStatementClose"; + public static final String PNAME_includeInnodbStatusInDeadlockExceptions = "includeInnodbStatusInDeadlockExceptions"; + public static final String PNAME_includeThreadDumpInDeadlockExceptions = "includeThreadDumpInDeadlockExceptions"; + public static final String PNAME_includeThreadNamesAsStatementComment = "includeThreadNamesAsStatementComment"; + public static final String PNAME_ignoreNonTxTables = "ignoreNonTxTables"; + public static final String PNAME_initialTimeout = "initialTimeout"; + public static final String PNAME_interactiveClient = "interactiveClient"; + public static final String PNAME_jdbcCompliantTruncation = "jdbcCompliantTruncation"; + public static final String PNAME_largeRowSizeThreshold = "largeRowSizeThreshold"; + public static final String PNAME_loadBalanceStrategy = "ha.loadBalanceStrategy"; + public static final String PNAME_loadBalanceBlacklistTimeout = "loadBalanceBlacklistTimeout"; + public static final String PNAME_loadBalancePingTimeout = "loadBalancePingTimeout"; + public static final String PNAME_loadBalanceValidateConnectionOnSwapServer = "loadBalanceValidateConnectionOnSwapServer"; + public static final String PNAME_loadBalanceConnectionGroup = "loadBalanceConnectionGroup"; + public static final String PNAME_loadBalanceExceptionChecker = "loadBalanceExceptionChecker"; + public static final String PNAME_loadBalanceSQLStateFailover = "loadBalanceSQLStateFailover"; + public static final String PNAME_loadBalanceSQLExceptionSubclassFailover = "loadBalanceSQLExceptionSubclassFailover"; + public static final String PNAME_loadBalanceAutoCommitStatementRegex = "loadBalanceAutoCommitStatementRegex"; + public static final String PNAME_loadBalanceAutoCommitStatementThreshold = "loadBalanceAutoCommitStatementThreshold"; + public static final String PNAME_localSocketAddress = "localSocketAddress"; + public static final String PNAME_locatorFetchBufferSize = "locatorFetchBufferSize"; + public static final String PNAME_logger = "logger"; + + public static final String PNAME_logSlowQueries = "logSlowQueries"; + public static final String PNAME_logXaCommands = "logXaCommands"; + public static final String PNAME_maintainTimeStats = "maintainTimeStats"; + public static final String PNAME_maxQuerySizeToLog = "maxQuerySizeToLog"; + public static final String PNAME_maxReconnects = "maxReconnects"; + public static final String PNAME_retriesAllDown = "retriesAllDown"; + public static final String PNAME_maxRows = "maxRows"; + public static final String PNAME_metadataCacheSize = "metadataCacheSize"; + public static final String PNAME_netTimeoutForStreamingResults = "netTimeoutForStreamingResults"; + public static final String PNAME_noAccessToProcedureBodies = "noAccessToProcedureBodies"; + public static final String PNAME_noDatetimeStringSync = "noDatetimeStringSync"; + public static final String PNAME_nullCatalogMeansCurrent = "nullCatalogMeansCurrent"; + public static final String PNAME_packetDebugBufferSize = "packetDebugBufferSize"; + public static final String PNAME_padCharsWithSpace = "padCharsWithSpace"; + public static final String PNAME_pedantic = "pedantic"; + public static final String PNAME_pinGlobalTxToPhysicalConnection = "pinGlobalTxToPhysicalConnection"; + public static final String PNAME_populateInsertRowWithDefaultValues = "populateInsertRowWithDefaultValues"; + public static final String PNAME_prepStmtCacheSize = "prepStmtCacheSize"; + public static final String PNAME_prepStmtCacheSqlLimit = "prepStmtCacheSqlLimit"; + public static final String PNAME_parseInfoCacheFactory = "parseInfoCacheFactory"; + public static final String PNAME_processEscapeCodesForPrepStmts = "processEscapeCodesForPrepStmts"; + public static final String PNAME_profileSQL = "profileSQL"; + public static final String PNAME_propertiesTransform = "propertiesTransform"; + public static final String PNAME_queriesBeforeRetryMaster = "queriesBeforeRetryMaster"; + public static final String PNAME_queryTimeoutKillsConnection = "queryTimeoutKillsConnection"; + public static final String PNAME_reconnectAtTxEnd = "reconnectAtTxEnd"; + public static final String PNAME_reportMetricsIntervalMillis = "reportMetricsIntervalMillis"; + public static final String PNAME_requireSSL = "requireSSL"; + public static final String PNAME_resourceId = "resourceId"; + public static final String PNAME_resultSetSizeThreshold = "resultSetSizeThreshold"; + public static final String PNAME_rewriteBatchedStatements = "rewriteBatchedStatements"; + public static final String PNAME_rollbackOnPooledClose = "rollbackOnPooledClose"; + public static final String PNAME_secondsBeforeRetryMaster = "secondsBeforeRetryMaster"; + public static final String PNAME_selfDestructOnPingSecondsLifetime = "selfDestructOnPingSecondsLifetime"; + public static final String PNAME_selfDestructOnPingMaxOperations = "selfDestructOnPingMaxOperations"; + public static final String PNAME_ha_enableJMX = "ha.enableJMX"; + public static final String PNAME_loadBalanceHostRemovalGracePeriod = "loadBalanceHostRemovalGracePeriod"; + public static final String PNAME_serverTimezone = "serverTimezone"; + public static final String PNAME_sessionVariables = "sessionVariables"; + public static final String PNAME_slowQueryThresholdMillis = "slowQueryThresholdMillis"; + public static final String PNAME_slowQueryThresholdNanos = "slowQueryThresholdNanos"; + public static final String PNAME_socketFactory = "socketFactory"; + public static final String PNAME_socksProxyHost = "socksProxyHost"; + public static final String PNAME_socksProxyPort = "socksProxyPort"; + public static final String PNAME_socketTimeout = "socketTimeout"; + public static final String PNAME_queryInterceptors = "queryInterceptors"; + public static final String PNAME_strictUpdates = "strictUpdates"; + public static final String PNAME_overrideSupportsIntegrityEnhancementFacility = "overrideSupportsIntegrityEnhancementFacility"; + public static final String PNAME_tcpNoDelay = "tcpNoDelay"; + public static final String PNAME_tcpKeepAlive = "tcpKeepAlive"; + public static final String PNAME_tcpRcvBuf = "tcpRcvBuf"; + public static final String PNAME_tcpSndBuf = "tcpSndBuf"; + public static final String PNAME_tcpTrafficClass = "tcpTrafficClass"; + public static final String PNAME_tinyInt1isBit = "tinyInt1isBit"; + public static final String PNAME_traceProtocol = "traceProtocol"; + public static final String PNAME_treatUtilDateAsTimestamp = "treatUtilDateAsTimestamp"; + public static final String PNAME_transformedBitIsBoolean = "transformedBitIsBoolean"; + public static final String PNAME_useCompression = "useCompression"; + public static final String PNAME_useColumnNamesInFindColumn = "useColumnNamesInFindColumn"; + public static final String PNAME_useConfigs = "useConfigs"; + public static final String PNAME_useCursorFetch = "useCursorFetch"; + public static final String PNAME_useHostsInPrivileges = "useHostsInPrivileges"; + public static final String PNAME_useInformationSchema = "useInformationSchema"; + public static final String PNAME_useLocalSessionState = "useLocalSessionState"; + public static final String PNAME_useLocalTransactionState = "useLocalTransactionState"; + public static final String PNAME_sendFractionalSeconds = "sendFractionalSeconds"; + public static final String PNAME_useNanosForElapsedTime = "useNanosForElapsedTime"; + public static final String PNAME_useOldAliasMetadataBehavior = "useOldAliasMetadataBehavior"; + public static final String PNAME_useOldUTF8Behavior = "useOldUTF8Behavior"; + public static final String PNAME_useOnlyServerErrorMessages = "useOnlyServerErrorMessages"; + public static final String PNAME_useReadAheadInput = "useReadAheadInput"; + public static final String PNAME_useSSL = "useSSL"; + public static final String PNAME_useStreamLengthsInPrepStmts = "useStreamLengthsInPrepStmts"; + public static final String PNAME_ultraDevHack = "ultraDevHack"; + public static final String PNAME_useUsageAdvisor = "useUsageAdvisor"; + public static final String PNAME_yearIsDateType = "yearIsDateType"; + public static final String PNAME_zeroDateTimeBehavior = "zeroDateTimeBehavior"; + public static final String PNAME_useAffectedRows = "useAffectedRows"; + public static final String PNAME_maxAllowedPacket = "maxAllowedPacket"; + public static final String PNAME_authenticationPlugins = "authenticationPlugins"; + public static final String PNAME_disabledAuthenticationPlugins = "disabledAuthenticationPlugins"; + public static final String PNAME_defaultAuthenticationPlugin = "defaultAuthenticationPlugin"; + public static final String PNAME_disconnectOnExpiredPasswords = "disconnectOnExpiredPasswords"; + public static final String PNAME_getProceduresReturnsFunctions = "getProceduresReturnsFunctions"; + public static final String PNAME_detectCustomCollations = "detectCustomCollations"; + public static final String PNAME_dontCheckOnDuplicateKeyUpdateInSQL = "dontCheckOnDuplicateKeyUpdateInSQL"; + public static final String PNAME_readOnlyPropagatesToServer = "readOnlyPropagatesToServer"; + public static final String PNAME_replicationConnectionGroup = "replicationConnectionGroup"; + + public static final String PNAME_useAsyncProtocol = "xdevapi.useAsyncProtocol"; + public static final String PNAME_sslMode = "xdevapi.ssl-mode"; + public static final String PNAME_sslTrustStoreUrl = "xdevapi.ssl-truststore"; + public static final String PNAME_sslTrustStoreType = "xdevapi.ssl-truststore-type"; + public static final String PNAME_sslTrustStorePassword = "xdevapi.ssl-truststore-password"; + public static final String PNAME_asyncResponseTimeout = "xdevapi.asyncResponseTimeout"; + public static final String PNAME_auth = "xdevapi.auth"; + + public static final String PNAME_enableEscapeProcessing = "enableEscapeProcessing"; + + public static final String PNAME_serverAffinityOrder = "serverAffinityOrder"; + + // TODO following names are used in code but have no definitions + public static final String PNAME_user = "user"; + public static final String PNAME_password = "password"; + public static final String PNAME_resultSetScannerRegex = "resultSetScannerRegex"; + public static final String PNAME_clientInfoSetSPName = "clientInfoSetSPName"; + public static final String PNAME_clientInfoGetSPName = "clientInfoGetSPName"; + public static final String PNAME_clientInfoGetBulkSPName = "clientInfoGetBulkSPName"; + public static final String PNAME_clientInfoCatalog = "clientInfoCatalog"; + public static final String PNAME_autoConfigureForColdFusion = "autoConfigureForColdFusion"; + + public static final String PNAME_testsuite_faultInjection_serverCharsetIndex = "com.mysql.cj.testsuite.faultInjection.serverCharsetIndex"; + // ---------------- + + /* + * auth - Authentication property set + * user auth.user + * password auth.password + */ + + // + // Yes, this looks goofy, but we're trying to avoid intern()ing here + // + private static final String STANDARD_LOGGER_NAME = StandardLogger.class.getName(); + + static { + PropertyDefinition[] pdefs = new PropertyDefinition[] { + new BooleanPropertyDefinition(PNAME_paranoid, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.paranoid"), "3.0.1", CATEGORY_SECURITY, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_passwordCharacterEncoding, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.passwordCharacterEncoding"), "5.1.7", CATEGORY_CONNECTION, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_serverRSAPublicKeyFile, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.serverRSAPublicKeyFile"), "5.1.31", CATEGORY_SECURITY, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_allowPublicKeyRetrieval, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.allowPublicKeyRetrieval"), "5.1.31", CATEGORY_SECURITY, Integer.MIN_VALUE), + + // SSL Options + + new StringPropertyDefinition(PNAME_clientCertificateKeyStoreUrl, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.clientCertificateKeyStoreUrl"), "5.1.0", CATEGORY_SECURITY, 5), + + new StringPropertyDefinition(PNAME_trustCertificateKeyStoreUrl, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.trustCertificateKeyStoreUrl"), "5.1.0", CATEGORY_SECURITY, 8), + + new StringPropertyDefinition(PNAME_clientCertificateKeyStoreType, NO_ALIAS, "JKS", RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.clientCertificateKeyStoreType"), "5.1.0", CATEGORY_SECURITY, 6), + + new StringPropertyDefinition(PNAME_clientCertificateKeyStorePassword, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.clientCertificateKeyStorePassword"), "5.1.0", CATEGORY_SECURITY, 7), + + new StringPropertyDefinition(PNAME_trustCertificateKeyStoreType, NO_ALIAS, "JKS", RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.trustCertificateKeyStoreType"), "5.1.0", CATEGORY_SECURITY, 9), + + new StringPropertyDefinition(PNAME_trustCertificateKeyStorePassword, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.trustCertificateKeyStorePassword"), "5.1.0", CATEGORY_SECURITY, 10), + + new BooleanPropertyDefinition(PNAME_verifyServerCertificate, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.verifyServerCertificate"), "5.1.6", CATEGORY_SECURITY, 4), + + new StringPropertyDefinition(PNAME_enabledSSLCipherSuites, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.enabledSSLCipherSuites"), "5.1.35", CATEGORY_SECURITY, 11), + + new StringPropertyDefinition(PNAME_enabledTLSProtocols, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.enabledTLSProtocols"), "8.0.8", CATEGORY_SECURITY, 12), + + new BooleanPropertyDefinition(PNAME_useUnbufferedInput, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useUnbufferedInput"), "3.0.11", CATEGORY_NETWORK, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_profilerEventHandler, NO_ALIAS, "com.mysql.cj.log.LoggingProfilerEventHandler", RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.profilerEventHandler"), "5.1.6", CATEGORY_DEBUGING_PROFILING, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_allowLoadLocalInfile, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.loadDataLocal"), "3.0.3", CATEGORY_SECURITY, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_allowMultiQueries, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.allowMultiQueries"), "3.1.1", CATEGORY_SECURITY, 1), + + new BooleanPropertyDefinition(PNAME_allowNanAndInf, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.allowNANandINF"), "3.1.5", CATEGORY_PREPARED_STATEMENTS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_allowUrlInLocalInfile, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.allowUrlInLoadLocal"), "3.1.4", CATEGORY_SECURITY, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_alwaysSendSetIsolation, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.alwaysSendSetIsolation"), "3.1.7", CATEGORY_PERFORMANCE, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_autoClosePStmtStreams, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.autoClosePstmtStreams"), "3.1.12", CATEGORY_PREPARED_STATEMENTS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_allowMasterDownConnections, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.allowMasterDownConnections"), "5.1.27", CATEGORY_HA, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_allowSlaveDownConnections, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.allowSlaveDownConnections"), "6.0.2", CATEGORY_HA, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_readFromMasterWhenNoSlaves, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.readFromMasterWhenNoSlaves"), "6.0.2", CATEGORY_HA, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_autoDeserialize, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.autoDeserialize"), "3.1.5", CATEGORY_BLOBS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_autoGenerateTestcaseScript, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.autoGenerateTestcaseScript"), "3.1.9", CATEGORY_DEBUGING_PROFILING, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_autoReconnect, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.autoReconnect"), "1.1", CATEGORY_HA, 0), + + new BooleanPropertyDefinition(PNAME_autoReconnectForPools, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.autoReconnectForPools"), "3.1.3", CATEGORY_HA, 1), + + new MemorySizePropertyDefinition(PNAME_blobSendChunkSize, NO_ALIAS, 1024 * 1024, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.blobSendChunkSize"), "3.1.9", CATEGORY_BLOBS, Integer.MIN_VALUE, 0, 0), + + new BooleanPropertyDefinition(PNAME_autoSlowLog, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.autoSlowLog"), "5.1.4", CATEGORY_DEBUGING_PROFILING, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_blobsAreStrings, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.blobsAreStrings"), "5.0.8", CATEGORY_BLOBS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_functionsNeverReturnBlobs, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.functionsNeverReturnBlobs"), "5.0.8", CATEGORY_BLOBS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_cacheCallableStmts, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.cacheCallableStatements"), "3.1.2", CATEGORY_PERFORMANCE, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_cachePrepStmts, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.cachePrepStmts"), "3.0.10", CATEGORY_PERFORMANCE, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_cacheResultSetMetadata, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.cacheRSMetadata"), "3.1.1", CATEGORY_PERFORMANCE, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_serverConfigCacheFactory, NO_ALIAS, PerVmServerConfigCacheFactory.class.getName(), RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.serverConfigCacheFactory"), "5.1.1", CATEGORY_PERFORMANCE, 12), + + new BooleanPropertyDefinition(PNAME_cacheServerConfiguration, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.cacheServerConfiguration"), "3.1.5", CATEGORY_PERFORMANCE, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_callableStmtCacheSize, NO_ALIAS, 100, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.callableStmtCacheSize"), "3.1.2", CATEGORY_PERFORMANCE, 5, 0, Integer.MAX_VALUE), + + new StringPropertyDefinition(PNAME_characterEncoding, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.characterEncoding"), "1.1g", CATEGORY_SESSION, 5), + + new StringPropertyDefinition(PNAME_characterSetResults, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.characterSetResults"), "3.0.13", CATEGORY_SESSION, 6), + + new StringPropertyDefinition(PNAME_connectionAttributes, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.connectionAttributes"), "5.1.25", CATEGORY_CONNECTION, 7), + + new StringPropertyDefinition(PNAME_clientInfoProvider, NO_ALIAS, "com.mysql.cj.jdbc.CommentClientInfoProvider", RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.clientInfoProvider"), "5.1.0", CATEGORY_DEBUGING_PROFILING, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_clobberStreamingResults, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.clobberStreamingResults"), "3.0.9", CATEGORY_RESULT_SETS, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_clobCharacterEncoding, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.clobCharacterEncoding"), "5.0.0", CATEGORY_BLOBS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_compensateOnDuplicateKeyUpdateCounts, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.compensateOnDuplicateKeyUpdateCounts"), "5.1.7", CATEGORY_PREPARED_STATEMENTS, + Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_connectionCollation, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.connectionCollation"), "3.0.13", CATEGORY_SESSION, 7), + + new StringPropertyDefinition(PNAME_connectionLifecycleInterceptors, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.connectionLifecycleInterceptors"), "5.1.4", CATEGORY_CONNECTION, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_connectTimeout, NO_ALIAS, 0, RUNTIME_MODIFIABLE, Messages.getString("ConnectionProperties.connectTimeout"), + "3.0.1", CATEGORY_NETWORK, 9, 0, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_continueBatchOnError, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.continueBatchOnError"), "3.0.3", CATEGORY_STATEMENTS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_createDatabaseIfNotExist, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.createDatabaseIfNotExist"), "3.1.9", CATEGORY_CONNECTION, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_defaultFetchSize, NO_ALIAS, 0, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.defaultFetchSize"), "3.1.9", CATEGORY_PERFORMANCE, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useServerPrepStmts, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useServerPrepStmts"), "3.1.0", CATEGORY_PREPARED_STATEMENTS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_dontTrackOpenResources, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.dontTrackOpenResources"), "3.1.7", CATEGORY_STATEMENTS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_dumpQueriesOnException, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.dumpQueriesOnException"), "3.1.3", CATEGORY_EXCEPTIONS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_elideSetAutoCommits, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.eliseSetAutoCommit"), "3.1.3", CATEGORY_PERFORMANCE, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_emptyStringsConvertToZero, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.emptyStringsConvertToZero"), "3.1.8", CATEGORY_RESULT_SETS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_emulateLocators, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.emulateLocators"), "3.1.0", CATEGORY_BLOBS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_emulateUnsupportedPstmts, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.emulateUnsupportedPstmts"), "3.1.7", CATEGORY_PREPARED_STATEMENTS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_enablePacketDebug, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.enablePacketDebug"), "3.1.3", CATEGORY_DEBUGING_PROFILING, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_enableQueryTimeouts, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.enableQueryTimeouts"), "5.0.6", CATEGORY_PERFORMANCE, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_explainSlowQueries, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.explainSlowQueries"), "3.1.2", CATEGORY_DEBUGING_PROFILING, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_exceptionInterceptors, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.exceptionInterceptors"), "5.1.8", CATEGORY_EXCEPTIONS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_failOverReadOnly, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.failoverReadOnly"), "3.0.12", CATEGORY_HA, 2), + + new BooleanPropertyDefinition(PNAME_gatherPerfMetrics, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.gatherPerfMetrics"), "3.1.2", CATEGORY_DEBUGING_PROFILING, 1), + + new BooleanPropertyDefinition(PNAME_generateSimpleParameterMetadata, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.generateSimpleParameterMetadata"), "5.0.5", CATEGORY_PREPARED_STATEMENTS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_holdResultsOpenOverStatementClose, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.holdRSOpenOverStmtClose"), "3.1.7", CATEGORY_RESULT_SETS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_includeInnodbStatusInDeadlockExceptions, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.includeInnodbStatusInDeadlockExceptions"), "5.0.7", CATEGORY_EXCEPTIONS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_includeThreadDumpInDeadlockExceptions, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.includeThreadDumpInDeadlockExceptions"), "5.1.15", CATEGORY_EXCEPTIONS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_includeThreadNamesAsStatementComment, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.includeThreadNamesAsStatementComment"), "5.1.15", CATEGORY_EXCEPTIONS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_ignoreNonTxTables, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.ignoreNonTxTables"), "3.0.9", CATEGORY_EXCEPTIONS, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_initialTimeout, NO_ALIAS, 2, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.initialTimeout"), "1.1", CATEGORY_HA, 5, 1, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_interactiveClient, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.interactiveClient"), "3.1.0", CATEGORY_CONNECTION, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_jdbcCompliantTruncation, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.jdbcCompliantTruncation"), "3.1.2", CATEGORY_RESULT_SETS, Integer.MIN_VALUE), + + new MemorySizePropertyDefinition(PNAME_largeRowSizeThreshold, NO_ALIAS, 2048, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.largeRowSizeThreshold"), "5.1.1", CATEGORY_PERFORMANCE, Integer.MIN_VALUE, 0, + Integer.MAX_VALUE), + + new StringPropertyDefinition(PNAME_loadBalanceStrategy, "haLoadBalanceStrategy", "random", RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.loadBalanceStrategy"), "5.0.6", CATEGORY_HA, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_loadBalanceBlacklistTimeout, NO_ALIAS, 0, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.loadBalanceBlacklistTimeout"), "5.1.0", CATEGORY_HA, Integer.MIN_VALUE, 0, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_loadBalancePingTimeout, NO_ALIAS, 0, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.loadBalancePingTimeout"), "5.1.13", CATEGORY_HA, Integer.MIN_VALUE, 0, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_loadBalanceValidateConnectionOnSwapServer, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.loadBalanceValidateConnectionOnSwapServer"), "5.1.13", CATEGORY_HA, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_loadBalanceConnectionGroup, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.loadBalanceConnectionGroup"), "5.1.13", CATEGORY_HA, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_loadBalanceExceptionChecker, NO_ALIAS, "com.mysql.cj.jdbc.ha.StandardLoadBalanceExceptionChecker", + RUNTIME_MODIFIABLE, Messages.getString("ConnectionProperties.loadBalanceExceptionChecker"), "5.1.13", CATEGORY_HA, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_loadBalanceSQLStateFailover, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.loadBalanceSQLStateFailover"), "5.1.13", CATEGORY_HA, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_loadBalanceSQLExceptionSubclassFailover, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.loadBalanceSQLExceptionSubclassFailover"), "5.1.13", CATEGORY_HA, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_loadBalanceAutoCommitStatementRegex, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.loadBalanceAutoCommitStatementRegex"), "5.1.15", CATEGORY_HA, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_loadBalanceAutoCommitStatementThreshold, NO_ALIAS, 0, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.loadBalanceAutoCommitStatementThreshold"), "5.1.15", CATEGORY_HA, Integer.MIN_VALUE, 0, + Integer.MAX_VALUE), + + new StringPropertyDefinition(PNAME_localSocketAddress, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.localSocketAddress"), "5.0.5", CATEGORY_NETWORK, Integer.MIN_VALUE), + + new MemorySizePropertyDefinition(PNAME_locatorFetchBufferSize, NO_ALIAS, 1024 * 1024, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.locatorFetchBufferSize"), "3.2.1", CATEGORY_BLOBS, Integer.MIN_VALUE, 0, Integer.MAX_VALUE), + + new StringPropertyDefinition(PNAME_logger, NO_ALIAS, STANDARD_LOGGER_NAME, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.logger", new Object[] { Log.class.getName(), STANDARD_LOGGER_NAME }), "3.1.1", + CATEGORY_DEBUGING_PROFILING, 0), + + new BooleanPropertyDefinition(PNAME_logSlowQueries, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.logSlowQueries"), "3.1.2", CATEGORY_DEBUGING_PROFILING, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_logXaCommands, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.logXaCommands"), "5.0.5", CATEGORY_DEBUGING_PROFILING, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_maintainTimeStats, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.maintainTimeStats"), "3.1.9", CATEGORY_PERFORMANCE, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_maxQuerySizeToLog, NO_ALIAS, 2048, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.maxQuerySizeToLog"), "3.1.3", CATEGORY_DEBUGING_PROFILING, 4, 0, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_maxReconnects, NO_ALIAS, 3, RUNTIME_MODIFIABLE, Messages.getString("ConnectionProperties.maxReconnects"), + "1.1", CATEGORY_HA, 4, 1, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_retriesAllDown, NO_ALIAS, 120, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.retriesAllDown"), "5.1.6", CATEGORY_HA, 4, 0, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_maxRows, NO_ALIAS, -1, RUNTIME_MODIFIABLE, Messages.getString("ConnectionProperties.maxRows"), + Messages.getString("ConnectionProperties.allVersions"), CATEGORY_RESULT_SETS, Integer.MIN_VALUE, -1, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_metadataCacheSize, NO_ALIAS, 50, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.metadataCacheSize"), "3.1.1", CATEGORY_PERFORMANCE, 5, 1, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_netTimeoutForStreamingResults, NO_ALIAS, 600, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.netTimeoutForStreamingResults"), "5.1.0", CATEGORY_RESULT_SETS, Integer.MIN_VALUE, 0, + Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_noAccessToProcedureBodies, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.noAccessToProcedureBodies"), "5.0.3", CATEGORY_METADATA, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_noDatetimeStringSync, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.noDatetimeStringSync"), "3.1.7", CATEGORY_DATETIMES, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_nullCatalogMeansCurrent, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.nullCatalogMeansCurrent"), "3.1.8", CATEGORY_METADATA, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_packetDebugBufferSize, NO_ALIAS, 20, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.packetDebugBufferSize"), "3.1.3", CATEGORY_DEBUGING_PROFILING, 7, 1, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_padCharsWithSpace, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.padCharsWithSpace"), "5.0.6", CATEGORY_RESULT_SETS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_pedantic, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.pedantic"), "3.0.0", CATEGORY_JDBC, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_pinGlobalTxToPhysicalConnection, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.pinGlobalTxToPhysicalConnection"), "5.0.1", CATEGORY_HA, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_populateInsertRowWithDefaultValues, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.populateInsertRowWithDefaultValues"), "5.0.5", CATEGORY_RESULT_SETS, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_prepStmtCacheSize, NO_ALIAS, 25, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.prepStmtCacheSize"), "3.0.10", CATEGORY_PERFORMANCE, 10, 0, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_prepStmtCacheSqlLimit, NO_ALIAS, 256, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.prepStmtCacheSqlLimit"), "3.0.10", CATEGORY_PERFORMANCE, 11, 1, Integer.MAX_VALUE), + + new StringPropertyDefinition(PNAME_parseInfoCacheFactory, NO_ALIAS, PerConnectionLRUFactory.class.getName(), RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.parseInfoCacheFactory"), "5.1.1", CATEGORY_PERFORMANCE, 12), + + new BooleanPropertyDefinition(PNAME_processEscapeCodesForPrepStmts, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.processEscapeCodesForPrepStmts"), "3.1.12", CATEGORY_PREPARED_STATEMENTS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_profileSQL, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.profileSQL"), "3.1.0", CATEGORY_DEBUGING_PROFILING, 1), + + new StringPropertyDefinition(PNAME_propertiesTransform, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.connectionPropertiesTransform"), "3.1.4", CATEGORY_CONNECTION, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_queriesBeforeRetryMaster, NO_ALIAS, 50, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.queriesBeforeRetryMaster"), "3.0.2", CATEGORY_HA, 7, 0, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_queryTimeoutKillsConnection, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.queryTimeoutKillsConnection"), "5.1.9", CATEGORY_STATEMENTS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_reconnectAtTxEnd, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.reconnectAtTxEnd"), "3.0.10", CATEGORY_HA, 4), + + new StringPropertyDefinition(PNAME_replicationConnectionGroup, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.replicationConnectionGroup"), "8.0.7", CATEGORY_HA, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_reportMetricsIntervalMillis, NO_ALIAS, 30000, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.reportMetricsIntervalMillis"), "3.1.2", CATEGORY_DEBUGING_PROFILING, 3, 0, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_requireSSL, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.requireSSL"), "3.1.0", CATEGORY_SECURITY, 3), + + new StringPropertyDefinition(PNAME_resourceId, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.resourceId"), "5.0.1", CATEGORY_HA, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_resultSetSizeThreshold, NO_ALIAS, 100, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.resultSetSizeThreshold"), "5.0.5", CATEGORY_DEBUGING_PROFILING, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_rewriteBatchedStatements, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.rewriteBatchedStatements"), "3.1.13", CATEGORY_PERFORMANCE, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_rollbackOnPooledClose, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.rollbackOnPooledClose"), "3.0.15", CATEGORY_CONNECTION, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_secondsBeforeRetryMaster, NO_ALIAS, 30, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.secondsBeforeRetryMaster"), "3.0.2", CATEGORY_HA, 8, 0, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_selfDestructOnPingSecondsLifetime, NO_ALIAS, 0, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.selfDestructOnPingSecondsLifetime"), "5.1.6", CATEGORY_HA, Integer.MAX_VALUE, 0, + Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_selfDestructOnPingMaxOperations, NO_ALIAS, 0, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.selfDestructOnPingMaxOperations"), "5.1.6", CATEGORY_HA, Integer.MAX_VALUE, 0, + Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_ha_enableJMX, "haEnableJMX", DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.ha.enableJMX"), "5.1.27", CATEGORY_HA, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_loadBalanceHostRemovalGracePeriod, NO_ALIAS, 15000, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.loadBalanceHostRemovalGracePeriod"), "6.0.3", CATEGORY_HA, Integer.MAX_VALUE, 0, + Integer.MAX_VALUE), + + new StringPropertyDefinition(PNAME_serverTimezone, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.serverTimezone"), "3.0.2", CATEGORY_DATETIMES, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_sessionVariables, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.sessionVariables"), "3.1.8", CATEGORY_SESSION, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_slowQueryThresholdMillis, NO_ALIAS, 2000, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.slowQueryThresholdMillis"), "3.1.2", CATEGORY_DEBUGING_PROFILING, 9, 0, Integer.MAX_VALUE), + + new LongPropertyDefinition(PNAME_slowQueryThresholdNanos, NO_ALIAS, 0, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.slowQueryThresholdNanos"), "5.0.7", CATEGORY_DEBUGING_PROFILING, 10), + + new StringPropertyDefinition(PNAME_socketFactory, NO_ALIAS, "com.mysql.cj.protocol.StandardSocketFactory", RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.socketFactory"), "3.0.3", CATEGORY_NETWORK, 4), + + new StringPropertyDefinition(PNAME_socksProxyHost, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.socksProxyHost"), "5.1.34", CATEGORY_NETWORK, 1), + + new IntegerPropertyDefinition(PNAME_socksProxyPort, NO_ALIAS, 1080, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.socksProxyPort"), "5.1.34", CATEGORY_NETWORK, 2, 0, 65535), + + new IntegerPropertyDefinition(PNAME_socketTimeout, NO_ALIAS, 0, RUNTIME_MODIFIABLE, Messages.getString("ConnectionProperties.socketTimeout"), + "3.0.1", CATEGORY_NETWORK, 10, 0, Integer.MAX_VALUE), + + new StringPropertyDefinition(PNAME_queryInterceptors, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.queryInterceptors"), "8.0.7", CATEGORY_STATEMENTS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_strictUpdates, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.strictUpdates"), "3.0.4", CATEGORY_RESULT_SETS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_overrideSupportsIntegrityEnhancementFacility, NO_ALIAS, false, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.overrideSupportsIEF"), "3.1.12", CATEGORY_INTEGRATION, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_tcpNoDelay, NO_ALIAS, Boolean.valueOf(SocketFactory.TCP_NO_DELAY_DEFAULT_VALUE).booleanValue(), + RUNTIME_MODIFIABLE, Messages.getString("ConnectionProperties.tcpNoDelay"), "5.0.7", CATEGORY_NETWORK, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_tcpKeepAlive, NO_ALIAS, Boolean.valueOf(SocketFactory.TCP_KEEP_ALIVE_DEFAULT_VALUE).booleanValue(), + RUNTIME_MODIFIABLE, Messages.getString("ConnectionProperties.tcpKeepAlive"), "5.0.7", CATEGORY_NETWORK, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_tcpRcvBuf, NO_ALIAS, Integer.parseInt(SocketFactory.TCP_RCV_BUF_DEFAULT_VALUE), RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.tcpSoRcvBuf"), "5.0.7", CATEGORY_NETWORK, Integer.MIN_VALUE, 0, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_tcpSndBuf, NO_ALIAS, Integer.parseInt(SocketFactory.TCP_SND_BUF_DEFAULT_VALUE), RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.tcpSoSndBuf"), "5.0.7", CATEGORY_NETWORK, Integer.MIN_VALUE, 0, Integer.MAX_VALUE), + + new IntegerPropertyDefinition(PNAME_tcpTrafficClass, NO_ALIAS, Integer.parseInt(SocketFactory.TCP_TRAFFIC_CLASS_DEFAULT_VALUE), + RUNTIME_MODIFIABLE, Messages.getString("ConnectionProperties.tcpTrafficClass"), "5.0.7", CATEGORY_NETWORK, Integer.MIN_VALUE, 0, 255), + + new BooleanPropertyDefinition(PNAME_tinyInt1isBit, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.tinyInt1isBit"), "3.0.16", CATEGORY_RESULT_SETS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_traceProtocol, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.traceProtocol"), "3.1.2", CATEGORY_DEBUGING_PROFILING, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_treatUtilDateAsTimestamp, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.treatUtilDateAsTimestamp"), "5.0.5", CATEGORY_DATETIMES, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_transformedBitIsBoolean, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.transformedBitIsBoolean"), "3.1.9", CATEGORY_RESULT_SETS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useCompression, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useCompression"), "3.0.17", CATEGORY_NETWORK, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useColumnNamesInFindColumn, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useColumnNamesInFindColumn"), "5.1.7", CATEGORY_JDBC, Integer.MAX_VALUE), + + new StringPropertyDefinition(PNAME_useConfigs, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useConfigs"), "3.1.5", CATEGORY_CONNECTION, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_useCursorFetch, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useCursorFetch"), "5.0.0", CATEGORY_PERFORMANCE, Integer.MAX_VALUE), + + new BooleanPropertyDefinition(PNAME_useHostsInPrivileges, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useHostsInPrivileges"), "3.0.2", CATEGORY_METADATA, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useInformationSchema, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useInformationSchema"), "5.0.0", CATEGORY_METADATA, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useLocalSessionState, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useLocalSessionState"), "3.1.7", CATEGORY_PERFORMANCE, 5), + + new BooleanPropertyDefinition(PNAME_useLocalTransactionState, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useLocalTransactionState"), "5.1.7", CATEGORY_PERFORMANCE, 6), + + new BooleanPropertyDefinition(PNAME_sendFractionalSeconds, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.sendFractionalSeconds"), "5.1.37", CATEGORY_DATETIMES, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useNanosForElapsedTime, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useNanosForElapsedTime"), "5.0.7", CATEGORY_DEBUGING_PROFILING, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useOldAliasMetadataBehavior, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useOldAliasMetadataBehavior"), "5.0.4", CATEGORY_JDBC, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useOldUTF8Behavior, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useOldUtf8Behavior"), "3.1.6", CATEGORY_SESSION, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useOnlyServerErrorMessages, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useOnlyServerErrorMessages"), "3.0.15", CATEGORY_DEBUGING_PROFILING, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useReadAheadInput, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useReadAheadInput"), "3.1.5", CATEGORY_PERFORMANCE, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useSSL, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useSSL"), "3.0.2", CATEGORY_SECURITY, 2), + + new BooleanPropertyDefinition(PNAME_useStreamLengthsInPrepStmts, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useStreamLengthsInPrepStmts"), "3.0.2", CATEGORY_PREPARED_STATEMENTS, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_ultraDevHack, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.ultraDevHack"), "2.0.3", CATEGORY_INTEGRATION, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useUsageAdvisor, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useUsageAdvisor"), "3.1.1", CATEGORY_DEBUGING_PROFILING, 10), + + new BooleanPropertyDefinition(PNAME_yearIsDateType, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.yearIsDateType"), "3.1.9", CATEGORY_DATETIMES, Integer.MIN_VALUE), + + new EnumPropertyDefinition<>(PNAME_zeroDateTimeBehavior, NO_ALIAS, ZeroDatetimeBehavior.EXCEPTION, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.zeroDateTimeBehavior", + new Object[] { ZeroDatetimeBehavior.EXCEPTION, ZeroDatetimeBehavior.ROUND, ZeroDatetimeBehavior.CONVERT_TO_NULL }), + "3.1.4", CATEGORY_DATETIMES, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_useAffectedRows, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.useAffectedRows"), "5.1.7", CATEGORY_CONNECTION, Integer.MIN_VALUE), + + new IntegerPropertyDefinition(PNAME_maxAllowedPacket, NO_ALIAS, 65535, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.maxAllowedPacket"), "5.1.8", CATEGORY_NETWORK, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_authenticationPlugins, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.authenticationPlugins"), "5.1.19", CATEGORY_CONNECTION, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_disabledAuthenticationPlugins, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.disabledAuthenticationPlugins"), "5.1.19", CATEGORY_CONNECTION, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_defaultAuthenticationPlugin, NO_ALIAS, "com.mysql.cj.protocol.a.authentication.MysqlNativePasswordPlugin", + RUNTIME_MODIFIABLE, Messages.getString("ConnectionProperties.defaultAuthenticationPlugin"), "5.1.19", CATEGORY_CONNECTION, + Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_disconnectOnExpiredPasswords, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.disconnectOnExpiredPasswords"), "5.1.23", CATEGORY_CONNECTION, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_getProceduresReturnsFunctions, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.getProceduresReturnsFunctions"), "5.1.26", CATEGORY_METADATA, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_detectCustomCollations, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.detectCustomCollations"), "5.1.29", CATEGORY_CONNECTION, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_dontCheckOnDuplicateKeyUpdateInSQL, NO_ALIAS, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.dontCheckOnDuplicateKeyUpdateInSQL"), "5.1.32", CATEGORY_PERFORMANCE, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_readOnlyPropagatesToServer, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.readOnlyPropagatesToServer"), "5.1.35", CATEGORY_PERFORMANCE, Integer.MIN_VALUE), + + // TODO improve X DevAPI properties descriptions + + new BooleanPropertyDefinition(PNAME_useAsyncProtocol, "xdevapiUseAsyncProtocol", DEFAULT_VALUE_TRUE, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.useAsyncProtocol"), "6.0.0", CATEGORY_XDEVAPI, Integer.MIN_VALUE), + new EnumPropertyDefinition<>(PNAME_sslMode, "xdevapiSSLMode", SslMode.REQUIRED, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.sslMode"), "8.0.7", CATEGORY_XDEVAPI, Integer.MIN_VALUE), + new StringPropertyDefinition(PNAME_sslTrustStoreUrl, "xdevapiSSLTruststore", DEFAULT_VALUE_NULL_STRING, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.sslTrustStoreUrl"), "6.0.6", CATEGORY_XDEVAPI, Integer.MIN_VALUE), + new StringPropertyDefinition(PNAME_sslTrustStoreType, "xdevapiSSLTruststoreType", "JKS", RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.sslTrustStoreType"), "6.0.6", CATEGORY_XDEVAPI, Integer.MIN_VALUE), + new StringPropertyDefinition(PNAME_sslTrustStorePassword, "xdevapiSSLTruststorePassword", DEFAULT_VALUE_NULL_STRING, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.sslTrustStorePassword"), "6.0.6", CATEGORY_XDEVAPI, Integer.MIN_VALUE), + new IntegerPropertyDefinition(PNAME_asyncResponseTimeout, "xdevapiAsyncResponseTimeout", 300, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.asyncResponseTimeout"), "8.0.7", CATEGORY_XDEVAPI, Integer.MIN_VALUE), + new EnumPropertyDefinition<>(PNAME_auth, "xdevapiAuth", AuthMech.PLAIN, RUNTIME_NOT_MODIFIABLE, Messages.getString("ConnectionProperties.auth"), + "8.0.8", CATEGORY_XDEVAPI, Integer.MIN_VALUE), + + new BooleanPropertyDefinition(PNAME_enableEscapeProcessing, NO_ALIAS, DEFAULT_VALUE_TRUE, RUNTIME_MODIFIABLE, + Messages.getString("ConnectionProperties.enableEscapeProcessing"), "6.0.1", CATEGORY_PERFORMANCE, Integer.MIN_VALUE), + + new StringPropertyDefinition(PNAME_serverAffinityOrder, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, RUNTIME_NOT_MODIFIABLE, + Messages.getString("ConnectionProperties.serverAffinityOrder"), "8.0.8", CATEGORY_HA, Integer.MIN_VALUE) }; + + HashMap> propertyNameToPropertyDefinitionMap = new HashMap<>(); + for (PropertyDefinition pdef : pdefs) { + String pname = pdef.getName(); + propertyNameToPropertyDefinitionMap.put(pname, pdef); + } + PROPERTY_NAME_TO_PROPERTY_DEFINITION = Collections.unmodifiableMap(propertyNameToPropertyDefinitionMap); + + } + + public static PropertyDefinition getPropertyDefinition(String propertyName) { + return PROPERTY_NAME_TO_PROPERTY_DEFINITION.get(propertyName); + } + + public static RuntimeProperty createRuntimeProperty(String propertyName) { + PropertyDefinition pdef = getPropertyDefinition(propertyName); + if (pdef != null) { + return pdef.createRuntimeProperty(); + } + // TODO improve message + throw ExceptionFactory.createException(WrongArgumentException.class, "Connection property definition is not found for '" + propertyName + "'"); + } + + static class XmlMap { + protected Map>> ordered = new TreeMap<>(); + protected Map> alpha = new TreeMap<>(); + } + + /** + * Returns a description of the connection properties as an XML document. + * + * @return the connection properties as an XML document. + */ + public static String exposeAsXml() { + StringBuilder xmlBuf = new StringBuilder(); + xmlBuf.append(""); + + int numCategories = PROPERTY_CATEGORIES.length; + + Map propertyListByCategory = new HashMap<>(); + + for (int i = 0; i < numCategories; i++) { + propertyListByCategory.put(PROPERTY_CATEGORIES[i], new XmlMap()); + } + + // + // The following properties are not exposed as 'normal' properties, but they are settable nonetheless, so we need to have them documented, make sure + // that they sort 'first' as #1 and #2 in the category + // + StringPropertyDefinition userDef = new StringPropertyDefinition(PropertyDefinitions.PNAME_user, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, + RUNTIME_NOT_MODIFIABLE, Messages.getString("ConnectionProperties.Username"), Messages.getString("ConnectionProperties.allVersions"), + CATEGORY_AUTH, Integer.MIN_VALUE + 1); + + StringPropertyDefinition passwordDef = new StringPropertyDefinition(PropertyDefinitions.PNAME_password, NO_ALIAS, DEFAULT_VALUE_NULL_STRING, + RUNTIME_NOT_MODIFIABLE, Messages.getString("ConnectionProperties.Password"), Messages.getString("ConnectionProperties.allVersions"), + CATEGORY_AUTH, Integer.MIN_VALUE + 2); + + XmlMap connectionSortMaps = propertyListByCategory.get(CATEGORY_AUTH); + TreeMap> userMap = new TreeMap<>(); + userMap.put(userDef.getName(), userDef); + + connectionSortMaps.ordered.put(Integer.valueOf(userDef.getOrder()), userMap); + + TreeMap> passwordMap = new TreeMap<>(); + passwordMap.put(passwordDef.getName(), passwordDef); + + connectionSortMaps.ordered.put(new Integer(passwordDef.getOrder()), passwordMap); + + for (PropertyDefinition pdef : PROPERTY_NAME_TO_PROPERTY_DEFINITION.values()) { + XmlMap sortMaps = propertyListByCategory.get(pdef.getCategory()); + int orderInCategory = pdef.getOrder(); + + if (orderInCategory == Integer.MIN_VALUE) { + sortMaps.alpha.put(pdef.getName(), pdef); + } else { + Integer order = Integer.valueOf(orderInCategory); + Map> orderMap = sortMaps.ordered.get(order); + + if (orderMap == null) { + orderMap = new TreeMap<>(); + sortMaps.ordered.put(order, orderMap); + } + + orderMap.put(pdef.getName(), pdef); + } + } + + for (int j = 0; j < numCategories; j++) { + XmlMap sortMaps = propertyListByCategory.get(PROPERTY_CATEGORIES[j]); + + xmlBuf.append("\n "); + + for (Map> orderedEl : sortMaps.ordered.values()) { + for (PropertyDefinition pdef : orderedEl.values()) { + xmlBuf.append("\n \n"); + xmlBuf.append(" "); + String escapedDescription = pdef.getDescription(); + escapedDescription = escapedDescription.replace("&", "&").replace("<", "<").replace(">", ">"); + + xmlBuf.append(escapedDescription); + xmlBuf.append("\n "); + } + } + + for (PropertyDefinition pdef : sortMaps.alpha.values()) { + xmlBuf.append("\n \n"); + xmlBuf.append(" "); + xmlBuf.append(pdef.getDescription()); + xmlBuf.append("\n "); + } + + xmlBuf.append("\n "); + } + + xmlBuf.append("\n"); + + return xmlBuf.toString(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/PropertySet.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/PropertySet.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/PropertySet.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.util.Properties; + +public interface PropertySet { + + void addProperty(RuntimeProperty prop); + + void removeProperty(String name); + + ReadableProperty getReadableProperty(String name); + + ReadableProperty getBooleanReadableProperty(String name); + + ReadableProperty getIntegerReadableProperty(String name); + + ReadableProperty getLongReadableProperty(String name); + + ReadableProperty getMemorySizeReadableProperty(String name); + + ReadableProperty getStringReadableProperty(String name); + + > ReadableProperty getEnumReadableProperty(String name); + + ModifiableProperty getModifiableProperty(String name); + + ModifiableProperty getBooleanModifiableProperty(String name); + + ModifiableProperty getIntegerModifiableProperty(String name); + + ModifiableProperty getLongModifiableProperty(String name); + + ModifiableProperty getMemorySizeModifiableProperty(String name); + + ModifiableProperty getStringModifiableProperty(String name); + + > ReadableProperty getEnumModifiableProperty(String name); + + /** + * Initializes the property set with driver properties that come from URL or passed to + * the driver manager. + * + * @param props + * properties + */ + void initializeProperties(Properties props); + + void postInitialization(); + + Properties exposeAsProperties(); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableBooleanProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableBooleanProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableBooleanProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +public class ReadableBooleanProperty extends AbstractReadableProperty { + + private static final long serialVersionUID = 1102859411443650569L; + + protected ReadableBooleanProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableEnumProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableEnumProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableEnumProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +public class ReadableEnumProperty> extends AbstractReadableProperty { + + private static final long serialVersionUID = -60853080911910124L; + + protected ReadableEnumProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableIntegerProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableIntegerProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableIntegerProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +public class ReadableIntegerProperty extends AbstractReadableProperty { + + private static final long serialVersionUID = 9208223182595760858L; + + public ReadableIntegerProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableLongProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableLongProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableLongProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +public class ReadableLongProperty extends AbstractReadableProperty { + + private static final long serialVersionUID = 1814429804634837665L; + + protected ReadableLongProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableMemorySizeProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableMemorySizeProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableMemorySizeProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +public class ReadableMemorySizeProperty extends ReadableIntegerProperty { + + private static final long serialVersionUID = 4200558564320133284L; + + protected String valueAsString; + + protected ReadableMemorySizeProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + this.valueAsString = propertyDefinition.getDefaultValue().toString(); + } + + @Override + public String getStringValue() { + return this.valueAsString; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +public interface ReadableProperty extends RuntimeProperty { + + /** + * Get internal value representation as Object. + * + * @return value + */ + T getValue(); + + /** + * Get initial value (default or defined in connection string/Properties) + * + * @return value + */ + T getInitialValue(); + + /** + * Get internal value representation as String. + * + * @return value + */ + String getStringValue(); + + // TODO: MYSQLCONNJ-92. Listeners, other driver components may want to add to be notified about property changes + //void addListener(Listener l); + //void removeListener(Listener l); + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableStringProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableStringProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/ReadableStringProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +public class ReadableStringProperty extends AbstractReadableProperty { + + private static final long serialVersionUID = -4141084145739428803L; + + protected ReadableStringProperty(PropertyDefinition propertyDefinition) { + super(propertyDefinition); + } + + @Override + public String getStringValue() { + return this.value; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/RuntimeProperty.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/RuntimeProperty.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/RuntimeProperty.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import java.util.Properties; + +import javax.naming.Reference; + +import com.mysql.cj.exceptions.ExceptionInterceptor; + +public interface RuntimeProperty { + + PropertyDefinition getPropertyDefinition(); + + void initializeFrom(Properties extractFrom, ExceptionInterceptor exceptionInterceptor); + + void initializeFrom(Reference ref, ExceptionInterceptor exceptionInterceptor); + + /** + * Reset to initial value (default or defined in connection string/Properties) + */ + void resetValue(); + + boolean isExplicitlySet(); + + /** + * Add listener for this property changes. + * + * @param l + * {@link RuntimePropertyListener} + */ + void addListener(RuntimePropertyListener l); + + void removeListener(RuntimePropertyListener l); + + @FunctionalInterface + public static interface RuntimePropertyListener { + void handlePropertyChange(RuntimeProperty prop); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/StringPropertyDefinition.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/StringPropertyDefinition.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/StringPropertyDefinition.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf; + +import com.mysql.cj.exceptions.ExceptionInterceptor; + +public class StringPropertyDefinition extends AbstractPropertyDefinition { + + private static final long serialVersionUID = 8228934389127796555L; + + public StringPropertyDefinition(String name, String alias, String defaultValue, boolean isRuntimeModifiable, String description, String sinceVersion, + String category, int orderInCategory) { + super(name, alias, defaultValue, isRuntimeModifiable, description, sinceVersion, category, orderInCategory); + } + + @Override + public String parseObject(String value, ExceptionInterceptor exceptionInterceptor) { + return value; + } + + /** + * Creates instance of ReadableStringProperty or ModifiableStringProperty depending on isRuntimeModifiable() result. + * + * @return + */ + @Override + public RuntimeProperty createRuntimeProperty() { + return isRuntimeModifiable() ? new ModifiableStringProperty(this) : new ReadableStringProperty(this); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/FailoverConnectionUrl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/FailoverConnectionUrl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/FailoverConnectionUrl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf.url; + +import java.util.Properties; + +import com.mysql.cj.conf.ConnectionUrl; +import com.mysql.cj.conf.ConnectionUrlParser; + +public class FailoverConnectionUrl extends ConnectionUrl { + /** + * Constructs an instance of {@link FailoverConnectionUrl}, performing all the required initializations. + * + * @param connStrParser + * a {@link ConnectionUrlParser} instance containing the parsed version of the original connection string + * @param info + * the connection arguments map + */ + public FailoverConnectionUrl(ConnectionUrlParser connStrParser, Properties info) { + super(connStrParser, info); + this.type = Type.FAILOVER_CONNECTION; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/LoadbalanceConnectionUrl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/LoadbalanceConnectionUrl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/LoadbalanceConnectionUrl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf.url; + +import static com.mysql.cj.conf.PropertyDefinitions.PNAME_loadBalanceAutoCommitStatementThreshold; +import static com.mysql.cj.conf.PropertyDefinitions.PNAME_queryInterceptors; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; + +import com.mysql.cj.conf.ConnectionUrl; +import com.mysql.cj.conf.ConnectionUrlParser; +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.util.StringUtils; + +public class LoadbalanceConnectionUrl extends ConnectionUrl { + /** + * Constructs an instance of {@link LoadbalanceConnectionUrl}, performing all the required initializations and validations. A loadbalance connection + * cannot deal with multiple hosts with same host:port. + * + * @param connStrParser + * a {@link ConnectionUrlParser} instance containing the parsed version of the original connection string + * @param info + * the connection arguments map + */ + public LoadbalanceConnectionUrl(ConnectionUrlParser connStrParser, Properties info) { + super(connStrParser, info); + this.type = Type.LOADBALANCE_CONNECTION; + + // TODO: Validate the hosts list: there can't be any two hosts with same host:port. + // Although this should be required, it also is incompatible with our current tests which are creating load-balanced connections + // using the same host configurations. + // Set visitedHosts = new HashSet<>(); + // for (HostInfo hi : this.hosts) { + // if (visitedHosts.contains(hi.getHostPortPair())) { + // throw ExceptionFactory.createException(WrongArgumentException.class, + // Messages.getString("ConnectionString.12", new Object[] { hi.getHostPortPair(), Type.LOADBALANCE_CONNECTION.getProtocol() })); + // } + // visitedHosts.add(hi.getHostPortPair()); + // } + } + + /** + * Constructs an instance of a {@link LoadbalanceConnectionUrl} based on a list of hosts and a global set of properties instead of connection string + * parsing. + * {@link ConnectionUrl} instances created by this process are not cached. + * + * @param hosts + * the hosts list to use in this connection URL + * @param properties + * the properties common to all hosts + */ + public LoadbalanceConnectionUrl(List hosts, Map properties) { + this.originalConnStr = ConnectionUrl.Type.LOADBALANCE_CONNECTION.getProtocol() + "//**internally_generated**" + System.currentTimeMillis() + "**"; + this.type = ConnectionUrl.Type.LOADBALANCE_CONNECTION; + this.hosts.addAll(hosts); + this.properties.putAll(properties); + injectPerTypeProperties(this.properties); + setupPropertiesTransformer(); // This is needed if new hosts come to be spawned in this connection URL. + } + + /** + * Injects additional properties into the connection arguments while it's being constructed. + * + * @param props + * the properties already containing all known connection arguments + */ + @Override + protected void injectPerTypeProperties(Map props) { + if (props.containsKey(PNAME_loadBalanceAutoCommitStatementThreshold)) { + try { + int autoCommitSwapThreshold = Integer.parseInt(props.get(PNAME_loadBalanceAutoCommitStatementThreshold)); + if (autoCommitSwapThreshold > 0) { + String queryInterceptors = props.get(PNAME_queryInterceptors); + String lbi = "com.mysql.cj.jdbc.ha.LoadBalancedAutoCommitInterceptor"; + if (StringUtils.isNullOrEmpty(queryInterceptors)) { + props.put(PNAME_queryInterceptors, lbi); + } else { + props.put(PNAME_queryInterceptors, queryInterceptors + "," + lbi); + } + } + } catch (Throwable t) { + // Ignore, this will be handled later. + } + } + } + + /** + * Returns a list of this connection URL hosts in the form of host:port pairs. + * + * @return a list of this connection URL hosts in the form of host:port pairs + */ + public List getHostInfoListAsHostPortPairs() { + return this.hosts.stream().map(hi -> hi.getHostPortPair()).collect(Collectors.toList()); + } + + /** + * Returns the list of {@link HostInfo} instances that matches the given collection of host:port pairs. Isolated host info elements are spawned for the + * missing elements. + * + * @param hostPortPairs + * a list of host:port pairs + * @return a list of {@link HostInfo} instances corresponding to the given host:port pairs + */ + public List getHostInfoListFromHostPortPairs(Collection hostPortPairs) { + return hostPortPairs.stream().map(this::getHostOrSpawnIsolated).collect(Collectors.toList()); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/ReplicationConnectionUrl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/ReplicationConnectionUrl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/ReplicationConnectionUrl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf.url; + +import static com.mysql.cj.conf.PropertyDefinitions.TYPE_PROPERTY_KEY; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; + +import com.mysql.cj.conf.ConnectionUrl; +import com.mysql.cj.conf.ConnectionUrlParser; +import com.mysql.cj.conf.HostInfo; + +public class ReplicationConnectionUrl extends ConnectionUrl { + private static final String TYPE_MASTER = "MASTER"; + private static final String TYPE_SLAVE = "SLAVE"; + + private List masterHosts = new ArrayList<>(); + private List slaveHosts = new ArrayList<>(); + + /** + * Constructs an instance of {@link ReplicationConnectionUrl}, performing all the required initializations. + * + * @param connStrParser + * a {@link ConnectionUrlParser} instance containing the parsed version of the original connection string + * @param info + * the connection arguments map + */ + public ReplicationConnectionUrl(ConnectionUrlParser connStrParser, Properties info) { + super(connStrParser, info); + this.type = Type.REPLICATION_CONNECTION; + + // Split masters and slaves: + LinkedList undefinedHosts = new LinkedList<>(); + for (HostInfo hi : this.hosts) { + Map hostProperties = hi.getHostProperties(); + if (hostProperties.containsKey(TYPE_PROPERTY_KEY)) { + if (TYPE_MASTER.equalsIgnoreCase(hostProperties.get(TYPE_PROPERTY_KEY))) { + this.masterHosts.add(hi); + } else if (TYPE_SLAVE.equalsIgnoreCase(hostProperties.get(TYPE_PROPERTY_KEY))) { + this.slaveHosts.add(hi); + } else { + undefinedHosts.add(hi); + } + } else { + undefinedHosts.add(hi); + } + } + if (!undefinedHosts.isEmpty()) { + if (this.masterHosts.isEmpty()) { + this.masterHosts.add(undefinedHosts.removeFirst()); + } + this.slaveHosts.addAll(undefinedHosts); + } + + // TODO: Validate the hosts list: there can't be any two hosts with same host:port. + // Although this should be required, it also is incompatible with our current tests which are creating replication connections + // using the same host configurations. + // Set visitedHosts = new HashSet<>(); + // for (List hostsLists : Arrays.asList(this.masterHosts, this.slaveHosts)) { + // for (HostInfo hi : hostsLists) { + // if (visitedHosts.contains(hi.getHostPortPair())) { + // throw ExceptionFactory.createException(WrongArgumentException.class, + // Messages.getString("ConnectionString.13", new Object[] { hi.getHostPortPair(), Type.REPLICATION_CONNECTION.getProtocol() })); + // } + // visitedHosts.add(hi.getHostPortPair()); + // } + // } + } + + /** + * Constructs an instance of a {@link ReplicationConnectionUrl} based on a list of master hosts, a list of slave hosts and a global set of properties + * instead of connection string parsing. + * {@link ConnectionUrl} instances created by this process are not cached. + * + * @param masters + * the master hosts list to use in this connection string + * @param slaves + * the slave hosts list to use in this connection string + * @param properties + * the properties common to all hosts + */ + public ReplicationConnectionUrl(List masters, List slaves, Map properties) { + this.originalConnStr = ConnectionUrl.Type.REPLICATION_CONNECTION.getProtocol() + "//**internally_generated**" + System.currentTimeMillis() + "**"; + this.type = ConnectionUrl.Type.REPLICATION_CONNECTION; + this.hosts.addAll(masters); + this.hosts.addAll(slaves); + this.masterHosts.addAll(masters); + this.slaveHosts.addAll(slaves); + this.properties.putAll(properties); + injectPerTypeProperties(this.properties); + setupPropertiesTransformer(); // This is needed if new hosts come to be spawned in this connection URL. + } + + /** + * Returns an existing master host info with the same host:port part or spawns a new isolated host info based on this connection URL if none was found. + * + * @param hostPortPair + * the host:port part to search for + * @return the existing host info or a new independent one + */ + public HostInfo getMasterHostOrSpawnIsolated(String hostPortPair) { + return super.getHostOrSpawnIsolated(hostPortPair, this.masterHosts); + } + + /** + * Returns the list of master hosts. + * + * @return the list of master hosts. + */ + public List getMastersList() { + return Collections.unmodifiableList(this.masterHosts); + } + + /** + * Returns a list of this connection URL master hosts in the form of host:port pairs. + * + * @return a list of this connection URL master hosts in the form of host:port pairs + */ + public List getMastersListAsHostPortPairs() { + return this.masterHosts.stream().map(hi -> hi.getHostPortPair()).collect(Collectors.toList()); + } + + /** + * Returns the list of {@link HostInfo} instances that matches the given collection of host:port pairs in the corresponding hosts list. Isolated host info + * elements are spawned for the missing elements. + * + * @param hostPortPairs + * a list of host:port pairs + * @return a list of {@link HostInfo} instances corresponding to the given host:port pairs + */ + public List getMasterHostsListFromHostPortPairs(Collection hostPortPairs) { + return hostPortPairs.stream().map(this::getMasterHostOrSpawnIsolated).collect(Collectors.toList()); + } + + /** + * Returns an existing slave host info with the same host:port part or spawns a new isolated host info based on this connection URL if none was found. + * + * @param hostPortPair + * the host:port part to search for + * @return the existing host info or a new independent one + */ + public HostInfo getSlaveHostOrSpawnIsolated(String hostPortPair) { + return super.getHostOrSpawnIsolated(hostPortPair, this.slaveHosts); + } + + /** + * Returns the list of slave hosts. + * + * @return the list of slave hosts. + */ + public List getSlavesList() { + return Collections.unmodifiableList(this.slaveHosts); + } + + /** + * Returns a list of this connection URL master hosts in the form of host:port pairs. + * + * @return a list of this connection URL master hosts in the form of host:port pairs + */ + public List getSlavesListAsHostPortPairs() { + return this.slaveHosts.stream().map(hi -> hi.getHostPortPair()).collect(Collectors.toList()); + } + + /** + * Returns the list of {@link HostInfo} instances that matches the given collection of host:port pairs in the corresponding hosts list. Isolated host info + * elements are spawned for the missing elements. + * + * @param hostPortPairs + * a list of host:port pairs + * @return a list of {@link HostInfo} instances corresponding to the given host:port pairs + */ + public List getSlaveHostsListFromHostPortPairs(Collection hostPortPairs) { + return hostPortPairs.stream().map(this::getSlaveHostOrSpawnIsolated).collect(Collectors.toList()); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/SingleConnectionUrl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/SingleConnectionUrl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/SingleConnectionUrl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf.url; + +import java.util.Properties; + +import com.mysql.cj.conf.ConnectionUrl; +import com.mysql.cj.conf.ConnectionUrlParser; + +public class SingleConnectionUrl extends ConnectionUrl { + /** + * Constructs an instance of {@link SingleConnectionUrl}, performing all the required initializations. + * + * @param connStrParser + * a {@link ConnectionUrlParser} instance containing the parsed version of the original connection string + * @param info + * the connection arguments map + */ + public SingleConnectionUrl(ConnectionUrlParser connStrParser, Properties info) { + super(connStrParser, info); + this.type = Type.SINGLE_CONNECTION; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/XDevAPIConnectionUrl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/XDevAPIConnectionUrl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/conf/url/XDevAPIConnectionUrl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.conf.url; + +import static com.mysql.cj.conf.PropertyDefinitions.ADDRESS_PROPERTY_KEY; +import static com.mysql.cj.conf.PropertyDefinitions.HOST_PROPERTY_KEY; +import static com.mysql.cj.conf.PropertyDefinitions.PORT_PROPERTY_KEY; +import static com.mysql.cj.conf.PropertyDefinitions.PRIORITY_PROPERTY_KEY; +import static com.mysql.cj.util.StringUtils.isNullOrEmpty; +import static com.mysql.cj.util.StringUtils.safeTrim; + +import java.util.Comparator; +import java.util.Map; +import java.util.Properties; + +import com.mysql.cj.Messages; +import com.mysql.cj.conf.ConnectionUrl; +import com.mysql.cj.conf.ConnectionUrlParser; +import com.mysql.cj.conf.ConnectionUrlParser.Pair; +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.WrongArgumentException; + +public class XDevAPIConnectionUrl extends ConnectionUrl { + private static final int DEFAULT_PORT = 33060; + + /** + * Constructs an instance of {@link XDevAPIConnectionUrl}, performing all the required initializations. + * + * @param connStrParser + * a {@link ConnectionUrlParser} instance containing the parsed version of the original connection string + * @param info + * the connection arguments map + */ + public XDevAPIConnectionUrl(ConnectionUrlParser connStrParser, Properties info) { + super(connStrParser, info); + this.type = Type.XDEVAPI_SESSION; + + /* + * Validate the hosts list: + * 1. Same user and password are required in all hosts. + * 2. If the host property 'priority' is set for one host, then in needs to be set on all others too. + * 3. 'Priority' value must be between 0 and 100. + */ + boolean first = true; + String user = null; + String password = null; + boolean hasPriority = false; + for (HostInfo hi : this.hosts) { + if (first) { + first = false; + user = hi.getUser(); + password = hi.getPassword(); + hasPriority = hi.getHostProperties().containsKey(PRIORITY_PROPERTY_KEY); + } else { + if (!user.equals(hi.getUser()) || !password.equals(hi.getPassword())) { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("ConnectionString.14", new Object[] { Type.XDEVAPI_SESSION.getProtocol() })); + } + if (hasPriority ^ hi.getHostProperties().containsKey(PRIORITY_PROPERTY_KEY)) { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("ConnectionString.15", new Object[] { Type.XDEVAPI_SESSION.getProtocol() })); + } + } + if (hasPriority) { + try { + int priority = Integer.parseInt(hi.getProperty(PRIORITY_PROPERTY_KEY)); + if (priority < 0 || priority > 100) { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("ConnectionString.16", new Object[] { Type.XDEVAPI_SESSION.getProtocol() })); + } + } catch (NumberFormatException e) { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("ConnectionString.16", new Object[] { Type.XDEVAPI_SESSION.getProtocol() })); + } + } + } + + // Sort the hosts list according to their priority settings. + if (hasPriority) { + this.hosts.sort(Comparator. comparing(hi -> Integer.parseInt(hi.getHostProperties().get(PRIORITY_PROPERTY_KEY))).reversed()); + } + } + + @Override + protected void processColdFusionAutoConfiguration() { + // Not needed. Abort this operation. + } + + @Override + protected Map preprocessPerTypeHostProperties(Map hostProps) { + if (hostProps.containsKey(ADDRESS_PROPERTY_KEY)) { + String address = hostProps.get(ADDRESS_PROPERTY_KEY); + Pair hostPortPair = ConnectionUrlParser.parseHostPortPair(address); + String host = safeTrim(hostPortPair.left); + Integer port = hostPortPair.right; + if (!isNullOrEmpty(host) && !hostProps.containsKey(HOST_PROPERTY_KEY)) { + hostProps.put(HOST_PROPERTY_KEY, host); + } + if (port != -1 && !hostProps.containsKey(PORT_PROPERTY_KEY)) { + hostProps.put(PORT_PROPERTY_KEY, port.toString()); + } + } + return hostProps; + } + + @Override + public int getDefaultPort() { + return DEFAULT_PORT; + } + + @Override + protected void fixProtocolDependencies(Map hostProps) { + // Not needed. Abort this operation. + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/AssertionFailedException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/AssertionFailedException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/AssertionFailedException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +import com.mysql.cj.Messages; + +/** + * Assertions for empty code paths that should never be executed. + */ +public class AssertionFailedException extends CJException { + + private static final long serialVersionUID = 5832552608575043403L; + + /** + * Convenience method. + * + * @param ex + * the exception that should never have been thrown. + * @return {@link AssertionFailedException} + * @throws AssertionFailedException + * for the exception ex. + */ + public static AssertionFailedException shouldNotHappen(Exception ex) throws AssertionFailedException { + throw new AssertionFailedException(ex); + } + + /** + * Create (and caller should subsequently throw) an AssertionFailedException. + * + *

+ * Typical use is as follows: + * + *

+     * if (something == null) {
+     *     throw AssertionFailedException.shouldNotHappen("Something cannot be null");
+     * }
+     * 
+ * + * @param assertion + * message + * @return the exception. exception should be thrown by the caller to satisfy compiler checks for data-flow, etc + */ + public static AssertionFailedException shouldNotHappen(String assertion) throws AssertionFailedException { + return new AssertionFailedException(assertion); + } + + /** + * Creates an AssertionFailedException for the given exception that should + * never have been thrown. + * + * @param ex + * the exception that should never have been thrown. + */ + public AssertionFailedException(Exception ex) { + super(Messages.getString("AssertionFailedException.0") + ex.toString() + Messages.getString("AssertionFailedException.1"), ex); + } + + /** + * Creates an AssertionFailedException for the reason given. + * + * @param assertion + * a description of the assertion that failed + */ + public AssertionFailedException(String assertion) { + super(Messages.getString("AssertionFailedException.2", new Object[] { assertion })); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJCommunicationsException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJCommunicationsException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJCommunicationsException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +import com.mysql.cj.conf.PropertySet; +import com.mysql.cj.protocol.ServerSession; + +public class CJCommunicationsException extends CJException { + + private static final long serialVersionUID = 344035358493554245L; + + public CJCommunicationsException() { + super(); + } + + public CJCommunicationsException(String message) { + super(message); + } + + public CJCommunicationsException(String message, Throwable cause) { + super(message, cause); + } + + public CJCommunicationsException(Throwable cause) { + super(cause); + } + + protected CJCommunicationsException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public void init(PropertySet propertySet, ServerSession serverSession, long lastPacketSentTimeMs, long lastPacketReceivedTimeMs) { + this.exceptionMessage = ExceptionFactory.createLinkFailureMessageBasedOnHeuristics(propertySet, serverSession, lastPacketSentTimeMs, + lastPacketReceivedTimeMs, getCause()); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJConnectionFeatureNotAvailableException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJConnectionFeatureNotAvailableException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJConnectionFeatureNotAvailableException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +import com.mysql.cj.Messages; +import com.mysql.cj.conf.PropertySet; +import com.mysql.cj.protocol.ServerSession; + +public class CJConnectionFeatureNotAvailableException extends CJCommunicationsException { + + private static final long serialVersionUID = -4129847384681995107L; + + public CJConnectionFeatureNotAvailableException(PropertySet propertySet, ServerSession serverSession, long lastPacketSentTimeMs, + Exception underlyingException) { + super(underlyingException); + init(propertySet, serverSession, lastPacketSentTimeMs, 0L); + } + + @Override + public String getMessage() { + return Messages.getString("ConnectionFeatureNotAvailableException.0"); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +/** + * The base unchecked exception thrown internally in connector. + */ +public class CJException extends RuntimeException { + + private static final long serialVersionUID = -8618536991444733607L; + + /** + * We can't override the {@link Throwable#detailMessage} directly because it has a private accessibility, + * thus for that need we use this protected variable and override {@link #getMessage()} + */ + protected String exceptionMessage; + + /** + * @serial + */ + private String SQLState = "S1000"; // GENERAL_ERROR by default + + /** + * @serial + */ + private int vendorCode = 0; + + private boolean isTransient = false; + + public CJException() { + super(); + } + + public CJException(String message) { + super(message); + } + + public CJException(Throwable cause) { + super(cause); + } + + public CJException(String message, Throwable cause) { + super(message, cause); + } + + protected CJException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public String getSQLState() { + return this.SQLState; + } + + public void setSQLState(String sQLState) { + this.SQLState = sQLState; + } + + public int getVendorCode() { + return this.vendorCode; + } + + public void setVendorCode(int vendorCode) { + this.vendorCode = vendorCode; + } + + public boolean isTransient() { + return this.isTransient; + } + + public void setTransient(boolean isTransient) { + this.isTransient = isTransient; + } + + @Override + public String getMessage() { + return this.exceptionMessage != null ? this.exceptionMessage : super.getMessage(); + } + + public void appendMessage(String messageToAppend) { + this.exceptionMessage = getMessage() + messageToAppend; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJOperationNotSupportedException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJOperationNotSupportedException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJOperationNotSupportedException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +public class CJOperationNotSupportedException extends CJException { + + private static final long serialVersionUID = 2619184100062994443L; + + public CJOperationNotSupportedException() { + super(); + } + + public CJOperationNotSupportedException(String message) { + super(message); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJPacketTooBigException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJPacketTooBigException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJPacketTooBigException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +import com.mysql.cj.Messages; + +/** + * Thrown when a packet that is too big for the server is created. + */ +public class CJPacketTooBigException extends CJException { + + private static final long serialVersionUID = 7186090399276725363L; + + public CJPacketTooBigException() { + super(); + } + + public CJPacketTooBigException(String message) { + super(message); + } + + public CJPacketTooBigException(Throwable cause) { + super(cause); + } + + public CJPacketTooBigException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Creates a new CJPacketTooBigException object. + * + * @param packetSize + * the size of the packet that was going to be sent + * @param maximumPacketSize + * the maximum size the server will accept + */ + public CJPacketTooBigException(long packetSize, long maximumPacketSize) { + super(Messages.getString("PacketTooBigException.0", new Object[] { packetSize, maximumPacketSize })); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJTimeoutException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJTimeoutException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/CJTimeoutException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +import com.mysql.cj.Messages; + +public class CJTimeoutException extends CJException { + + private static final long serialVersionUID = -7440108828056331100L; + + public CJTimeoutException() { + super(Messages.getString("MySQLTimeoutException.0")); + } + + public CJTimeoutException(String message) { + super(message); + } + + public CJTimeoutException(Throwable cause) { + super(cause); + } + + public CJTimeoutException(String message, Throwable cause) { + super(message, cause); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ClosedOnExpiredPasswordException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ClosedOnExpiredPasswordException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ClosedOnExpiredPasswordException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +/** + * Equivalent to SQLSTATE ER_MUST_CHANGE_PASSWORD_LOGIN = 1862 + * "Your password has expired. To log in you must change it using a client that supports expired passwords." + * + * Server closes connection when this failure happens. + */ +public class ClosedOnExpiredPasswordException extends CJException { + + private static final long serialVersionUID = -3807215681364413250L; + + public ClosedOnExpiredPasswordException() { + super(); + setVendorCode(MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN); + } + + public ClosedOnExpiredPasswordException(String message) { + super(message); + setVendorCode(MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN); + } + + public ClosedOnExpiredPasswordException(String message, Throwable cause) { + super(message, cause); + setVendorCode(MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN); + } + + public ClosedOnExpiredPasswordException(Throwable cause) { + super(cause); + setVendorCode(MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN); + } + + protected ClosedOnExpiredPasswordException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + setVendorCode(MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD_LOGIN); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ConnectionIsClosedException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ConnectionIsClosedException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ConnectionIsClosedException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +/** + * Operation attempted on already closed Connection + */ +public class ConnectionIsClosedException extends CJException { + + private static final long serialVersionUID = -8001652264426656450L; + + public ConnectionIsClosedException() { + super(); + setSQLState("08003"); + } + + public ConnectionIsClosedException(String message) { + super(message); + setSQLState("08003"); + } + + public ConnectionIsClosedException(String message, Throwable cause) { + super(message, cause); + setSQLState("08003"); + } + + public ConnectionIsClosedException(Throwable cause) { + super(cause); + setSQLState("08003"); + } + + protected ConnectionIsClosedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + setSQLState("08003"); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/DataConversionException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/DataConversionException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/DataConversionException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +/** + * Indicates that data could not be converted according to the given request. + */ +public class DataConversionException extends DataReadException { + private static final long serialVersionUID = -863576663404236982L; + + public DataConversionException(String msg) { + super(msg); + setSQLState("22018"); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/DataReadException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/DataReadException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/DataReadException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +/** + * Indicates that data could not be read according to the given request. + */ +public class DataReadException extends CJException { + private static final long serialVersionUID = 1684265521187171525L; + + public DataReadException(Exception cause) { + super(cause); + setSQLState("S1009"); + } + + public DataReadException(String msg) { + super(msg); + setSQLState("S1009"); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/DataTruncationException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/DataTruncationException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/DataTruncationException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +public class DataTruncationException extends CJException { + + private static final long serialVersionUID = -5209088385943506720L; + + /** + * @serial + */ + private int index; + + /** + * @serial + */ + private boolean parameter; + + /** + * @serial + */ + private boolean read; + + /** + * @serial + */ + private int dataSize; + + /** + * @serial + */ + private int transferSize; + + public DataTruncationException() { + super(); + } + + public DataTruncationException(String message) { + super(message); + } + + public DataTruncationException(String message, Throwable cause) { + super(message, cause); + } + + public DataTruncationException(Throwable cause) { + super(cause); + } + + protected DataTruncationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public DataTruncationException(String message, int index, boolean parameter, boolean read, int dataSize, int transferSize, int vendorErrorCode) { + super(message); + this.setIndex(index); + this.setParameter(parameter); + this.setRead(read); + this.setDataSize(dataSize); + this.setTransferSize(transferSize); + setVendorCode(vendorErrorCode); + } + + public int getIndex() { + return this.index; + } + + public void setIndex(int index) { + this.index = index; + } + + public boolean isParameter() { + return this.parameter; + } + + public void setParameter(boolean parameter) { + this.parameter = parameter; + } + + public boolean isRead() { + return this.read; + } + + public void setRead(boolean read) { + this.read = read; + } + + public int getDataSize() { + return this.dataSize; + } + + public void setDataSize(int dataSize) { + this.dataSize = dataSize; + } + + public int getTransferSize() { + return this.transferSize; + } + + public void setTransferSize(int transferSize) { + this.transferSize = transferSize; + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/DeadlockTimeoutRollbackMarker.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/DeadlockTimeoutRollbackMarker.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/DeadlockTimeoutRollbackMarker.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +/** + * Marker interface for exceptions that are caused by deadlock/wait timeout + */ +public interface DeadlockTimeoutRollbackMarker { + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ExceptionFactory.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ExceptionFactory.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ExceptionFactory.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +import java.net.BindException; + +import com.mysql.cj.Messages; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.PropertySet; +import com.mysql.cj.protocol.ServerSession; +import com.mysql.cj.util.Util; + +public class ExceptionFactory { + + private static final long DEFAULT_WAIT_TIMEOUT_SECONDS = 28800; + + private static final int DUE_TO_TIMEOUT_FALSE = 0; + + private static final int DUE_TO_TIMEOUT_MAYBE = 2; + + private static final int DUE_TO_TIMEOUT_TRUE = 1; + + public static CJException createException(String message) { + return createException(CJException.class, message); + } + + @SuppressWarnings("unchecked") + public static T createException(Class clazz, String message) { + + T sqlEx; + try { + sqlEx = clazz.getConstructor(String.class).newInstance(message); + } catch (Throwable e) { + sqlEx = (T) new CJException(message); + } + return sqlEx; + } + + public static CJException createException(String message, ExceptionInterceptor interceptor) { + return createException(CJException.class, message, interceptor); + } + + /** + * + * @param clazz + * exception class + * @param message + * message + * @param interceptor + * exception interceptor + * @param + * {@link CJException} + * @return {@link CJException} instance + */ + public static T createException(Class clazz, String message, ExceptionInterceptor interceptor) { + T sqlEx = createException(clazz, message); + + // TODO: Decide whether we need to intercept exceptions at this level + //if (interceptor != null) { + // @SuppressWarnings("unchecked") + // T interceptedEx = (T) interceptor.interceptException(sqlEx, null); + // if (interceptedEx != null) { + // return interceptedEx; + // } + //} + + return sqlEx; + } + + public static CJException createException(String message, Throwable cause) { + return createException(CJException.class, message, cause); + } + + public static T createException(Class clazz, String message, Throwable cause) { + + T sqlEx = createException(clazz, message); + + if (cause != null) { + try { + sqlEx.initCause(cause); + } catch (Throwable t) { + // we're not going to muck with that here, since it's an error condition anyway! + } + + if (cause instanceof CJException) { + sqlEx.setSQLState(((CJException) cause).getSQLState()); + sqlEx.setVendorCode(((CJException) cause).getVendorCode()); + sqlEx.setTransient(((CJException) cause).isTransient()); + } + } + return sqlEx; + } + + public static CJException createException(String message, Throwable cause, ExceptionInterceptor interceptor) { + return createException(CJException.class, message, cause, interceptor); + } + + public static CJException createException(String message, String sqlState, int vendorErrorCode, boolean isTransient, Throwable cause, + ExceptionInterceptor interceptor) { + CJException ex = createException(CJException.class, message, cause, interceptor); + ex.setSQLState(sqlState); + ex.setVendorCode(vendorErrorCode); + ex.setTransient(isTransient); + return ex; + } + + /** + * + * @param clazz + * exception class + * @param message + * message + * @param cause + * exception caused this one + * @param interceptor + * exception interceptor + * @param + * {@link CJException} + * @return {@link CJException} instance + */ + public static T createException(Class clazz, String message, Throwable cause, ExceptionInterceptor interceptor) { + T sqlEx = createException(clazz, message, cause); + + // TODO: Decide whether we need to intercept exceptions at this level + //if (interceptor != null) { + // @SuppressWarnings("unchecked") + // T interceptedEx = (T) interceptor.interceptException(sqlEx, null); + // if (interceptedEx != null) { + // return interceptedEx; + // } + //} + + return sqlEx; + } + + public static CJCommunicationsException createCommunicationsException(PropertySet propertySet, ServerSession serverSession, long lastPacketSentTimeMs, + long lastPacketReceivedTimeMs, Throwable cause, ExceptionInterceptor interceptor) { + CJCommunicationsException sqlEx = createException(CJCommunicationsException.class, null, cause, interceptor); + sqlEx.init(propertySet, serverSession, lastPacketSentTimeMs, lastPacketReceivedTimeMs); + + // TODO: Decide whether we need to intercept exceptions at this level + //if (interceptor != null) { + // @SuppressWarnings("unchecked") + // T interceptedEx = (T) interceptor.interceptException(sqlEx, null); + // if (interceptedEx != null) { + // return interceptedEx; + // } + //} + + return sqlEx; + } + + /** + * Creates a communications link failure message to be used in CommunicationsException + * that (hopefully) has some better information and suggestions based on heuristics. + * + * @param propertySet + * property set + * @param serverSession + * server session + * @param lastPacketSentTimeMs + * lastPacketSentTimeMs + * @param lastPacketReceivedTimeMs + * lastPacketReceivedTimeMs + * @param underlyingException + * underlyingException + * @return message + */ + public static String createLinkFailureMessageBasedOnHeuristics(PropertySet propertySet, ServerSession serverSession, long lastPacketSentTimeMs, + long lastPacketReceivedTimeMs, Throwable underlyingException) { + long serverTimeoutSeconds = 0; + boolean isInteractiveClient = false; + + if (propertySet != null) { + isInteractiveClient = propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_interactiveClient).getValue(); + + String serverTimeoutSecondsStr = null; + + if (serverSession != null) { + if (isInteractiveClient) { + serverTimeoutSecondsStr = serverSession.getServerVariable("interactive_timeout"); + } else { + serverTimeoutSecondsStr = serverSession.getServerVariable("wait_timeout"); + } + } + + if (serverTimeoutSecondsStr != null) { + try { + serverTimeoutSeconds = Long.parseLong(serverTimeoutSecondsStr); + } catch (NumberFormatException nfe) { + serverTimeoutSeconds = 0; + } + } + } + + StringBuilder exceptionMessageBuf = new StringBuilder(); + + long nowMs = System.currentTimeMillis(); + + if (lastPacketSentTimeMs == 0) { + lastPacketSentTimeMs = nowMs; + } + + long timeSinceLastPacketSentMs = (nowMs - lastPacketSentTimeMs); + long timeSinceLastPacketSeconds = timeSinceLastPacketSentMs / 1000; + + long timeSinceLastPacketReceivedMs = (nowMs - lastPacketReceivedTimeMs); + + int dueToTimeout = DUE_TO_TIMEOUT_FALSE; + + StringBuilder timeoutMessageBuf = null; + + if (serverTimeoutSeconds != 0) { + if (timeSinceLastPacketSeconds > serverTimeoutSeconds) { + dueToTimeout = DUE_TO_TIMEOUT_TRUE; + + timeoutMessageBuf = new StringBuilder(); + + timeoutMessageBuf.append(Messages.getString("CommunicationsException.2")); + + if (!isInteractiveClient) { + timeoutMessageBuf.append(Messages.getString("CommunicationsException.3")); + } else { + timeoutMessageBuf.append(Messages.getString("CommunicationsException.4")); + } + + } + } else if (timeSinceLastPacketSeconds > DEFAULT_WAIT_TIMEOUT_SECONDS) { + dueToTimeout = DUE_TO_TIMEOUT_MAYBE; + + timeoutMessageBuf = new StringBuilder(); + + timeoutMessageBuf.append(Messages.getString("CommunicationsException.5")); + timeoutMessageBuf.append(Messages.getString("CommunicationsException.6")); + timeoutMessageBuf.append(Messages.getString("CommunicationsException.7")); + timeoutMessageBuf.append(Messages.getString("CommunicationsException.8")); + } + + if (dueToTimeout == DUE_TO_TIMEOUT_TRUE || dueToTimeout == DUE_TO_TIMEOUT_MAYBE) { + + if (lastPacketReceivedTimeMs != 0) { + Object[] timingInfo = { Long.valueOf(timeSinceLastPacketReceivedMs), Long.valueOf(timeSinceLastPacketSentMs) }; + exceptionMessageBuf.append(Messages.getString("CommunicationsException.ServerPacketTimingInfo", timingInfo)); + } else { + exceptionMessageBuf.append( + Messages.getString("CommunicationsException.ServerPacketTimingInfoNoRecv", new Object[] { Long.valueOf(timeSinceLastPacketSentMs) })); + } + + if (timeoutMessageBuf != null) { + exceptionMessageBuf.append(timeoutMessageBuf); + } + + exceptionMessageBuf.append(Messages.getString("CommunicationsException.11")); + exceptionMessageBuf.append(Messages.getString("CommunicationsException.12")); + exceptionMessageBuf.append(Messages.getString("CommunicationsException.13")); + + } else { + // + // Attempt to determine the reason for the underlying exception (we can only make a best-guess here) + // + if (underlyingException instanceof BindException) { + String localSocketAddress = propertySet.getStringReadableProperty(PropertyDefinitions.PNAME_localSocketAddress).getValue(); + if (localSocketAddress != null && !Util.interfaceExists(localSocketAddress)) { + exceptionMessageBuf.append(Messages.getString("CommunicationsException.LocalSocketAddressNotAvailable")); + } else { + // too many client connections??? + exceptionMessageBuf.append(Messages.getString("CommunicationsException.TooManyClientConnections")); + } + } + } + + if (exceptionMessageBuf.length() == 0) { + // We haven't figured out a good reason, so copy it. + exceptionMessageBuf.append(Messages.getString("CommunicationsException.20")); + + if (propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_maintainTimeStats).getValue() + && !propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_paranoid).getValue()) { + exceptionMessageBuf.append("\n\n"); + if (lastPacketReceivedTimeMs != 0) { + Object[] timingInfo = { Long.valueOf(timeSinceLastPacketReceivedMs), Long.valueOf(timeSinceLastPacketSentMs) }; + exceptionMessageBuf.append(Messages.getString("CommunicationsException.ServerPacketTimingInfo", timingInfo)); + } else { + exceptionMessageBuf.append(Messages.getString("CommunicationsException.ServerPacketTimingInfoNoRecv", + new Object[] { Long.valueOf(timeSinceLastPacketSentMs) })); + } + } + } + + return exceptionMessageBuf.toString(); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ExceptionInterceptor.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ExceptionInterceptor.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ExceptionInterceptor.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +import java.util.Properties; + +import com.mysql.cj.log.Log; + +public interface ExceptionInterceptor { + + /** + * Called once per connection that wants to use the extension + * + * The properties are the same ones passed in in the URL or arguments to + * Driver.connect() or DriverManager.getConnection(). + * + * @param props + * configuration values as passed to the connection. Note that + * in order to support javax.sql.DataSources, configuration properties specific + * to an interceptor must be passed via setURL() on the + * DataSource. Extension properties are not exposed via + * accessor/mutator methods on DataSources. + * @param log + * logger instance + * @return {@link ExceptionInterceptor} + */ + + ExceptionInterceptor init(Properties props, Log log); + + /** + * Called by the driver when this extension should release any resources + * it is holding and cleanup internally before the connection is + * closed. + */ + void destroy(); + + Exception interceptException(Exception sqlEx); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ExceptionInterceptorChain.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ExceptionInterceptorChain.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/ExceptionInterceptorChain.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.stream.Collectors; + +import com.mysql.cj.log.Log; +import com.mysql.cj.util.Util; + +public class ExceptionInterceptorChain implements ExceptionInterceptor { + List interceptors; + + public ExceptionInterceptorChain(String interceptorClasses, Properties props, Log log) { + this.interceptors = Util. loadClasses(interceptorClasses, "Connection.BadExceptionInterceptor", this).stream() + .map(o -> o.init(props, log)).collect(Collectors.toList()); + } + + public void addRingZero(ExceptionInterceptor interceptor) { + this.interceptors.add(0, interceptor); + } + + public Exception interceptException(Exception sqlEx) { + if (this.interceptors != null) { + Iterator iter = this.interceptors.iterator(); + + while (iter.hasNext()) { + sqlEx = iter.next().interceptException(sqlEx); + } + } + + return sqlEx; + } + + public void destroy() { + if (this.interceptors != null) { + Iterator iter = this.interceptors.iterator(); + + while (iter.hasNext()) { + iter.next().destroy(); + } + } + + } + + public ExceptionInterceptor init(Properties properties, Log log) { + if (this.interceptors != null) { + Iterator iter = this.interceptors.iterator(); + + while (iter.hasNext()) { + iter.next().init(properties, log); + } + } + return this; + } + + public List getInterceptors() { + return this.interceptors; + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/FeatureNotAvailableException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/FeatureNotAvailableException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/FeatureNotAvailableException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +public class FeatureNotAvailableException extends CJException { + + private static final long serialVersionUID = -6649508222074639690L; + + public FeatureNotAvailableException() { + super(); + } + + public FeatureNotAvailableException(String message) { + super(message); + } + + public FeatureNotAvailableException(String message, Throwable cause) { + super(message, cause); + } + + public FeatureNotAvailableException(Throwable cause) { + super(cause); + } + + public FeatureNotAvailableException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/InvalidConnectionAttributeException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/InvalidConnectionAttributeException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/InvalidConnectionAttributeException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +public class InvalidConnectionAttributeException extends CJException { + + private static final long serialVersionUID = -4814924499233623016L; + + public InvalidConnectionAttributeException() { + super(); + setSQLState("01S00"); + } + + public InvalidConnectionAttributeException(String message) { + super(message); + setSQLState("01S00"); + } + + public InvalidConnectionAttributeException(String message, Throwable cause) { + super(message, cause); + setSQLState("01S00"); + } + + public InvalidConnectionAttributeException(Throwable cause) { + super(cause); + setSQLState("01S00"); + } + + public InvalidConnectionAttributeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + setSQLState("01S00"); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/MysqlErrorNumbers.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/MysqlErrorNumbers.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/MysqlErrorNumbers.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,1423 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import com.mysql.cj.Messages; + +/** + * Constants representing MySQL error numbers returned by the server in error messages. + */ +public final class MysqlErrorNumbers { + + public final static int ER_ERROR_MESSAGES = 298; + public final static int ER_HASHCHK = 1000; //SQLSTATE: HY000 Message: hashchk + public final static int ER_NISAMCHK = 1001; //SQLSTATE: HY000 Message: isamchk + public final static int ER_NO = 1002; //SQLSTATE: HY000 Message: NO; Used in the construction of other messages. + public final static int ER_YES = 1003; //SQLSTATE: HY000 Message: YES + public final static int ER_CANT_CREATE_FILE = 1004; //SQLSTATE: HY000 Message: Can't create file '%s' (errno: %d - %s) + public final static int ER_CANT_CREATE_TABLE = 1005; //SQLSTATE: HY000 Message: Can't create table '%s' (errno: %d) + public final static int ER_CANT_CREATE_DB = 1006; //SQLSTATE: HY000 Message: Can't create database '%s' (errno: %d) + public final static int ER_DB_CREATE_EXISTS = 1007; //SQLSTATE: HY000 Message: Can't create database '%s'; database exists... + public final static int ER_DB_DROP_EXISTS = 1008; //SQLSTATE: HY000 Message: Can't drop database '%s'; database doesn't exist + public final static int ER_DB_DROP_DELETE = 1009; //SQLSTATE: HY000 Message: Error dropping database (can't delete '%s', errno: %d) + public final static int ER_DB_DROP_RMDIR = 1010; //SQLSTATE: HY000 Message: Error dropping database (can't rmdir '%s', errno: %d) + public final static int ER_CANT_DELETE_FILE = 1011; //SQLSTATE: HY000 Message: Error on delete of '%s' (errno: %d - %s) + public final static int ER_CANT_FIND_SYSTEM_REC = 1012; //SQLSTATE: HY000 Message: Can't read record in system table + public final static int ER_CANT_GET_STAT = 1013; //SQLSTATE: HY000 Message: Can't get status of '%s' (errno: %d - %s) + public final static int ER_CANT_GET_WD = 1014; //SQLSTATE: HY000 Message: Can't get working directory (errno: %d - %s) + public final static int ER_CANT_LOCK = 1015; //SQLSTATE: HY000 Message: Can't lock file (errno: %d - %s) + public final static int ER_CANT_OPEN_FILE = 1016; //SQLSTATE: HY000 Message: Can't open file: '%s' (errno: %d - %s) + public final static int ER_FILE_NOT_FOUND = 1017; //SQLSTATE: HY000 Message: Can't find file: '%s' (errno: %d - %s) + public final static int ER_CANT_READ_DIR = 1018; //SQLSTATE: HY000 Message: Can't read dir of '%s' (errno: %d - %s) + public final static int ER_CANT_SET_WD = 1019; //SQLSTATE: HY000 Message: Can't change dir to '%s' (errno: %d - %s) + public final static int ER_CHECKREAD = 1020; //SQLSTATE: HY000 Message: Record has changed since last read in table '%s' + public final static int ER_DISK_FULL = 1021; //SQLSTATE: HY000 Message: Disk full (%s); waiting for someone to free some space... (errno: %d - %s) + public final static int ER_DUP_KEY = 1022; //SQLSTATE: 23000 Message: Can't write; duplicate key in table '%s' + public final static int ER_ERROR_ON_CLOSE = 1023; //SQLSTATE: HY000 Message: Error on close of '%s' (errno: %d - %s) + public final static int ER_ERROR_ON_READ = 1024; //SQLSTATE: HY000 Message: Error reading file '%s' (errno: %d - %s) + public final static int ER_ERROR_ON_RENAME = 1025; //SQLSTATE: HY000 Message: Error on rename of '%s' to '%s' (errno: %d - %s) + public final static int ER_ERROR_ON_WRITE = 1026; //SQLSTATE: HY000 Message: Error writing file '%s' (errno: %d - %s) + public final static int ER_FILE_USED = 1027; //SQLSTATE: HY000 Message: '%s' is locked against change + public final static int ER_FILSORT_ABORT = 1028; //SQLSTATE: HY000 Message: Sort aborted + public final static int ER_FORM_NOT_FOUND = 1029; //SQLSTATE: HY000 Message: View '%s' doesn't exist for '%s' + public final static int ER_GET_ERRNO = 1030; //SQLSTATE: HY000 Message: Got error %d from storage engine... + public final static int ER_ILLEGAL_HA = 1031; //SQLSTATE: HY000 Message: Table storage engine for '%s' doesn't have this option + public final static int ER_KEY_NOT_FOUND = 1032; //SQLSTATE: HY000 Message: Can't find record in '%s' + public final static int ER_NOT_FORM_FILE = 1033; //SQLSTATE: HY000 Message: Incorrect information in file: '%s' + public final static int ER_NOT_KEYFILE = 1034; //SQLSTATE: HY000 Message: Incorrect key file for table '%s'; try to repair it + public final static int ER_OLD_KEYFILE = 1035; //SQLSTATE: HY000 Message: Old key file for table '%s'; repair it! + public final static int ER_OPEN_AS_READONLY = 1036; //SQLSTATE: HY000 Message: Table '%s' is read only + public final static int ER_OUTOFMEMORY = 1037; //SQLSTATE: HY001 Message: Out of memory; restart server and try again (needed %d bytes) + public final static int ER_OUT_OF_SORTMEMORY = 1038; //SQLSTATE: HY001 Message: Out of sort memory, consider increasing server sort buffer size + public final static int ER_UNEXPECTED_EOF = 1039; //SQLSTATE: HY000 Message: Unexpected EOF found when reading file '%s' (errno: %d - %s) + public final static int ER_CON_COUNT_ERROR = 1040; //SQLSTATE: 08004 Message: Too many connections + public final static int ER_OUT_OF_RESOURCES = 1041; //SQLSTATE: HY000 Message: Out of memory; check if mysqld or some other process uses all available memory; if not, you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space + public final static int ER_BAD_HOST_ERROR = 1042; //SQLSTATE: 08S01 Message: Can't get hostname for your address + public final static int ER_HANDSHAKE_ERROR = 1043; //SQLSTATE: 08S01 Message: Bad handshake + public final static int ER_DBACCESS_DENIED_ERROR = 1044; //SQLSTATE: 42000 Message: Access denied for user '%s'@'%s' to database '%s' + public final static int ER_ACCESS_DENIED_ERROR = 1045; //SQLSTATE: 28000 Message: Access denied for user '%s'@'%s' (using password: %s) + public final static int ER_NO_DB_ERROR = 1046; //SQLSTATE: 3D000 Message: No database selected + public final static int ER_UNKNOWN_COM_ERROR = 1047; //SQLSTATE: 08S01 Message: Unknown command + public final static int ER_BAD_NULL_ERROR = 1048; //SQLSTATE: 23000 Message: Column '%s' cannot be null + public final static int ER_BAD_DB_ERROR = 1049; //SQLSTATE: 42000 Message: Unknown database '%s' + public final static int ER_TABLE_EXISTS_ERROR = 1050; //SQLSTATE: 42S01 Message: Table '%s' already exists + public final static int ER_BAD_TABLE_ERROR = 1051; //SQLSTATE: 42S02 Message: Unknown table '%s' + public final static int ER_NON_UNIQ_ERROR = 1052; //SQLSTATE: 23000 Message: Column '%s' in %s is ambiguous + public final static int ER_SERVER_SHUTDOWN = 1053; //SQLSTATE: 08S01 Message: Server shutdown in progress + public final static int ER_BAD_FIELD_ERROR = 1054; //SQLSTATE: 42S22 Message: Unknown column '%s' in '%s' + public final static int ER_WRONG_FIELD_WITH_GROUP = 1055; //SQLSTATE: 42000 Message: '%s' isn't in GROUP BY + public final static int ER_WRONG_GROUP_FIELD = 1056; //SQLSTATE: 42000 Message: Can't group on '%s' + public final static int ER_WRONG_SUM_SELECT = 1057; //SQLSTATE: 42000 Message: Statement has sum functions and columns in same statement + public final static int ER_WRONG_VALUE_COUNT = 1058; //SQLSTATE: 21S01 Message: Column count doesn't match value count + public final static int ER_TOO_LONG_IDENT = 1059; //SQLSTATE: 42000 Message: Identifier name '%s' is too long + public final static int ER_DUP_FIELDNAME = 1060; //SQLSTATE: 42S21 Message: Duplicate column name '%s' + public final static int ER_DUP_KEYNAME = 1061; //SQLSTATE: 42000 Message: Duplicate key name '%s' + public final static int ER_DUP_ENTRY = 1062; //SQLSTATE: 23000 Message: Duplicate entry '%s' for key %d + public final static int ER_WRONG_FIELD_SPEC = 1063; //SQLSTATE: 42000 Message: Incorrect column specifier for column '%s' + public final static int ER_PARSE_ERROR = 1064; //SQLSTATE: 42000 Message: %s near '%s' at line %d + public final static int ER_EMPTY_QUERY = 1065; //SQLSTATE: 42000 Message: Query was empty + public final static int ER_NONUNIQ_TABLE = 1066; //SQLSTATE: 42000 Message: Not unique table/alias: '%s' + public final static int ER_INVALID_DEFAULT = 1067; //SQLSTATE: 42000 Message: Invalid default value for '%s' + public final static int ER_MULTIPLE_PRI_KEY = 1068; //SQLSTATE: 42000 Message: Multiple primary key defined + public final static int ER_TOO_MANY_KEYS = 1069; //SQLSTATE: 42000 Message: Too many keys specified; max %d keys allowed + public final static int ER_TOO_MANY_KEY_PARTS = 1070; //SQLSTATE: 42000 Message: Too many key parts specified; max %d parts allowed + public final static int ER_TOO_LONG_KEY = 1071; //SQLSTATE: 42000 Message: Specified key was too long; max key length is %d bytes + public final static int ER_KEY_COLUMN_DOES_NOT_EXITS = 1072; //SQLSTATE: 42000 Message: Key column '%s' doesn't exist in table + public final static int ER_BLOB_USED_AS_KEY = 1073; //SQLSTATE: 42000 Message: BLOB column '%s' can't be used in key specification with the used table type + public final static int ER_TOO_BIG_FIELDLENGTH = 1074; //SQLSTATE: 42000 Message: Column length too big for column '%s' (max = %lu); use BLOB or TEXT instead + public final static int ER_WRONG_AUTO_KEY = 1075; //SQLSTATE: 42000 Message: Incorrect table definition; there can be only one auto column and it must be defined as a key + public final static int ER_READY = 1076; //SQLSTATE: HY000 Message: %s: ready for connections. Version: '%s' socket: '%s' port: %d + public final static int ER_NORMAL_SHUTDOWN = 1077; //SQLSTATE: HY000 Message: %s: Normal shutdown + public final static int ER_GOT_SIGNAL = 1078; //SQLSTATE: HY000 Message: %s: Got signal %d. Aborting! + public final static int ER_SHUTDOWN_COMPLETE = 1079; //SQLSTATE: HY000 Message: %s: Shutdown complete + public final static int ER_FORCING_CLOSE = 1080; //SQLSTATE: 08S01 Message: %s: Forcing close of thread %ld user: '%s' + public final static int ER_IPSOCK_ERROR = 1081; //SQLSTATE: 08S01 Message: Can't create IP socket + public final static int ER_NO_SUCH_INDEX = 1082; //SQLSTATE: 42S12 Message: Table '%s' has no index like the one used in CREATE INDEX; recreate the table + public final static int ER_WRONG_FIELD_TERMINATORS = 1083; //SQLSTATE: 42000 Message: Field separator argument is not what is expected; check the manual + public final static int ER_BLOBS_AND_NO_TERMINATED = 1084; //SQLSTATE: 42000 Message: You can't use fixed rowlength with BLOBs; please use 'fields terminated by' + public final static int ER_TEXTFILE_NOT_READABLE = 1085; //SQLSTATE: HY000 Message: The file '%s' must be in the database directory or be readable by all + public final static int ER_FILE_EXISTS_ERROR = 1086; //SQLSTATE: HY000 Message: File '%s' already exists + public final static int ER_LOAD_INFO = 1087; //SQLSTATE: HY000 Message: Records: %ld Deleted: %ld Skipped: %ld Warnings: %ld + public final static int ER_ALTER_INFO = 1088; //SQLSTATE: HY000 Message: Records: %ld Duplicates: %ld + public final static int ER_WRONG_SUB_KEY = 1089; //SQLSTATE: HY000 Message: Incorrect prefix key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique prefix keys + public final static int ER_CANT_REMOVE_ALL_FIELDS = 1090; //SQLSTATE: 42000 Message: You can't delete all columns with ALTER TABLE; use DROP TABLE instead + public final static int ER_CANT_DROP_FIELD_OR_KEY = 1091; //SQLSTATE: 42000 Message: Can't DROP '%s'; check that column/key exists + public final static int ER_INSERT_INFO = 1092; //SQLSTATE: HY000 Message: Records: %ld Duplicates: %ld Warnings: %ld + public final static int ER_UPDATE_TABLE_USED = 1093; //SQLSTATE: HY000 Message: You can't specify target table '%s' for update in FROM clause + public final static int ER_NO_SUCH_THREAD = 1094; //SQLSTATE: HY000 Message: Unknown thread id: %lu + public final static int ER_KILL_DENIED_ERROR = 1095; //SQLSTATE: HY000 Message: You are not owner of thread %lu + public final static int ER_NO_TABLES_USED = 1096; //SQLSTATE: HY000 Message: No tables used + public final static int ER_TOO_BIG_SET = 1097; //SQLSTATE: HY000 Message: Too many strings for column %s and SET + public final static int ER_NO_UNIQUE_LOGFILE = 1098; //SQLSTATE: HY000 Message: Can't generate a unique log-filename %s.(1-999) + public final static int ER_TABLE_NOT_LOCKED_FOR_WRITE = 1099; //SQLSTATE: HY000 Message: Table '%s' was locked with a READ lock and can't be updated + public final static int ER_TABLE_NOT_LOCKED = 1100; //SQLSTATE: HY000 Message: Table '%s' was not locked with LOCK TABLES + public final static int ER_BLOB_CANT_HAVE_DEFAULT = 1101; //SQLSTATE: 42000 Message: BLOB/TEXT column '%s' can't have a default value + public final static int ER_WRONG_DB_NAME = 1102; //SQLSTATE: 42000 Message: Incorrect database name '%s' + public final static int ER_WRONG_TABLE_NAME = 1103; //SQLSTATE: 42000 Message: Incorrect table name '%s' + public final static int ER_TOO_BIG_SELECT = 1104; //SQLSTATE: 42000 Message: The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay + public final static int ER_UNKNOWN_ERROR = 1105; //SQLSTATE: HY000 Message: Unknown error + public final static int ER_UNKNOWN_PROCEDURE = 1106; //SQLSTATE: 42000 Message: Unknown procedure '%s' + public final static int ER_WRONG_PARAMCOUNT_TO_PROCEDURE = 1107; //SQLSTATE: 42000 Message: Incorrect parameter count to procedure '%s' + public final static int ER_WRONG_PARAMETERS_TO_PROCEDURE = 1108; //SQLSTATE: HY000 Message: Incorrect parameters to procedure '%s' + public final static int ER_UNKNOWN_TABLE = 1109; //SQLSTATE: 42S02 Message: Unknown table '%s' in %s + public final static int ER_FIELD_SPECIFIED_TWICE = 1110; //SQLSTATE: 42000 Message: Column '%s' specified twice + public final static int ER_INVALID_GROUP_FUNC_USE = 1111; //SQLSTATE: HY000 Message: Invalid use of group function + public final static int ER_UNSUPPORTED_EXTENSION = 1112; //SQLSTATE: 42000 Message: Table '%s' uses an extension that doesn't exist in this MySQL version + public final static int ER_TABLE_MUST_HAVE_COLUMNS = 1113; //SQLSTATE: 42000 Message: A table must have at least 1 column + public final static int ER_RECORD_FILE_FULL = 1114; //SQLSTATE: HY000 Message: The table '%s' is full + public final static int ER_UNKNOWN_CHARACTER_SET = 1115; //SQLSTATE: 42000 Message: Unknown character set: '%s' + public final static int ER_TOO_MANY_TABLES = 1116; //SQLSTATE: HY000 Message: Too many tables; MySQL can only use %d tables in a join + public final static int ER_TOO_MANY_FIELDS = 1117; //SQLSTATE: HY000 Message: Too many columns + public final static int ER_TOO_BIG_ROWSIZE = 1118; //SQLSTATE: 42000 Message: Row size too large. The maximum row size for the used table type, not counting BLOBs, is %ld. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs + public final static int ER_STACK_OVERRUN = 1119; //SQLSTATE: HY000 Message: Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld --thread_stack=#' to specify a bigger stack if needed + public final static int ER_WRONG_OUTER_JOIN = 1120; //SQLSTATE: 42000 Message: Cross dependency found in OUTER JOIN; examine your ON conditions + public final static int ER_NULL_COLUMN_IN_INDEX = 1121; //SQLSTATE: 42000 Message: Table handler doesn't support NULL in given index. Please change column '%s' to be NOT NULL or use another handler + public final static int ER_CANT_FIND_UDF = 1122; //SQLSTATE: HY000 Message: Can't load function '%s' + public final static int ER_CANT_INITIALIZE_UDF = 1123; //SQLSTATE: HY000 Message: Can't initialize function '%s'; %s + public final static int ER_UDF_NO_PATHS = 1124; //SQLSTATE: HY000 Message: No paths allowed for shared library + public final static int ER_UDF_EXISTS = 1125; //SQLSTATE: HY000 Message: Function '%s' already exists + public final static int ER_CANT_OPEN_LIBRARY = 1126; //SQLSTATE: HY000 Message: Can't open shared library '%s' (errno: %d %s) + public final static int ER_CANT_FIND_DL_ENTRY = 1127; //SQLSTATE: HY000 Message: Can't find symbol '%s' in library + public final static int ER_FUNCTION_NOT_DEFINED = 1128; //SQLSTATE: HY000 Message: Function '%s' is not defined + public final static int ER_HOST_IS_BLOCKED = 1129; //SQLSTATE: HY000 Message: Host '%s' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts' + public final static int ER_HOST_NOT_PRIVILEGED = 1130; //SQLSTATE: HY000 Message: Host '%s' is not allowed to connect to this MySQL server + public final static int ER_PASSWORD_ANONYMOUS_USER = 1131; //SQLSTATE: 42000 Message: You are using MySQL as an anonymous user and anonymous users are not allowed to change passwords + public final static int ER_PASSWORD_NOT_ALLOWED = 1132; //SQLSTATE: 42000 Message: You must have privileges to update tables in the mysql database to be able to change passwords for others + public final static int ER_PASSWORD_NO_MATCH = 1133; //SQLSTATE: 42000 Message: Can't find any matching row in the user table + public final static int ER_UPDATE_INFO = 1134; //SQLSTATE: HY000 Message: Rows matched: %ld Changed: %ld Warnings: %ld + public final static int ER_CANT_CREATE_THREAD = 1135; //SQLSTATE: HY000 Message: Can't create a new thread (errno %d); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug + public final static int ER_WRONG_VALUE_COUNT_ON_ROW = 1136; //SQLSTATE: 21S01 Message: Column count doesn't match value count at row %ld + public final static int ER_CANT_REOPEN_TABLE = 1137; //SQLSTATE: HY000 Message: Can't reopen table: '%s' + public final static int ER_INVALID_USE_OF_NULL = 1138; //SQLSTATE: 22004 Message: Invalid use of NULL value + public final static int ER_REGEXP_ERROR = 1139; //SQLSTATE: 42000 Message: Got error '%s' from regexp + public final static int ER_MIX_OF_GROUP_FUNC_AND_FIELDS = 1140; //SQLSTATE: 42000 Message: Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause + public final static int ER_NONEXISTING_GRANT = 1141; //SQLSTATE: 42000 Message: There is no such grant defined for user '%s' on host '%s' + public final static int ER_TABLEACCESS_DENIED_ERROR = 1142; //SQLSTATE: 42000 Message: %s command denied to user '%s'@'%s' for table '%s' + public final static int ER_COLUMNACCESS_DENIED_ERROR = 1143; //SQLSTATE: 42000 Message: %s command denied to user '%s'@'%s' for column '%s' in table '%s' + public final static int ER_ILLEGAL_GRANT_FOR_TABLE = 1144; //SQLSTATE: 42000 Message: Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used + public final static int ER_GRANT_WRONG_HOST_OR_USER = 1145; //SQLSTATE: 42000 Message: The host or user argument to GRANT is too long + public final static int ER_NO_SUCH_TABLE = 1146; //SQLSTATE: 42S02 Message: Table '%s.%s' doesn't exist + public final static int ER_NONEXISTING_TABLE_GRANT = 1147; //SQLSTATE: 42000 Message: There is no such grant defined for user '%s' on host '%s' on table '%s' + public final static int ER_NOT_ALLOWED_COMMAND = 1148; //SQLSTATE: 42000 Message: The used command is not allowed with this MySQL version + public final static int ER_SYNTAX_ERROR = 1149; //SQLSTATE: 42000 Message: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use + public final static int ER_DELAYED_CANT_CHANGE_LOCK = 1150; //SQLSTATE: HY000 Message: Delayed insert thread couldn't get requested lock for table %s + public final static int ER_TOO_MANY_DELAYED_THREADS = 1151; //SQLSTATE: HY000 Message: Too many delayed threads in use + public final static int ER_ABORTING_CONNECTION = 1152; //SQLSTATE: 08S01 Message: Aborted connection %ld to db: '%s' user: '%s' (%s) + public final static int ER_NET_PACKET_TOO_LARGE = 1153; //SQLSTATE: 08S01 Message: Got a packet bigger than 'max_allowed_packet' bytes + public final static int ER_NET_READ_ERROR_FROM_PIPE = 1154; //SQLSTATE: 08S01 Message: Got a read error from the connection pipe + public final static int ER_NET_FCNTL_ERROR = 1155; //SQLSTATE: 08S01 Message: Got an error from fcntl() + public final static int ER_NET_PACKETS_OUT_OF_ORDER = 1156; //SQLSTATE: 08S01 Message: Got packets out of order + public final static int ER_NET_UNCOMPRESS_ERROR = 1157; //SQLSTATE: 08S01 Message: Couldn't uncompress communication packet + public final static int ER_NET_READ_ERROR = 1158; //SQLSTATE: 08S01 Message: Got an error reading communication packets + public final static int ER_NET_READ_INTERRUPTED = 1159; //SQLSTATE: 08S01 Message: Got timeout reading communication packets + public final static int ER_NET_ERROR_ON_WRITE = 1160; //SQLSTATE: 08S01 Message: Got an error writing communication packets + public final static int ER_NET_WRITE_INTERRUPTED = 1161; //SQLSTATE: 08S01 Message: Got timeout writing communication packets + public final static int ER_TOO_LONG_STRING = 1162; //SQLSTATE: 42000 Message: Result string is longer than 'max_allowed_packet' bytes + public final static int ER_TABLE_CANT_HANDLE_BLOB = 1163; //SQLSTATE: 42000 Message: The used table type doesn't support BLOB/TEXT columns + public final static int ER_TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164; //SQLSTATE: 42000 Message: The used table type doesn't support AUTO_INCREMENT columns + public final static int ER_DELAYED_INSERT_TABLE_LOCKED = 1165; //SQLSTATE: HY000 Message: INSERT DELAYED can't be used with table '%s' because it is locked with LOCK TABLES + public final static int ER_WRONG_COLUMN_NAME = 1166; //SQLSTATE: 42000 Message: Incorrect column name '%s' + public final static int ER_WRONG_KEY_COLUMN = 1167; //SQLSTATE: 42000 Message: The used storage engine can't index column '%s' + public final static int ER_WRONG_MRG_TABLE = 1168; //SQLSTATE: HY000 Message: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist + public final static int ER_DUP_UNIQUE = 1169; //SQLSTATE: 23000 Message: Can't write, because of unique constraint, to table '%s' + public final static int ER_BLOB_KEY_WITHOUT_LENGTH = 1170; //SQLSTATE: 42000 Message: BLOB/TEXT column '%s' used in key specification without a key length + public final static int ER_PRIMARY_CANT_HAVE_NULL = 1171; //SQLSTATE: 42000 Message: All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead + public final static int ER_TOO_MANY_ROWS = 1172; //SQLSTATE: 42000 Message: Result consisted of more than one row + public final static int ER_REQUIRES_PRIMARY_KEY = 1173; //SQLSTATE: 42000 Message: This table type requires a primary key + public final static int ER_NO_RAID_COMPILED = 1174; //SQLSTATE: HY000 Message: This version of MySQL is not compiled with RAID support + public final static int ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175; //SQLSTATE: HY000 Message: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column + public final static int ER_KEY_DOES_NOT_EXITS = 1176; //SQLSTATE: 42000 Message: Key '%s' doesn't exist in table '%s' + public final static int ER_CHECK_NO_SUCH_TABLE = 1177; //SQLSTATE: 42000 Message: Can't open table + public final static int ER_CHECK_NOT_IMPLEMENTED = 1178; //SQLSTATE: 42000 Message: The storage engine for the table doesn't support %s + public final static int ER_CANT_DO_THIS_DURING_AN_TRANSACTION = 1179; //SQLSTATE: 25000 Message: You are not allowed to execute this command in a transaction + public final static int ER_ERROR_DURING_COMMIT = 1180; //SQLSTATE: HY000 Message: Got error %d during COMMIT + public final static int ER_ERROR_DURING_ROLLBACK = 1181; //SQLSTATE: HY000 Message: Got error %d during ROLLBACK + public final static int ER_ERROR_DURING_FLUSH_LOGS = 1182; //SQLSTATE: HY000 Message: Got error %d during FLUSH_LOGS + public final static int ER_ERROR_DURING_CHECKPOINT = 1183; //SQLSTATE: HY000 Message: Got error %d during CHECKPOINT + public final static int ER_NEW_ABORTING_CONNECTION = 1184; //SQLSTATE: 08S01 Message: Aborted connection %ld to db: '%s' user: '%s' host: '%s' (%s) + public final static int ER_DUMP_NOT_IMPLEMENTED = 1185; //SQLSTATE: HY000 Message: The storage engine for the table does not support binary table dump + public final static int ER_FLUSH_MASTER_BINLOG_CLOSED = 1186; //SQLSTATE: HY000 Message: Binlog closed, cannot RESET MASTER + public final static int ER_INDEX_REBUILD = 1187; //SQLSTATE: HY000 Message: Failed rebuilding the index of dumped table '%s' + public final static int ER_MASTER = 1188; //SQLSTATE: HY000 Message: Error from master: '%s' + public final static int ER_MASTER_NET_READ = 1189; //SQLSTATE: 08S01 Message: Net error reading from master + public final static int ER_MASTER_NET_WRITE = 1190; //SQLSTATE: 08S01 Message: Net error writing to master + public final static int ER_FT_MATCHING_KEY_NOT_FOUND = 1191; //SQLSTATE: HY000 Message: Can't find FULLTEXT index matching the column list + public final static int ER_LOCK_OR_ACTIVE_TRANSACTION = 1192; //SQLSTATE: HY000 Message: Can't execute the given command because you have active locked tables or an active transaction + public final static int ER_UNKNOWN_SYSTEM_VARIABLE = 1193; //SQLSTATE: HY000 Message: Unknown system variable '%s' + public final static int ER_CRASHED_ON_USAGE = 1194; //SQLSTATE: HY000 Message: Table '%s' is marked as crashed and should be repaired + public final static int ER_CRASHED_ON_REPAIR = 1195; //SQLSTATE: HY000 Message: Table '%s' is marked as crashed and last (automatic?) repair failed + public final static int ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196; //SQLSTATE: HY000 Message: Some non-transactional changed tables couldn't be rolled back + public final static int ER_TRANS_CACHE_FULL = 1197; //SQLSTATE: HY000 Message: Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again + public final static int ER_SLAVE_MUST_STOP = 1198; //SQLSTATE: HY000 Message: This operation cannot be performed with a running slave; run STOP SLAVE first + public final static int ER_SLAVE_NOT_RUNNING = 1199; //SQLSTATE: HY000 Message: This operation requires a running slave; configure slave and do START SLAVE + public final static int ER_BAD_SLAVE = 1200; //SQLSTATE: HY000 Message: The server is not configured as slave; fix in config file or with CHANGE MASTER TO + public final static int ER_MASTER_INFO = 1201; //SQLSTATE: HY000 Message: Could not initialize master info structure; more error messages can be found in the MySQL error log + public final static int ER_SLAVE_THREAD = 1202; //SQLSTATE: HY000 Message: Could not create slave thread; check system resources + public final static int ER_TOO_MANY_USER_CONNECTIONS = 1203; //SQLSTATE: 42000 Message: User %s already has more than 'max_user_connections' active connections + public final static int ER_SET_CONSTANTS_ONLY = 1204; //SQLSTATE: HY000 Message: You may only use constant expressions with SET + public final static int ER_LOCK_WAIT_TIMEOUT = 1205; //SQLSTATE: HY000 Message: Lock wait timeout exceeded; try restarting transaction + public final static int ER_LOCK_TABLE_FULL = 1206; //SQLSTATE: HY000 Message: The total number of locks exceeds the lock table size + public final static int ER_READ_ONLY_TRANSACTION = 1207; //SQLSTATE: 25000 Message: Update locks cannot be acquired during a READ UNCOMMITTED transaction + public final static int ER_DROP_DB_WITH_READ_LOCK = 1208; //SQLSTATE: HY000 Message: DROP DATABASE not allowed while thread is holding global read lock + public final static int ER_CREATE_DB_WITH_READ_LOCK = 1209; //SQLSTATE: HY000 Message: CREATE DATABASE not allowed while thread is holding global read lock + public final static int ER_WRONG_ARGUMENTS = 1210; //SQLSTATE: HY000 Message: Incorrect arguments to %s + public final static int ER_NO_PERMISSION_TO_CREATE_USER = 1211; //SQLSTATE: 42000 Message: '%s'@'%s' is not allowed to create new users + public final static int ER_UNION_TABLES_IN_DIFFERENT_DIR = 1212; //SQLSTATE: HY000 Message: Incorrect table definition; all MERGE tables must be in the same database + public final static int ER_LOCK_DEADLOCK = 1213; //SQLSTATE: 40001 Message: Deadlock found when trying to get lock; try restarting transaction + public final static int ER_TABLE_CANT_HANDLE_FT = 1214; //SQLSTATE: HY000 Message: The used table type doesn't support FULLTEXT indexes + public final static int ER_CANNOT_ADD_FOREIGN = 1215; //SQLSTATE: HY000 Message: Cannot add foreign key constraint + public final static int ER_NO_REFERENCED_ROW = 1216; //SQLSTATE: 23000 Message: Cannot add or update a child row: a foreign key constraint fails + public final static int ER_ROW_IS_REFERENCED = 1217; //SQLSTATE: 23000 Message: Cannot delete or update a parent row: a foreign key constraint fails + public final static int ER_CONNECT_TO_MASTER = 1218; //SQLSTATE: 08S01 Message: Error connecting to master: %s + public final static int ER_QUERY_ON_MASTER = 1219; //SQLSTATE: HY000 Message: Error running query on master: %s + public final static int ER_ERROR_WHEN_EXECUTING_COMMAND = 1220; //SQLSTATE: HY000 Message: Error when executing command %s: %s + public final static int ER_WRONG_USAGE = 1221; //SQLSTATE: HY000 Message: Incorrect usage of %s and %s + public final static int ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222; //SQLSTATE: 21000 Message: The used SELECT statements have a different number of columns + public final static int ER_CANT_UPDATE_WITH_READLOCK = 1223; //SQLSTATE: HY000 Message: Can't execute the query because you have a conflicting read lock + public final static int ER_MIXING_NOT_ALLOWED = 1224; //SQLSTATE: HY000 Message: Mixing of transactional and non-transactional tables is disabled + public final static int ER_DUP_ARGUMENT = 1225; //SQLSTATE: HY000 Message: Option '%s' used twice in statement + public final static int ER_USER_LIMIT_REACHED = 1226; //SQLSTATE: 42000 Message: User '%s' has exceeded the '%s' resource (current value: %ld) + public final static int ER_SPECIFIC_ACCESS_DENIED_ERROR = 1227; //SQLSTATE: 42000 Message: Access denied; you need (at least one of) the %s privilege(s) for this operation + public final static int ER_LOCAL_VARIABLE = 1228; //SQLSTATE: HY000 Message: Variable '%s' is a SESSION variable and can't be used with SET GLOBAL + public final static int ER_GLOBAL_VARIABLE = 1229; //SQLSTATE: HY000 Message: Variable '%s' is a GLOBAL variable and should be set with SET GLOBAL + public final static int ER_NO_DEFAULT = 1230; //SQLSTATE: 42000 Message: Variable '%s' doesn't have a default value + public final static int ER_WRONG_VALUE_FOR_VAR = 1231; //SQLSTATE: 42000 Message: Variable '%s' can't be set to the value of '%s' + public final static int ER_WRONG_TYPE_FOR_VAR = 1232; //SQLSTATE: 42000 Message: Incorrect argument type to variable '%s' + public final static int ER_VAR_CANT_BE_READ = 1233; //SQLSTATE: HY000 Message: Variable '%s' can only be set, not read + public final static int ER_CANT_USE_OPTION_HERE = 1234; //SQLSTATE: 42000 Message: Incorrect usage/placement of '%s' + public final static int ER_NOT_SUPPORTED_YET = 1235; //SQLSTATE: 42000 Message: This version of MySQL doesn't yet support '%s' + public final static int ER_MASTER_FATAL_ERROR_READING_BINLOG = 1236; //SQLSTATE: HY000 Message: Got fatal error %d from master when reading data from binary log: '%s' + public final static int ER_SLAVE_IGNORED_TABLE = 1237; //SQLSTATE: HY000 Message: Slave SQL thread ignored the query because of replicate-*-table rules + public final static int ER_INCORRECT_GLOBAL_LOCAL_VAR = 1238; //SQLSTATE: HY000 Message: Variable '%s' is a %s variable + public final static int ER_WRONG_FK_DEF = 1239; //SQLSTATE: 42000 Message: Incorrect foreign key definition for '%s': %s + public final static int ER_KEY_REF_DO_NOT_MATCH_TABLE_REF = 1240; //SQLSTATE: HY000 Message: Key reference and table reference don't match + public final static int ER_OPERAND_COLUMNS = 1241; //SQLSTATE: 21000 Message: Operand should contain %d column(s) + public final static int ER_SUBQUERY_NO_1_ROW = 1242; //SQLSTATE: 21000 Message: Subquery returns more than 1 row + public final static int ER_UNKNOWN_STMT_HANDLER = 1243; //SQLSTATE: HY000 Message: Unknown prepared statement handler (%.*s) given to %s + public final static int ER_CORRUPT_HELP_DB = 1244; //SQLSTATE: HY000 Message: Help database is corrupt or does not exist + public final static int ER_CYCLIC_REFERENCE = 1245; //SQLSTATE: HY000 Message: Cyclic reference on subqueries + public final static int ER_AUTO_CONVERT = 1246; //SQLSTATE: HY000 Message: Converting column '%s' from %s to %s + public final static int ER_ILLEGAL_REFERENCE = 1247; //SQLSTATE: 42S22 Message: Reference '%s' not supported (%s) + public final static int ER_DERIVED_MUST_HAVE_ALIAS = 1248; //SQLSTATE: 42000 Message: Every derived table must have its own alias + public final static int ER_SELECT_REDUCED = 1249; //SQLSTATE: 01000 Message: Select %u was reduced during optimization + public final static int ER_TABLENAME_NOT_ALLOWED_HERE = 1250; //SQLSTATE: 42000 Message: Table '%s' from one of the SELECTs cannot be used in %s + public final static int ER_NOT_SUPPORTED_AUTH_MODE = 1251; //SQLSTATE: 08004 Message: Client does not support authentication protocol requested by server; consider upgrading MySQL client + public final static int ER_SPATIAL_CANT_HAVE_NULL = 1252; //SQLSTATE: 42000 Message: All parts of a SPATIAL index must be NOT NULL + public final static int ER_COLLATION_CHARSET_MISMATCH = 1253; //SQLSTATE: 42000 Message: COLLATION '%s' is not valid for CHARACTER SET '%s' + public final static int ER_SLAVE_WAS_RUNNING = 1254; //SQLSTATE: HY000 Message: Slave is already running + public final static int ER_SLAVE_WAS_NOT_RUNNING = 1255; //SQLSTATE: HY000 Message: Slave already has been stopped + public final static int ER_TOO_BIG_FOR_UNCOMPRESS = 1256; //SQLSTATE: HY000 Message: Uncompressed data size too large; the maximum size is %d (probably, length of uncompressed data was corrupted) + public final static int ER_ZLIB_Z_MEM_ERROR = 1257; //SQLSTATE: HY000 Message: ZLIB: Not enough memory + public final static int ER_ZLIB_Z_BUF_ERROR = 1258; //SQLSTATE: HY000 Message: ZLIB: Not enough room in the output buffer (probably, length of uncompressed data was corrupted) + public final static int ER_ZLIB_Z_DATA_ERROR = 1259; //SQLSTATE: HY000 Message: ZLIB: Input data corrupted + public final static int ER_CUT_VALUE_GROUP_CONCAT = 1260; //SQLSTATE: HY000 Message: Row %u was cut by GROUP_CONCAT() + public final static int ER_WARN_TOO_FEW_RECORDS = 1261; //SQLSTATE: 01000 Message: Row %ld doesn't contain data for all columns + public final static int ER_WARN_TOO_MANY_RECORDS = 1262; //SQLSTATE: 01000 Message: Row %ld was truncated; it contained more data than there were input columns + public final static int ER_WARN_NULL_TO_NOTNULL = 1263; //SQLSTATE: 22004 Message: Column set to default value; NULL supplied to NOT NULL column '%s' at row %ld + public final static int ER_WARN_DATA_OUT_OF_RANGE = 1264; //SQLSTATE: 22003 Message: Out of range value for column '%s' at row %ld + public final static int ER_WARN_DATA_TRUNCATED = 1265; //SQLSTATE: 01000 Message: Data truncated for column '%s' at row %ld + public final static int ER_WARN_USING_OTHER_HANDLER = 1266; //SQLSTATE: HY000 Message: Using storage engine %s for table '%s' + public final static int ER_CANT_AGGREGATE_2COLLATIONS = 1267; //SQLSTATE: HY000 Message: Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s' + public final static int ER_DROP_USER = 1268; //SQLSTATE: HY000 Message: Cannot drop one or more of the requested users + public final static int ER_REVOKE_GRANTS = 1269; //SQLSTATE: HY000 Message: Can't revoke all privileges for one or more of the requested users + public final static int ER_CANT_AGGREGATE_3COLLATIONS = 1270; //SQLSTATE: HY000 Message: Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s' + public final static int ER_CANT_AGGREGATE_NCOLLATIONS = 1271; //SQLSTATE: HY000 Message: Illegal mix of collations for operation '%s' + public final static int ER_VARIABLE_IS_NOT_STRUCT = 1272; //SQLSTATE: HY000 Message: Variable '%s' is not a variable component (can't be used as XXXX.variable_name) + public final static int ER_UNKNOWN_COLLATION = 1273; //SQLSTATE: HY000 Message: Unknown collation: '%s' + public final static int ER_SLAVE_IGNORED_SSL_PARAMS = 1274; //SQLSTATE: HY000 Message: SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later if MySQL slave with SSL is started + public final static int ER_SERVER_IS_IN_SECURE_AUTH_MODE = 1275; //SQLSTATE: HY000 Message: Server is running in --secure-auth mode, but '%s'@'%s' has a password in the old format; please change the password to the new format + public final static int ER_WARN_FIELD_RESOLVED = 1276; //SQLSTATE: HY000 Message: Field or reference '%s%s%s%s%s' of SELECT #%d was resolved in SELECT #%d + public final static int ER_BAD_SLAVE_UNTIL_COND = 1277; //SQLSTATE: HY000 Message: Incorrect parameter or combination of parameters for START SLAVE UNTIL + public final static int ER_MISSING_SKIP_SLAVE = 1278; //SQLSTATE: HY000 Message: It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you will get problems if you get an unexpected slave's mysqld restart + public final static int ER_UNTIL_COND_IGNORED = 1279; //SQLSTATE: HY000 Message: SQL thread is not to be started so UNTIL options are ignored + public final static int ER_WRONG_NAME_FOR_INDEX = 1280; //SQLSTATE: 42000 Message: Incorrect index name '%s' + public final static int ER_WRONG_NAME_FOR_CATALOG = 1281; //SQLSTATE: 42000 Message: Incorrect catalog name '%s' + public final static int ER_WARN_QC_RESIZE = 1282; //SQLSTATE: HY000 Message: Query cache failed to set size %lu; new query cache size is %lu + public final static int ER_BAD_FT_COLUMN = 1283; //SQLSTATE: HY000 Message: Column '%s' cannot be part of FULLTEXT index + public final static int ER_UNKNOWN_KEY_CACHE = 1284; //SQLSTATE: HY000 Message: Unknown key cache '%s' + public final static int ER_WARN_HOSTNAME_WONT_WORK = 1285; //SQLSTATE: HY000 Message: MySQL is started in --skip-name-resolve mode; you must restart it without this switch for this grant to work + public final static int ER_UNKNOWN_STORAGE_ENGINE = 1286; //SQLSTATE: 42000 Message: Unknown storage engine '%s' + public final static int ER_WARN_DEPRECATED_SYNTAX = 1287; //SQLSTATE: HY000 Message: '%s' is deprecated and will be removed in a future release. Please use %s instead + public final static int ER_NON_UPDATABLE_TABLE = 1288; //SQLSTATE: HY000 Message: The target table %s of the %s is not updatable + public final static int ER_FEATURE_DISABLED = 1289; //SQLSTATE: HY000 Message: The '%s' feature is disabled; you need MySQL built with '%s' to have it working + public final static int ER_OPTION_PREVENTS_STATEMENT = 1290; //SQLSTATE: HY000 Message: The MySQL server is running with the %s option so it cannot execute this statement + public final static int ER_DUPLICATED_VALUE_IN_TYPE = 1291; //SQLSTATE: HY000 Message: Column '%s' has duplicated value '%s' in %s + public final static int ER_TRUNCATED_WRONG_VALUE = 1292; //SQLSTATE: 22007 Message: Truncated incorrect %s value: '%s' + public final static int ER_TOO_MUCH_AUTO_TIMESTAMP_COLS = 1293; //SQLSTATE: HY000 Message: Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause + public final static int ER_INVALID_ON_UPDATE = 1294; //SQLSTATE: HY000 Message: Invalid ON UPDATE clause for '%s' column + public final static int ER_UNSUPPORTED_PS = 1295; //SQLSTATE: HY000 Message: This command is not supported in the prepared statement protocol yet + public final static int ER_GET_ERRMSG = 1296; //SQLSTATE: HY000 Message: Got error %d '%s' from %s + public final static int ER_GET_TEMPORARY_ERRMSG = 1297; //SQLSTATE: HY000 Message: Got temporary error %d '%s' from %s + public final static int ER_UNKNOWN_TIME_ZONE = 1298; //SQLSTATE: HY000 Message: Unknown or incorrect time zone: '%s' + public final static int ER_WARN_INVALID_TIMESTAMP = 1299; //SQLSTATE: HY000 Message: Invalid TIMESTAMP value in column '%s' at row %ld + public final static int ER_INVALID_CHARACTER_STRING = 1300; //SQLSTATE: HY000 Message: Invalid %s character string: '%s' + public final static int ER_WARN_ALLOWED_PACKET_OVERFLOWED = 1301; //SQLSTATE: HY000 Message: Result of %s() was larger than max_allowed_packet (%ld) - truncated + public final static int ER_CONFLICTING_DECLARATIONS = 1302; //SQLSTATE: HY000 Message: Conflicting declarations: '%s%s' and '%s%s' + public final static int ER_SP_NO_RECURSIVE_CREATE = 1303; //SQLSTATE: 2F003 Message: Can't create a %s from within another stored routine + public final static int ER_SP_ALREADY_EXISTS = 1304; //SQLSTATE: 42000 Message: %s %s already exists + public final static int ER_SP_DOES_NOT_EXIST = 1305; //SQLSTATE: 42000 Message: %s %s does not exist + public final static int ER_SP_DROP_FAILED = 1306; //SQLSTATE: HY000 Message: Failed to DROP %s %s + public final static int ER_SP_STORE_FAILED = 1307; //SQLSTATE: HY000 Message: Failed to CREATE %s %s + public final static int ER_SP_LILABEL_MISMATCH = 1308; //SQLSTATE: 42000 Message: %s with no matching label: %s + public final static int ER_SP_LABEL_REDEFINE = 1309; //SQLSTATE: 42000 Message: Redefining label %s + public final static int ER_SP_LABEL_MISMATCH = 1310; //SQLSTATE: 42000 Message: End-label %s without match + public final static int ER_SP_UNINIT_VAR = 1311; //SQLSTATE: 01000 Message: Referring to uninitialized variable %s + public final static int ER_SP_BADSELECT = 1312; //SQLSTATE: 0A000 Message: PROCEDURE %s can't return a result set in the given context + public final static int ER_SP_BADRETURN = 1313; //SQLSTATE: 42000 Message: RETURN is only allowed in a FUNCTION + public final static int ER_SP_BADSTATEMENT = 1314; //SQLSTATE: 0A000 Message: %s is not allowed in stored procedures + public final static int ER_UPDATE_LOG_DEPRECATED_IGNORED = 1315; //SQLSTATE: 42000 Message: The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored. This option will be removed in MySQL 5.6. + public final static int ER_UPDATE_LOG_DEPRECATED_TRANSLATED = 1316; //SQLSTATE: 42000 Message: The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN. This option will be removed in MySQL 5.6. + public final static int ER_QUERY_INTERRUPTED = 1317; //SQLSTATE: 70100 Message: Query execution was interrupted + public final static int ER_SP_WRONG_NO_OF_ARGS = 1318; //SQLSTATE: 42000 Message: Incorrect number of arguments for %s %s; expected %u, got %u + public final static int ER_SP_COND_MISMATCH = 1319; //SQLSTATE: 42000 Message: Undefined CONDITION: %s + public final static int ER_SP_NORETURN = 1320; //SQLSTATE: 42000 Message: No RETURN found in FUNCTION %s + public final static int ER_SP_NORETURNEND = 1321; //SQLSTATE: 2F005 Message: FUNCTION %s ended without RETURN + public final static int ER_SP_BAD_CURSOR_QUERY = 1322; //SQLSTATE: 42000 Message: Cursor statement must be a SELECT + public final static int ER_SP_BAD_CURSOR_SELECT = 1323; //SQLSTATE: 42000 Message: Cursor SELECT must not have INTO + public final static int ER_SP_CURSOR_MISMATCH = 1324; //SQLSTATE: 42000 Message: Undefined CURSOR: %s + public final static int ER_SP_CURSOR_ALREADY_OPEN = 1325; //SQLSTATE: 24000 Message: Cursor is already open + public final static int ER_SP_CURSOR_NOT_OPEN = 1326; //SQLSTATE: 24000 Message: Cursor is not open + public final static int ER_SP_UNDECLARED_VAR = 1327; //SQLSTATE: 42000 Message: Undeclared variable: %s + public final static int ER_SP_WRONG_NO_OF_FETCH_ARGS = 1328; //SQLSTATE: HY000 Message: Incorrect number of FETCH variables + public final static int ER_SP_FETCH_NO_DATA = 1329; //SQLSTATE: 02000 Message: No data - zero rows fetched, selected, or processed + public final static int ER_SP_DUP_PARAM = 1330; //SQLSTATE: 42000 Message: Duplicate parameter: %s + public final static int ER_SP_DUP_VAR = 1331; //SQLSTATE: 42000 Message: Duplicate variable: %s + public final static int ER_SP_DUP_COND = 1332; //SQLSTATE: 42000 Message: Duplicate condition: %s + public final static int ER_SP_DUP_CURS = 1333; //SQLSTATE: 42000 Message: Duplicate cursor: %s + public final static int ER_SP_CANT_ALTER = 1334; //SQLSTATE: HY000 Message: Failed to ALTER %s %s + public final static int ER_SP_SUBSELECT_NYI = 1335; //SQLSTATE: 0A000 Message: Subquery value not supported + public final static int ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG = 1336; //SQLSTATE: 0A000 Message: %s is not allowed in stored function or trigger + public final static int ER_SP_VARCOND_AFTER_CURSHNDLR = 1337; //SQLSTATE: 42000 Message: Variable or condition declaration after cursor or handler declaration + public final static int ER_SP_CURSOR_AFTER_HANDLER = 1338; //SQLSTATE: 42000 Message: Cursor declaration after handler declaration + public final static int ER_SP_CASE_NOT_FOUND = 1339; //SQLSTATE: 20000 Message: Case not found for CASE statement + public final static int ER_FPARSER_TOO_BIG_FILE = 1340; //SQLSTATE: HY000 Message: Configuration file '%s' is too big + public final static int ER_FPARSER_BAD_HEADER = 1341; //SQLSTATE: HY000 Message: Malformed file type header in file '%s' + public final static int ER_FPARSER_EOF_IN_COMMENT = 1342; //SQLSTATE: HY000 Message: Unexpected end of file while parsing comment '%s' + public final static int ER_FPARSER_ERROR_IN_PARAMETER = 1343; //SQLSTATE: HY000 Message: Error while parsing parameter '%s' (line: '%s') + public final static int ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344; //SQLSTATE: HY000 Message: Unexpected end of file while skipping unknown parameter '%s' + public final static int ER_VIEW_NO_EXPLAIN = 1345; //SQLSTATE: HY000 Message: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table + public final static int ER_FRM_UNKNOWN_TYPE = 1346; //SQLSTATE: HY000 Message: File '%s' has unknown type '%s' in its header + public final static int ER_WRONG_OBJECT = 1347; //SQLSTATE: HY000 Message: '%s.%s' is not %s + public final static int ER_NONUPDATEABLE_COLUMN = 1348; //SQLSTATE: HY000 Message: Column '%s' is not updatable + public final static int ER_VIEW_SELECT_DERIVED = 1349; //SQLSTATE: HY000 Message: View's SELECT contains a subquery in the FROM clause + public final static int ER_VIEW_SELECT_CLAUSE = 1350; //SQLSTATE: HY000 Message: View's SELECT contains a '%s' clause + public final static int ER_VIEW_SELECT_VARIABLE = 1351; //SQLSTATE: HY000 Message: View's SELECT contains a variable or parameter + public final static int ER_VIEW_SELECT_TMPTABLE = 1352; //SQLSTATE: HY000 Message: View's SELECT refers to a temporary table '%s' + public final static int ER_VIEW_WRONG_LIST = 1353; //SQLSTATE: HY000 Message: View's SELECT and view's field list have different column counts + public final static int ER_WARN_VIEW_MERGE = 1354; //SQLSTATE: HY000 Message: View merge algorithm can't be used here for now (assumed undefined algorithm) + public final static int ER_WARN_VIEW_WITHOUT_KEY = 1355; //SQLSTATE: HY000 Message: View being updated does not have complete key of underlying table in it + public final static int ER_VIEW_INVALID = 1356; //SQLSTATE: HY000 Message: View '%s.%s' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them + public final static int ER_SP_NO_DROP_SP = 1357; //SQLSTATE: HY000 Message: Can't drop or alter a %s from within another stored routine + public final static int ER_SP_GOTO_IN_HNDLR = 1358; //SQLSTATE: HY000 Message: GOTO is not allowed in a stored procedure handler + public final static int ER_TRG_ALREADY_EXISTS = 1359; //SQLSTATE: HY000 Message: Trigger already exists + public final static int ER_TRG_DOES_NOT_EXIST = 1360; //SQLSTATE: HY000 Message: Trigger does not exist + public final static int ER_TRG_ON_VIEW_OR_TEMP_TABLE = 1361; //SQLSTATE: HY000 Message: Trigger's '%s' is view or temporary table + public final static int ER_TRG_CANT_CHANGE_ROW = 1362; //SQLSTATE: HY000 Message: Updating of %s row is not allowed in %strigger + public final static int ER_TRG_NO_SUCH_ROW_IN_TRG = 1363; //SQLSTATE: HY000 Message: There is no %s row in %s trigger + public final static int ER_NO_DEFAULT_FOR_FIELD = 1364; //SQLSTATE: HY000 Message: Field '%s' doesn't have a default value + public final static int ER_DIVISION_BY_ZERO = 1365; //SQLSTATE: 22012 Message: Division by 0 + public final static int ER_TRUNCATED_WRONG_VALUE_FOR_FIELD = 1366; //SQLSTATE: HY000 Message: Incorrect %s value: '%s' for column '%s' at row %ld + public final static int ER_ILLEGAL_VALUE_FOR_TYPE = 1367; //SQLSTATE: 22007 Message: Illegal %s '%s' value found during parsing + public final static int ER_VIEW_NONUPD_CHECK = 1368; //SQLSTATE: HY000 Message: CHECK OPTION on non-updatable view '%s.%s' + public final static int ER_VIEW_CHECK_FAILED = 1369; //SQLSTATE: HY000 Message: CHECK OPTION failed '%s.%s' + public final static int ER_PROCACCESS_DENIED_ERROR = 1370; //SQLSTATE: 42000 Message: %s command denied to user '%s'@'%s' for routine '%s' + public final static int ER_RELAY_LOG_FAIL = 1371; //SQLSTATE: HY000 Message: Failed purging old relay logs: %s + public final static int ER_PASSWD_LENGTH = 1372; //SQLSTATE: HY000 Message: Password hash should be a %d-digit hexadecimal number + public final static int ER_UNKNOWN_TARGET_BINLOG = 1373; //SQLSTATE: HY000 Message: Target log not found in binlog index + public final static int ER_IO_ERR_LOG_INDEX_READ = 1374; //SQLSTATE: HY000 Message: I/O error reading log index file + public final static int ER_BINLOG_PURGE_PROHIBITED = 1375; //SQLSTATE: HY000 Message: Server configuration does not permit binlog purge + public final static int ER_FSEEK_FAIL = 1376; //SQLSTATE: HY000 Message: Failed on fseek() + public final static int ER_BINLOG_PURGE_FATAL_ERR = 1377; //SQLSTATE: HY000 Message: Fatal error during log purge + public final static int ER_LOG_IN_USE = 1378; //SQLSTATE: HY000 Message: A purgeable log is in use, will not purge + public final static int ER_LOG_PURGE_UNKNOWN_ERR = 1379; //SQLSTATE: HY000 Message: Unknown error during log purge + public final static int ER_RELAY_LOG_INIT = 1380; //SQLSTATE: HY000 Message: Failed initializing relay log position: %s + public final static int ER_NO_BINARY_LOGGING = 1381; //SQLSTATE: HY000 Message: You are not using binary logging + public final static int ER_RESERVED_SYNTAX = 1382; //SQLSTATE: HY000 Message: The '%s' syntax is reserved for purposes internal to the MySQL server + public final static int ER_WSAS_FAILED = 1383; //SQLSTATE: HY000 Message: WSAStartup Failed + public final static int ER_DIFF_GROUPS_PROC = 1384; //SQLSTATE: HY000 Message: Can't handle procedures with different groups yet + public final static int ER_NO_GROUP_FOR_PROC = 1385; //SQLSTATE: HY000 Message: Select must have a group with this procedure + public final static int ER_ORDER_WITH_PROC = 1386; //SQLSTATE: HY000 Message: Can't use ORDER clause with this procedure + public final static int ER_LOGGING_PROHIBIT_CHANGING_OF = 1387; //SQLSTATE: HY000 Message: Binary logging and replication forbid changing the global server %s + public final static int ER_NO_FILE_MAPPING = 1388; //SQLSTATE: HY000 Message: Can't map file: %s, errno: %d + public final static int ER_WRONG_MAGIC = 1389; //SQLSTATE: HY000 Message: Wrong magic in %s + public final static int ER_PS_MANY_PARAM = 1390; //SQLSTATE: HY000 Message: Prepared statement contains too many placeholders + public final static int ER_KEY_PART_0 = 1391; //SQLSTATE: HY000 Message: Key part '%s' length cannot be 0 + public final static int ER_VIEW_CHECKSUM = 1392; //SQLSTATE: HY000 Message: View text checksum failed + public final static int ER_VIEW_MULTIUPDATE = 1393; //SQLSTATE: HY000 Message: Can not modify more than one base table through a join view '%s.%s' + public final static int ER_VIEW_NO_INSERT_FIELD_LIST = 1394; //SQLSTATE: HY000 Message: Can not insert into join view '%s.%s' without fields list + public final static int ER_VIEW_DELETE_MERGE_VIEW = 1395; //SQLSTATE: HY000 Message: Can not delete from join view '%s.%s' + public final static int ER_CANNOT_USER = 1396; //SQLSTATE: HY000 Message: Operation %s failed for %s + public final static int ER_XAER_NOTA = 1397; //SQLSTATE: XAE04 Message: XAER_NOTA: Unknown XID + public final static int ER_XAER_INVAL = 1398; //SQLSTATE: XAE05 Message: XAER_INVAL: Invalid arguments (or unsupported command) + public final static int ER_XAER_RMFAIL = 1399; //SQLSTATE: XAE07 Message: XAER_RMFAIL: The command cannot be executed when global transaction is in the %s state + public final static int ER_XAER_OUTSIDE = 1400; //SQLSTATE: XAE09 Message: XAER_OUTSIDE: Some work is done outside global transaction + public final static int ER_XA_RMERR = 1401; + public final static int ER_XA_RBROLLBACK = 1402; //SQLSTATE: XA100 Message: XA_RBROLLBACK: Transaction branch was rolled back + public final static int ER_NONEXISTING_PROC_GRANT = 1403; //SQLSTATE: 42000 Message: There is no such grant defined for user '%s' on host '%s' on routine '%s' + public final static int ER_PROC_AUTO_GRANT_FAIL = 1404; //SQLSTATE: HY000 Message: Failed to grant EXECUTE and ALTER ROUTINE privileges + public final static int ER_PROC_AUTO_REVOKE_FAIL = 1405; //SQLSTATE: HY000 Message: Failed to revoke all privileges to dropped routine + public final static int ER_DATA_TOO_LONG = 1406; //SQLSTATE: 22001 Message: Data too long for column '%s' at row %ld + public final static int ER_SP_BAD_SQLSTATE = 1407; //SQLSTATE: 42000 Message: Bad; //SQLSTATE: '%s' + public final static int ER_STARTUP = 1408; //SQLSTATE: HY000 Message: %s: ready for connections. Version: '%s' socket: '%s' port: %d %s + public final static int ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR = 1409; //SQLSTATE: HY000 Message: Can't load value from file with fixed size rows to variable + public final static int ER_CANT_CREATE_USER_WITH_GRANT = 1410; //SQLSTATE: 42000 Message: You are not allowed to create a user with GRANT + public final static int ER_WRONG_VALUE_FOR_TYPE = 1411; //SQLSTATE: HY000 Message: Incorrect %s value: '%s' for function %s + public final static int ER_TABLE_DEF_CHANGED = 1412; //SQLSTATE: HY000 Message: Table definition has changed, please retry transaction + public final static int ER_SP_DUP_HANDLER = 1413; //SQLSTATE: 42000 Message: Duplicate handler declared in the same block + public final static int ER_SP_NOT_VAR_ARG = 1414; //SQLSTATE: 42000 Message: OUT or INOUT argument %d for routine %s is not a variable or NEW pseudo-variable in BEFORE trigger + public final static int ER_SP_NO_RETSET = 1415; //SQLSTATE: 0A000 Message: Not allowed to return a result set from a %s + public final static int ER_CANT_CREATE_GEOMETRY_OBJECT = 1416; //SQLSTATE: 22003 Message: Cannot get geometry object from data you send to the GEOMETRY field + public final static int ER_FAILED_ROUTINE_BREAK_BINLOG = 1417; //SQLSTATE: HY000 Message: A routine failed and has neither NO SQL nor READS SQL DATA in its declaration and binary logging is enabled; if non-transactional tables were updated, the binary log will miss their changes + public final static int ER_BINLOG_UNSAFE_ROUTINE = 1418; //SQLSTATE: HY000 Message: This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable) + public final static int ER_BINLOG_CREATE_ROUTINE_NEED_SUPER = 1419; //SQLSTATE: HY000 Message: You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable) + public final static int ER_EXEC_STMT_WITH_OPEN_CURSOR = 1420; //SQLSTATE: HY000 Message: You can't execute a prepared statement which has an open cursor associated with it. Reset the statement to re-execute it. + public final static int ER_STMT_HAS_NO_OPEN_CURSOR = 1421; //SQLSTATE: HY000 Message: The statement (%lu) has no open cursor. + public final static int ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG = 1422; //SQLSTATE: HY000 Message: Explicit or implicit commit is not allowed in stored function or trigger. + public final static int ER_NO_DEFAULT_FOR_VIEW_FIELD = 1423; //SQLSTATE: HY000 Message: Field of view '%s.%s' underlying table doesn't have a default value + public final static int ER_SP_NO_RECURSION = 1424; //SQLSTATE: HY000 Message: Recursive stored functions and triggers are not allowed. + public final static int ER_TOO_BIG_SCALE = 1425; //SQLSTATE: 42000 Message: Too big scale %d specified for column '%s'. Maximum is %lu. + public final static int ER_TOO_BIG_PRECISION = 1426; //SQLSTATE: 42000 Message: Too big precision %d specified for column '%s'. Maximum is %lu. + public final static int ER_M_BIGGER_THAN_D = 1427; //SQLSTATE: 42000 Message: For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '%s'). + public final static int ER_WRONG_LOCK_OF_SYSTEM_TABLE = 1428; //SQLSTATE: HY000 Message: You can't combine write-locking of system tables with other tables or lock types + public final static int ER_CONNECT_TO_FOREIGN_DATA_SOURCE = 1429; //SQLSTATE: HY000 Message: Unable to connect to foreign data source: %s + public final static int ER_QUERY_ON_FOREIGN_DATA_SOURCE = 1430; //SQLSTATE: HY000 Message: There was a problem processing the query on the foreign data source. Data source error: %s + public final static int ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST = 1431; //SQLSTATE: HY000 Message: The foreign data source you are trying to reference does not exist. Data source error: %s + public final static int ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE = 1432; //SQLSTATE: HY000 Message: Can't create federated table. The data source connection string '%s' is not in the correct format + public final static int ER_FOREIGN_DATA_STRING_INVALID = 1433; //SQLSTATE: HY000 Message: The data source connection string '%s' is not in the correct format + public final static int ER_CANT_CREATE_FEDERATED_TABLE = 1434; //SQLSTATE: HY000 Message: Can't create federated table. Foreign data src error: %s + public final static int ER_TRG_IN_WRONG_SCHEMA = 1435; //SQLSTATE: HY000 Message: Trigger in wrong schema + public final static int ER_STACK_OVERRUN_NEED_MORE = 1436; //SQLSTATE: HY000 Message: Thread stack overrun: %ld bytes used of a %ld byte stack, and %ld bytes needed. Use 'mysqld --thread_stack=#' to specify a bigger stack. + public final static int ER_TOO_LONG_BODY = 1437; //SQLSTATE: 42000 Message: Routine body for '%s' is too long + public final static int ER_WARN_CANT_DROP_DEFAULT_KEYCACHE = 1438; //SQLSTATE: HY000 Message: Cannot drop default keycache + public final static int ER_TOO_BIG_DISPLAYWIDTH = 1439; //SQLSTATE: 42000 Message: Display width out of range for column '%s' (max = %lu) + public final static int ER_XAER_DUPID = 1440; //SQLSTATE: XAE08 Message: XAER_DUPID: The XID already exists + public final static int ER_DATETIME_FUNCTION_OVERFLOW = 1441; //SQLSTATE: 22008 Message: Datetime function: %s field overflow + public final static int ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG = 1442; //SQLSTATE: HY000 Message: Can't update table '%s' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. + public final static int ER_VIEW_PREVENT_UPDATE = 1443; //SQLSTATE: HY000 Message: The definition of table '%s' prevents operation %s on table '%s'. + public final static int ER_PS_NO_RECURSION = 1444; //SQLSTATE: HY000 Message: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner + public final static int ER_SP_CANT_SET_AUTOCOMMIT = 1445; //SQLSTATE: HY000 Message: Not allowed to set autocommit from a stored function or trigger + public final static int ER_MALFORMED_DEFINER = 1446; //SQLSTATE: HY000 Message: Definer is not fully qualified + public final static int ER_VIEW_FRM_NO_USER = 1447; //SQLSTATE: HY000 Message: View '%s'.'%s' has no definer information (old table format). Current user is used as definer. Please recreate the view! + public final static int ER_VIEW_OTHER_USER = 1448; //SQLSTATE: HY000 Message: You need the SUPER privilege for creation view with '%s'@'%s' definer + public final static int ER_NO_SUCH_USER = 1449; //SQLSTATE: HY000 Message: The user specified as a definer ('%s'@'%s') does not exist + public final static int ER_FORBID_SCHEMA_CHANGE = 1450; //SQLSTATE: HY000 Message: Changing schema from '%s' to '%s' is not allowed. + public final static int ER_ROW_IS_REFERENCED_2 = 1451; //SQLSTATE: 23000 Message: Cannot delete or update a parent row: a foreign key constraint fails (%s) + public final static int ER_NO_REFERENCED_ROW_2 = 1452; //SQLSTATE: 23000 Message: Cannot add or update a child row: a foreign key constraint fails (%s) + public final static int ER_SP_BAD_VAR_SHADOW = 1453; //SQLSTATE: 42000 Message: Variable '%s' must be quoted with `...`, or renamed + public final static int ER_TRG_NO_DEFINER = 1454; //SQLSTATE: HY000 Message: No definer attribute for trigger '%s'.'%s'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger. + public final static int ER_OLD_FILE_FORMAT = 1455; //SQLSTATE: HY000 Message: '%s' has an old format, you should re-create the '%s' object(s) + public final static int ER_SP_RECURSION_LIMIT = 1456; //SQLSTATE: HY000 Message: Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %s + public final static int ER_SP_PROC_TABLE_CORRUPT = 1457; //SQLSTATE: HY000 Message: Failed to load routine %s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d) + public final static int ER_SP_WRONG_NAME = 1458; //SQLSTATE: 42000 Message: Incorrect routine name '%s' + public final static int ER_TABLE_NEEDS_UPGRADE = 1459; //SQLSTATE: HY000 Message: Table upgrade required. Please do "REPAIR TABLE `%s`" or dump/reload to fix it! + public final static int ER_SP_NO_AGGREGATE = 1460; //SQLSTATE: 42000 Message: AGGREGATE is not supported for stored functions + public final static int ER_MAX_PREPARED_STMT_COUNT_REACHED = 1461; //SQLSTATE: 42000 Message: Can't create more than max_prepared_stmt_count statements (current value: %lu) + public final static int ER_VIEW_RECURSIVE = 1462; //SQLSTATE: HY000 Message: `%s`.`%s` contains view recursion + public final static int ER_NON_GROUPING_FIELD_USED = 1463; //SQLSTATE: 42000 Message: non-grouping field '%s' is used in %s clause + public final static int ER_TABLE_CANT_HANDLE_SPKEYS = 1464; //SQLSTATE: HY000 Message: The used table type doesn't support SPATIAL indexes + public final static int ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA = 1465; //SQLSTATE: HY000 Message: Triggers can not be created on system tables + public final static int ER_REMOVED_SPACES = 1466; //SQLSTATE: HY000 Message: Leading spaces are removed from name '%s' + public final static int ER_AUTOINC_READ_FAILED = 1467; //SQLSTATE: HY000 Message: Failed to read auto-increment value from storage engine + public final static int ER_USERNAME = 1468; //SQLSTATE: HY000 Message: user name + public final static int ER_HOSTNAME = 1469; //SQLSTATE: HY000 Message: host name + public final static int ER_WRONG_STRING_LENGTH = 1470; //SQLSTATE: HY000 Message: String '%s' is too long for %s (should be no longer than %d) + public final static int ER_NON_INSERTABLE_TABLE = 1471; //SQLSTATE: HY000 Message: The target table %s of the %s is not insertable-into + public final static int ER_ADMIN_WRONG_MRG_TABLE = 1472; //SQLSTATE: HY000 Message: Table '%s' is differently defined or of non-MyISAM type or doesn't exist + public final static int ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT = 1473; //SQLSTATE: HY000 Message: Too high level of nesting for select + public final static int ER_NAME_BECOMES_EMPTY = 1474; //SQLSTATE: HY000 Message: Name '%s' has become '' + public final static int ER_AMBIGUOUS_FIELD_TERM = 1475; //SQLSTATE: HY000 Message: First character of the FIELDS TERMINATED string is ambiguous; please use non-optional and non-empty FIELDS ENCLOSED BY + public final static int ER_FOREIGN_SERVER_EXISTS = 1476; //SQLSTATE: HY000 Message: The foreign server, %s, you are trying to create already exists. + public final static int ER_FOREIGN_SERVER_DOESNT_EXIST = 1477; //SQLSTATE: HY000 Message: The foreign server name you are trying to reference does not exist. Data source error: %s + public final static int ER_ILLEGAL_HA_CREATE_OPTION = 1478; //SQLSTATE: HY000 Message: Table storage engine '%s' does not support the create option '%s' + public final static int ER_PARTITION_REQUIRES_VALUES_ERROR = 1479; //SQLSTATE: HY000 Message: Syntax error: %s PARTITIONING requires definition of VALUES %s for each partition + public final static int ER_PARTITION_WRONG_VALUES_ERROR = 1480; //SQLSTATE: HY000 Message: Only %s PARTITIONING can use VALUES %s in partition definition + public final static int ER_PARTITION_MAXVALUE_ERROR = 1481; //SQLSTATE: HY000 Message: MAXVALUE can only be used in last partition definition + public final static int ER_PARTITION_SUBPARTITION_ERROR = 1482; //SQLSTATE: HY000 Message: Subpartitions can only be hash partitions and by key + public final static int ER_PARTITION_SUBPART_MIX_ERROR = 1483; //SQLSTATE: HY000 Message: Must define subpartitions on all partitions if on one partition + public final static int ER_PARTITION_WRONG_NO_PART_ERROR = 1484; //SQLSTATE: HY000 Message: Wrong number of partitions defined, mismatch with previous setting + public final static int ER_PARTITION_WRONG_NO_SUBPART_ERROR = 1485; //SQLSTATE: HY000 Message: Wrong number of subpartitions defined, mismatch with previous setting + public final static int ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR = 1486; //SQLSTATE: HY000 Message: Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed + public final static int ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR = 1487; //SQLSTATE: HY000 Message: Expression in RANGE/LIST VALUES must be constant + public final static int ER_FIELD_NOT_FOUND_PART_ERROR = 1488; //SQLSTATE: HY000 Message: Field in list of fields for partition function not found in table + public final static int ER_LIST_OF_FIELDS_ONLY_IN_HASH_ERROR = 1489; //SQLSTATE: HY000 Message: List of fields is only allowed in KEY partitions + public final static int ER_INCONSISTENT_PARTITION_INFO_ERROR = 1490; //SQLSTATE: HY000 Message: The partition info in the frm file is not consistent with what can be written into the frm file + public final static int ER_PARTITION_FUNC_NOT_ALLOWED_ERROR = 1491; //SQLSTATE: HY000 Message: The %s function returns the wrong type + public final static int ER_PARTITIONS_MUST_BE_DEFINED_ERROR = 1492; //SQLSTATE: HY000 Message: For %s partitions each partition must be defined + public final static int ER_RANGE_NOT_INCREASING_ERROR = 1493; //SQLSTATE: HY000 Message: VALUES LESS THAN value must be strictly increasing for each partition + public final static int ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR = 1494; //SQLSTATE: HY000 Message: VALUES value must be of same type as partition function + public final static int ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR = 1495; //SQLSTATE: HY000 Message: Multiple definition of same constant in list partitioning + public final static int ER_PARTITION_ENTRY_ERROR = 1496; //SQLSTATE: HY000 Message: Partitioning can not be used stand-alone in query + public final static int ER_MIX_HANDLER_ERROR = 1497; //SQLSTATE: HY000 Message: The mix of handlers in the partitions is not allowed in this version of MySQL + public final static int ER_PARTITION_NOT_DEFINED_ERROR = 1498; //SQLSTATE: HY000 Message: For the partitioned engine it is necessary to define all %s + public final static int ER_TOO_MANY_PARTITIONS_ERROR = 1499; //SQLSTATE: HY000 Message: Too many partitions (including subpartitions) were defined + public final static int ER_SUBPARTITION_ERROR = 1500; //SQLSTATE: HY000 Message: It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning + public final static int ER_CANT_CREATE_HANDLER_FILE = 1501; //SQLSTATE: HY000 Message: Failed to create specific handler file + public final static int ER_BLOB_FIELD_IN_PART_FUNC_ERROR = 1502; //SQLSTATE: HY000 Message: A BLOB field is not allowed in partition function + public final static int ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF = 1503; //SQLSTATE: HY000 Message: A %s must include all columns in the table's partitioning function + public final static int ER_NO_PARTS_ERROR = 1504; //SQLSTATE: HY000 Message: Number of %s = 0 is not an allowed value + public final static int ER_PARTITION_MGMT_ON_NONPARTITIONED = 1505; //SQLSTATE: HY000 Message: Partition management on a not partitioned table is not possible + public final static int ER_FOREIGN_KEY_ON_PARTITIONED = 1506; //SQLSTATE: HY000 Message: Foreign key clause is not yet supported in conjunction with partitioning + public final static int ER_DROP_PARTITION_NON_EXISTENT = 1507; //SQLSTATE: HY000 Message: Error in list of partitions to %s + public final static int ER_DROP_LAST_PARTITION = 1508; //SQLSTATE: HY000 Message: Cannot remove all partitions, use DROP TABLE instead + public final static int ER_COALESCE_ONLY_ON_HASH_PARTITION = 1509; //SQLSTATE: HY000 Message: COALESCE PARTITION can only be used on HASH/KEY partitions + public final static int ER_REORG_HASH_ONLY_ON_SAME_NO = 1510; //SQLSTATE: HY000 Message: REORGANIZE PARTITION can only be used to reorganize partitions not to change their numbers + public final static int ER_REORG_NO_PARAM_ERROR = 1511; //SQLSTATE: HY000 Message: REORGANIZE PARTITION without parameters can only be used on auto-partitioned tables using HASH PARTITIONs + public final static int ER_ONLY_ON_RANGE_LIST_PARTITION = 1512; //SQLSTATE: HY000 Message: %s PARTITION can only be used on RANGE/LIST partitions + public final static int ER_ADD_PARTITION_SUBPART_ERROR = 1513; //SQLSTATE: HY000 Message: Trying to Add partition(s) with wrong number of subpartitions + public final static int ER_ADD_PARTITION_NO_NEW_PARTITION = 1514; //SQLSTATE: HY000 Message: At least one partition must be added + public final static int ER_COALESCE_PARTITION_NO_PARTITION = 1515; //SQLSTATE: HY000 Message: At least one partition must be coalesced + public final static int ER_REORG_PARTITION_NOT_EXIST = 1516; //SQLSTATE: HY000 Message: More partitions to reorganize than there are partitions + public final static int ER_SAME_NAME_PARTITION = 1517; //SQLSTATE: HY000 Message: Duplicate partition name %s + public final static int ER_NO_BINLOG_ERROR = 1518; //SQLSTATE: HY000 Message: It is not allowed to shut off binlog on this command + public final static int ER_CONSECUTIVE_REORG_PARTITIONS = 1519; //SQLSTATE: HY000 Message: When reorganizing a set of partitions they must be in consecutive order + public final static int ER_REORG_OUTSIDE_RANGE = 1520; //SQLSTATE: HY000 Message: Reorganize of range partitions cannot change total ranges except for last partition where it can extend the range + public final static int ER_PARTITION_FUNCTION_FAILURE = 1521; //SQLSTATE: HY000 Message: Partition function not supported in this version for this handler + public final static int ER_PART_STATE_ERROR = 1522; //SQLSTATE: HY000 Message: Partition state cannot be defined from CREATE/ALTER TABLE + public final static int ER_LIMITED_PART_RANGE = 1523; //SQLSTATE: HY000 Message: The %s handler only supports 32 bit integers in VALUES + public final static int ER_PLUGIN_IS_NOT_LOADED = 1524; //SQLSTATE: HY000 Message: Plugin '%s' is not loaded + public final static int ER_WRONG_VALUE = 1525; //SQLSTATE: HY000 Message: Incorrect %s value: '%s' + public final static int ER_NO_PARTITION_FOR_GIVEN_VALUE = 1526; //SQLSTATE: HY000 Message: Table has no partition for value %s + public final static int ER_FILEGROUP_OPTION_ONLY_ONCE = 1527; //SQLSTATE: HY000 Message: It is not allowed to specify %s more than once + public final static int ER_CREATE_FILEGROUP_FAILED = 1528; //SQLSTATE: HY000 Message: Failed to create %s + public final static int ER_DROP_FILEGROUP_FAILED = 1529; //SQLSTATE: HY000 Message: Failed to drop %s + public final static int ER_TABLESPACE_AUTO_EXTEND_ERROR = 1530; //SQLSTATE: HY000 Message: The handler doesn't support autoextend of tablespaces + public final static int ER_WRONG_SIZE_NUMBER = 1531; //SQLSTATE: HY000 Message: A size parameter was incorrectly specified, either number or on the form 10M + public final static int ER_SIZE_OVERFLOW_ERROR = 1532; //SQLSTATE: HY000 Message: The size number was correct but we don't allow the digit part to be more than 2 billion + public final static int ER_ALTER_FILEGROUP_FAILED = 1533; //SQLSTATE: HY000 Message: Failed to alter: %s + public final static int ER_BINLOG_ROW_LOGGING_FAILED = 1534; //SQLSTATE: HY000 Message: Writing one row to the row-based binary log failed + public final static int ER_BINLOG_ROW_WRONG_TABLE_DEF = 1535; //SQLSTATE: HY000 Message: Table definition on master and slave does not match: %s + public final static int ER_BINLOG_ROW_RBR_TO_SBR = 1536; //SQLSTATE: HY000 Message: Slave running with --log-slave-updates must use row-based binary logging to be able to replicate row-based binary log events + public final static int ER_EVENT_ALREADY_EXISTS = 1537; //SQLSTATE: HY000 Message: Event '%s' already exists + public final static int ER_EVENT_STORE_FAILED = 1538; //SQLSTATE: HY000 Message: Failed to store event %s. Error code %d from storage engine. + public final static int ER_EVENT_DOES_NOT_EXIST = 1539; //SQLSTATE: HY000 Message: Unknown event '%s' + public final static int ER_EVENT_CANT_ALTER = 1540; //SQLSTATE: HY000 Message: Failed to alter event '%s' + public final static int ER_EVENT_DROP_FAILED = 1541; //SQLSTATE: HY000 Message: Failed to drop %s + public final static int ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG = 1542; //SQLSTATE: HY000 Message: INTERVAL is either not positive or too big + public final static int ER_EVENT_ENDS_BEFORE_STARTS = 1543; //SQLSTATE: HY000 Message: ENDS is either invalid or before STARTS + public final static int ER_EVENT_EXEC_TIME_IN_THE_PAST = 1544; //SQLSTATE: HY000 Message: Event execution time is in the past. Event has been disabled + public final static int ER_EVENT_OPEN_TABLE_FAILED = 1545; //SQLSTATE: HY000 Message: Failed to open mysql.event + public final static int ER_EVENT_NEITHER_M_EXPR_NOR_M_AT = 1546; //SQLSTATE: HY000 Message: No datetime expression provided + public final static int ER_COL_COUNT_DOESNT_MATCH_CORRUPTED = 1547; //SQLSTATE: HY000 Message: Column count of mysql.%s is wrong. Expected %d, found %d. The table is probably corrupted + public final static int ER_CANNOT_LOAD_FROM_TABLE = 1548; //SQLSTATE: HY000 Message: Cannot load from mysql.%s. The table is probably corrupted + public final static int ER_EVENT_CANNOT_DELETE = 1549; //SQLSTATE: HY000 Message: Failed to delete the event from mysql.event + public final static int ER_EVENT_COMPILE_ERROR = 1550; //SQLSTATE: HY000 Message: Error during compilation of event's body + public final static int ER_EVENT_SAME_NAME = 1551; //SQLSTATE: HY000 Message: Same old and new event name + public final static int ER_EVENT_DATA_TOO_LONG = 1552; //SQLSTATE: HY000 Message: Data for column '%s' too long + public final static int ER_DROP_INDEX_FK = 1553; //SQLSTATE: HY000 Message: Cannot drop index '%s': needed in a foreign key constraint + public final static int ER_WARN_DEPRECATED_SYNTAX_WITH_VER = 1554; //SQLSTATE: HY000 Message: The syntax '%s' is deprecated and will be removed in MySQL %s. Please use %s instead + public final static int ER_CANT_WRITE_LOCK_LOG_TABLE = 1555; //SQLSTATE: HY000 Message: You can't write-lock a log table. Only read access is possible + public final static int ER_CANT_LOCK_LOG_TABLE = 1556; //SQLSTATE: HY000 Message: You can't use locks with log tables. + public final static int ER_FOREIGN_DUPLICATE_KEY = 1557; //SQLSTATE: 23000 Message: Upholding foreign key constraints for table '%s', entry '%s', key %d would lead to a duplicate entry + public final static int ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE = 1558; //SQLSTATE: HY000 Message: Column count of mysql.%s is wrong. Expected %d, found %d. Created with MySQL %d, now running %d. Please use mysql_upgrade to fix this error. + public final static int ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR = 1559; //SQLSTATE: HY000 Message: Cannot switch out of the row-based binary log format when the session has open temporary tables + public final static int ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1560; //SQLSTATE: HY000 Message: Cannot change the binary logging format inside a stored function or trigger + public final static int ER_NDB_CANT_SWITCH_BINLOG_FORMAT = 1561; //SQLSTATE: HY000 Message: The NDB cluster engine does not support changing the binlog format on the fly yet + public final static int ER_PARTITION_NO_TEMPORARY = 1562; //SQLSTATE: HY000 Message: Cannot create temporary table with partitions + public final static int ER_PARTITION_CONST_DOMAIN_ERROR = 1563; //SQLSTATE: HY000 Message: Partition constant is out of partition function domain + public final static int ER_PARTITION_FUNCTION_IS_NOT_ALLOWED = 1564; //SQLSTATE: HY000 Message: This partition function is not allowed + public final static int ER_DDL_LOG_ERROR = 1565; //SQLSTATE: HY000 Message: Error in DDL log + public final static int ER_NULL_IN_VALUES_LESS_THAN = 1566; //SQLSTATE: HY000 Message: Not allowed to use NULL value in VALUES LESS THAN + public final static int ER_WRONG_PARTITION_NAME = 1567; //SQLSTATE: HY000 Message: Incorrect partition name + public final static int ER_CANT_CHANGE_TX_ISOLATION = 1568; //SQLSTATE: 25001 Message: Transaction isolation level can't be changed while a transaction is in progress + public final static int ER_DUP_ENTRY_AUTOINCREMENT_CASE = 1569; //SQLSTATE: HY000 Message: ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '%s' for key '%s' + public final static int ER_EVENT_MODIFY_QUEUE_ERROR = 1570; //SQLSTATE: HY000 Message: Internal scheduler error %d + public final static int ER_EVENT_SET_VAR_ERROR = 1571; //SQLSTATE: HY000 Message: Error during starting/stopping of the scheduler. Error code %u + public final static int ER_PARTITION_MERGE_ERROR = 1572; //SQLSTATE: HY000 Message: Engine cannot be used in partitioned tables + public final static int ER_CANT_ACTIVATE_LOG = 1573; //SQLSTATE: HY000 Message: Cannot activate '%s' log + public final static int ER_RBR_NOT_AVAILABLE = 1574; //SQLSTATE: HY000 Message: The server was not built with row-based replication + public final static int ER_BASE64_DECODE_ERROR = 1575; //SQLSTATE: HY000 Message: Decoding of base64 string failed + public final static int ER_EVENT_RECURSION_FORBIDDEN = 1576; //SQLSTATE: HY000 Message: Recursion of EVENT DDL statements is forbidden when body is present + public final static int ER_EVENTS_DB_ERROR = 1577; //SQLSTATE: HY000 Message: Cannot proceed because system tables used by Event Scheduler were found damaged at server start + public final static int ER_ONLY_INTEGERS_ALLOWED = 1578; //SQLSTATE: HY000 Message: Only integers allowed as number here + public final static int ER_UNSUPORTED_LOG_ENGINE = 1579; //SQLSTATE: HY000 Message: This storage engine cannot be used for log tables" + public final static int ER_BAD_LOG_STATEMENT = 1580; //SQLSTATE: HY000 Message: You cannot '%s' a log table if logging is enabled + public final static int ER_CANT_RENAME_LOG_TABLE = 1581; //SQLSTATE: HY000 Message: Cannot rename '%s'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to '%s' + public final static int ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT = 1582; //SQLSTATE: 42000 Message: Incorrect parameter count in the call to native function '%s' + public final static int ER_WRONG_PARAMETERS_TO_NATIVE_FCT = 1583; //SQLSTATE: 42000 Message: Incorrect parameters in the call to native function '%s' + public final static int ER_WRONG_PARAMETERS_TO_STORED_FCT = 1584; //SQLSTATE: 42000 Message: Incorrect parameters in the call to stored function '%s' + public final static int ER_NATIVE_FCT_NAME_COLLISION = 1585; //SQLSTATE: HY000 Message: This function '%s' has the same name as a native function + public final static int ER_DUP_ENTRY_WITH_KEY_NAME = 1586; //SQLSTATE: 23000 Message: Duplicate entry '%s' for key '%s' + public final static int ER_BINLOG_PURGE_EMFILE = 1587; //SQLSTATE: HY000 Message: Too many files opened, please execute the command again + public final static int ER_EVENT_CANNOT_CREATE_IN_THE_PAST = 1588; //SQLSTATE: HY000 Message: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation. + public final static int ER_EVENT_CANNOT_ALTER_IN_THE_PAST = 1589; //SQLSTATE: HY000 Message: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation. + public final static int ER_SLAVE_INCIDENT = 1590; //SQLSTATE: HY000 Message: The incident %s occured on the master. Message: %s + public final static int ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT = 1591; //SQLSTATE: HY000 Message: Table has no partition for some existing values + public final static int ER_BINLOG_UNSAFE_STATEMENT = 1592; //SQLSTATE: HY000 Message: Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. %s + public final static int ER_SLAVE_FATAL_ERROR = 1593; //SQLSTATE: HY000 Message: Fatal error: %s + public final static int ER_SLAVE_RELAY_LOG_READ_FAILURE = 1594; //SQLSTATE: HY000 Message: Relay log read failure: %s + public final static int ER_SLAVE_RELAY_LOG_WRITE_FAILURE = 1595; //SQLSTATE: HY000 Message: Relay log write failure: %s + public final static int ER_SLAVE_CREATE_EVENT_FAILURE = 1596; //SQLSTATE: HY000 Message: Failed to create %s + public final static int ER_SLAVE_MASTER_COM_FAILURE = 1597; //SQLSTATE: HY000 Message: Master command %s failed: %s + public final static int ER_BINLOG_LOGGING_IMPOSSIBLE = 1598; //SQLSTATE: HY000 Message: Binary logging not possible. Message: %s + public final static int ER_VIEW_NO_CREATION_CTX = 1599; //SQLSTATE: HY000 Message: View `%s`.`%s` has no creation context + public final static int ER_VIEW_INVALID_CREATION_CTX = 1600; //SQLSTATE: HY000 Message: Creation context of view `%s`.`%s' is invalid + public final static int ER_SR_INVALID_CREATION_CTX = 1601; //SQLSTATE: HY000 Message: Creation context of stored routine `%s`.`%s` is invalid + public final static int ER_TRG_CORRUPTED_FILE = 1602; //SQLSTATE: HY000 Message: Corrupted TRG file for table `%s`.`%s` + public final static int ER_TRG_NO_CREATION_CTX = 1603; //SQLSTATE: HY000 Message: Triggers for table `%s`.`%s` have no creation context + public final static int ER_TRG_INVALID_CREATION_CTX = 1604; //SQLSTATE: HY000 Message: Trigger creation context of table `%s`.`%s` is invalid + public final static int ER_EVENT_INVALID_CREATION_CTX = 1605; //SQLSTATE: HY000 Message: Creation context of event `%s`.`%s` is invalid + public final static int ER_TRG_CANT_OPEN_TABLE = 1606; //SQLSTATE: HY000 Message: Cannot open table for trigger `%s`.`%s` + public final static int ER_CANT_CREATE_SROUTINE = 1607; //SQLSTATE: HY000 Message: Cannot create stored routine `%s`. Check warnings + public final static int ER_NEVER_USED = 1608; //SQLSTATE: HY000 Message: Ambiguous slave modes combination. %s + public final static int ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT = 1609; //SQLSTATE: HY000 Message: The BINLOG statement of type `%s` was not preceded by a format description BINLOG statement. + public final static int ER_SLAVE_CORRUPT_EVENT = 1610; //SQLSTATE: HY000 Message: Corrupted replication event was detected + public final static int ER_LOAD_DATA_INVALID_COLUMN = 1611; //SQLSTATE: HY000 Message: Invalid column reference (%s) in LOAD DATA + public final static int ER_LOG_PURGE_NO_FILE = 1612; //SQLSTATE: HY000 Message: Being purged log %s was not found + public final static int ER_XA_RBTIMEOUT = 1613; //SQLSTATE: XA106 Message: XA_RBTIMEOUT: Transaction branch was rolled back: took too long + public final static int ER_XA_RBDEADLOCK = 1614; //SQLSTATE: XA102 Message: XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected + public final static int ER_NEED_REPREPARE = 1615; //SQLSTATE: HY000 Message: Prepared statement needs to be re-prepared + public final static int ER_DELAYED_NOT_SUPPORTED = 1616; //SQLSTATE: HY000 Message: DELAYED option not supported for table '%s' + public final static int WARN_NO_MASTER_INFO = 1617; //SQLSTATE: HY000 Message: The master info structure does not exist + public final static int WARN_OPTION_IGNORED = 1618; //SQLSTATE: HY000 Message: <%s> option ignored + public final static int WARN_PLUGIN_DELETE_BUILTIN = 1619; //SQLSTATE: HY000 Message: Built-in plugins cannot be deleted + public final static int WARN_PLUGIN_BUSY = 1620; //SQLSTATE: HY000 Message: Plugin is busy and will be uninstalled on shutdown + public final static int ER_VARIABLE_IS_READONLY = 1621; //SQLSTATE: HY000 Message: %s variable '%s' is read-only. Use SET %s to assign the value + public final static int ER_WARN_ENGINE_TRANSACTION_ROLLBACK = 1622; //SQLSTATE: HY000 Message: Storage engine %s does not support rollback for this statement. Transaction rolled back and must be restarted + public final static int ER_SLAVE_HEARTBEAT_FAILURE = 1623; //SQLSTATE: HY000 Message: Unexpected master's heartbeat data: %s + public final static int ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE = 1624; //SQLSTATE: HY000 Message: The requested value for the heartbeat period is either negative or exceeds the maximum allowed (%s seconds). + public final static int ER_NDB_REPLICATION_SCHEMA_ERROR = 1625; //SQLSTATE: HY000 Message: Bad schema for mysql.ndb_replication table. Message: %s + public final static int ER_CONFLICT_FN_PARSE_ERROR = 1626; //SQLSTATE: HY000 Message: Error in parsing conflict function. Message: %s + public final static int ER_EXCEPTIONS_WRITE_ERROR = 1627; //SQLSTATE: HY000 Message: Write to exceptions table failed. Message: %s" + public final static int ER_TOO_LONG_TABLE_COMMENT = 1628; //SQLSTATE: HY000 Message: Comment for table '%s' is too long (max = %lu) + public final static int ER_TOO_LONG_FIELD_COMMENT = 1629; //SQLSTATE: HY000 Message: Comment for field '%s' is too long (max = %lu) + public final static int ER_FUNC_INEXISTENT_NAME_COLLISION = 1630; //SQLSTATE: 42000 Message: FUNCTION %s does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual + public final static int ER_DATABASE_NAME = 1631; //SQLSTATE: HY000 Message: Database + public final static int ER_TABLE_NAME = 1632; //SQLSTATE: HY000 Message: Table + public final static int ER_PARTITION_NAME = 1633; //SQLSTATE: HY000 Message: Partition + public final static int ER_SUBPARTITION_NAME = 1634; //SQLSTATE: HY000 Message: Subpartition + public final static int ER_TEMPORARY_NAME = 1635; //SQLSTATE: HY000 Message: Temporary + public final static int ER_RENAMED_NAME = 1636; //SQLSTATE: HY000 Message: Renamed + public final static int ER_TOO_MANY_CONCURRENT_TRXS = 1637; //SQLSTATE: HY000 Message: Too many active concurrent transactions + public final static int WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED = 1638; //SQLSTATE: HY000 Message: Non-ASCII separator arguments are not fully supported + public final static int ER_DEBUG_SYNC_TIMEOUT = 1639; //SQLSTATE: HY000 Message: debug sync point wait timed out + public final static int ER_DEBUG_SYNC_HIT_LIMIT = 1640; //SQLSTATE: HY000 Message: debug sync point hit limit reached + public final static int ER_DUP_SIGNAL_SET = 1641; //SQLSTATE: 42000 Message: Duplicate condition information item '%s' + public final static int ER_SIGNAL_WARN = 1642; //SQLSTATE: 01000 Message: Unhandled user-defined warning condition + public final static int ER_SIGNAL_NOT_FOUND = 1643; //SQLSTATE: 02000 Message: Unhandled user-defined not found condition + public final static int ER_SIGNAL_EXCEPTION = 1644; //SQLSTATE: HY000 Message: Unhandled user-defined exception condition + public final static int ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER = 1645; //SQLSTATE: 0K000 Message: RESIGNAL when handler not active + public final static int ER_SIGNAL_BAD_CONDITION_TYPE = 1646; //SQLSTATE: HY000 Message: SIGNAL/RESIGNAL can only use a CONDITION defined with; //SQLSTATE + public final static int WARN_COND_ITEM_TRUNCATED = 1647; //SQLSTATE: HY000 Message: Data truncated for condition item '%s' + public final static int ER_COND_ITEM_TOO_LONG = 1648; //SQLSTATE: HY000 Message: Data too long for condition item '%s' + public final static int ER_UNKNOWN_LOCALE = 1649; //SQLSTATE: HY000 Message: Unknown locale: '%s' + public final static int ER_SLAVE_IGNORE_SERVER_IDS = 1650; //SQLSTATE: HY000 Message: The requested server id %d clashes with the slave startup option --replicate-same-server-id + public final static int ER_QUERY_CACHE_DISABLED = 1651; //SQLSTATE: HY000 Message: Query cache is disabled; restart the server with query_cache_type=1 to enable it + public final static int ER_SAME_NAME_PARTITION_FIELD = 1652; //SQLSTATE: HY000 Message: Duplicate partition field name '%s' + public final static int ER_PARTITION_COLUMN_LIST_ERROR = 1653; //SQLSTATE: HY000 Message: Inconsistency in usage of column lists for partitioning + public final static int ER_WRONG_TYPE_COLUMN_VALUE_ERROR = 1654; //SQLSTATE: HY000 Message: Partition column values of incorrect type + public final static int ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR = 1655; //SQLSTATE: HY000 Message: Too many fields in '%s' + public final static int ER_MAXVALUE_IN_VALUES_IN = 1656; //SQLSTATE: HY000 Message: Cannot use MAXVALUE as value in VALUES IN + public final static int ER_TOO_MANY_VALUES_ERROR = 1657; //SQLSTATE: HY000 Message: Cannot have more than one value for this type of %s partitioning + public final static int ER_ROW_SINGLE_PARTITION_FIELD_ERROR = 1658; //SQLSTATE: HY000 Message: Row expressions in VALUES IN only allowed for multi-field column partitioning + public final static int ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD = 1659; //SQLSTATE: HY000 Message: Field '%s' is of a not allowed type for this type of partitioning + public final static int ER_PARTITION_FIELDS_TOO_LONG = 1660; //SQLSTATE: HY000 Message: The total length of the partitioning fields is too large + public final static int ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE = 1661; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since both row-incapable engines and statement-incapable engines are involved. + public final static int ER_BINLOG_ROW_MODE_AND_STMT_ENGINE = 1662; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = ROW and at least one table uses a storage engine limited to statement-based logging. + public final static int ER_BINLOG_UNSAFE_AND_STMT_ENGINE = 1663; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since statement is unsafe, storage engine is limited to statement-based logging, and BINLOG_FORMAT = MIXED. %s + public final static int ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE = 1664; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since statement is in row format and at least one table uses a storage engine limited to statement-based logging. + public final static int ER_BINLOG_STMT_MODE_AND_ROW_ENGINE = 1665; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging.%s + public final static int ER_BINLOG_ROW_INJECTION_AND_STMT_MODE = 1666; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since statement is in row format and BINLOG_FORMAT = STATEMENT. + public final static int ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1667; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since more than one engine is involved and at least one engine is self-logging. + public final static int ER_BINLOG_UNSAFE_LIMIT = 1668; //SQLSTATE: HY000 Message: The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted. + public final static int ER_BINLOG_UNSAFE_INSERT_DELAYED = 1669; //SQLSTATE: HY000 Message: The statement is unsafe because it uses INSERT DELAYED. This is unsafe because the times when rows are inserted cannot be predicted. + public final static int ER_BINLOG_UNSAFE_SYSTEM_TABLE = 1670; //SQLSTATE: HY000 Message: The statement is unsafe because it uses the general log, slow query log, or performance_schema table(s). This is unsafe because system tables may differ on slaves. + public final static int ER_BINLOG_UNSAFE_AUTOINC_COLUMNS = 1671; //SQLSTATE: HY000 Message: Statement is unsafe because it invokes a trigger or a stored function that inserts into an AUTO_INCREMENT column. Inserted values cannot be logged correctly. + public final static int ER_BINLOG_UNSAFE_UDF = 1672; //SQLSTATE: HY000 Message: Statement is unsafe because it uses a UDF which may not return the same value on the slave. + public final static int ER_BINLOG_UNSAFE_SYSTEM_VARIABLE = 1673; //SQLSTATE: HY000 Message: Statement is unsafe because it uses a system variable that may have a different value on the slave. + public final static int ER_BINLOG_UNSAFE_SYSTEM_FUNCTION = 1674; //SQLSTATE: HY000 Message: Statement is unsafe because it uses a system function that may return a different value on the slave. + public final static int ER_BINLOG_UNSAFE_NONTRANS_AFTER_TRANS = 1675; //SQLSTATE: HY000 Message: Statement is unsafe because it accesses a non-transactional table after accessing a transactional table within the same transaction. + public final static int ER_MESSAGE_AND_STATEMENT = 1676; //SQLSTATE: HY000 Message: %s Statement: %s + public final static int ER_SLAVE_CONVERSION_FAILED = 1677; //SQLSTATE: HY000 Message: Column %d of table '%s.%s' cannot be converted from type '%s' to type '%s' + public final static int ER_SLAVE_CANT_CREATE_CONVERSION = 1678; //SQLSTATE: HY000 Message: Can't create conversion table for table '%s.%s' + public final static int ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1679; //SQLSTATE: HY000 Message: Cannot modify @@session.binlog_format inside a transaction + public final static int ER_PATH_LENGTH = 1680; //SQLSTATE: HY000 Message: The path specified for %s is too long. + public final static int ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT = 1681; //SQLSTATE: HY000 Message: The syntax '%s' is deprecated and will be removed in MySQL %s. + public final static int ER_WRONG_NATIVE_TABLE_STRUCTURE = 1682; //SQLSTATE: HY000 Message: Native table '%s'.'%s' has the wrong structure + public final static int ER_WRONG_PERFSCHEMA_USAGE = 1683; //SQLSTATE: HY000 Message: Invalid performance_schema usage. + public final static int ER_WARN_I_S_SKIPPED_TABLE = 1684; //SQLSTATE: HY000 Message: Table '%s'.'%s' was skipped since its definition is being modified by concurrent DDL statement + public final static int ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1685; //SQLSTATE: HY000 Message: Cannot modify @@session.binlog_direct_non_transactional_updates inside a transaction + public final static int ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1686; //SQLSTATE: HY000 Message: Cannot change the binlog direct flag inside a stored function or trigger + public final static int ER_SPATIAL_MUST_HAVE_GEOM_COL = 1687; //SQLSTATE: 42000 Message: A SPATIAL index may only contain a geometrical type column + public final static int ER_TOO_LONG_INDEX_COMMENT = 1688; //SQLSTATE: HY000 Message: Comment for index '%s' is too long (max = %lu) + public final static int ER_LOCK_ABORTED = 1689; //SQLSTATE: HY000 Message: Wait on a lock was aborted due to a pending exclusive lock + public final static int ER_DATA_OUT_OF_RANGE = 1690; //SQLSTATE: 22003 Message: %s value is out of range in '%s' + public final static int ER_WRONG_SPVAR_TYPE_IN_LIMIT = 1691; //SQLSTATE: HY000 Message: A variable of a non-integer type in LIMIT clause + public final static int ER_BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1692; //SQLSTATE: HY000 Message: Mixing self-logging and non-self-logging engines in a statement is unsafe. + public final static int ER_BINLOG_UNSAFE_MIXED_STATEMENT = 1693; //SQLSTATE: HY000 Message: Statement accesses nontransactional table as well as transactional or temporary table, and writes to any of them. + public final static int ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1694; //SQLSTATE: HY000 Message: Cannot modify @@session.sql_log_bin inside a transaction + public final static int ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1695; //SQLSTATE: HY000 Message: Cannot change the sql_log_bin inside a stored function or trigger + public final static int ER_FAILED_READ_FROM_PAR_FILE = 1696; //SQLSTATE: HY000 Message: Failed to read from the .par file + public final static int ER_VALUES_IS_NOT_INT_TYPE_ERROR = 1697; //SQLSTATE: HY000 Message: VALUES value for partition '%s' must have type INT + public final static int ER_ACCESS_DENIED_NO_PASSWORD_ERROR = 1698; //SQLSTATE: 28000 Message: Access denied for user '%s'@'%s' + public final static int ER_SET_PASSWORD_AUTH_PLUGIN = 1699; //SQLSTATE: HY000 Message: SET PASSWORD has no significance for users authenticating via plugins + public final static int ER_GRANT_PLUGIN_USER_EXISTS = 1700; //SQLSTATE: HY000 Message: GRANT with IDENTIFIED WITH is illegal because the user %-.*s already exists + public final static int ER_TRUNCATE_ILLEGAL_FK = 1701; //SQLSTATE: 42000 Message: Cannot truncate a table referenced in a foreign key constraint (%s) + public final static int ER_PLUGIN_IS_PERMANENT = 1702; //SQLSTATE: HY000 Message: Plugin '%s' is force_plus_permanent and can not be unloaded + public final static int ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN = 1703; //SQLSTATE: HY000 Message: The requested value for the heartbeat period is less than 1 millisecond. The value is reset to 0, meaning that heartbeating will effectively be disabled. + public final static int ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX = 1704; //SQLSTATE: HY000 Message: The requested value for the heartbeat period exceeds the value of `slave_net_timeout' seconds. A sensible value for the period should be less than the timeout. + public final static int ER_STMT_CACHE_FULL = 1705; //SQLSTATE: HY000 Message: Multi-row statements required more than 'max_binlog_stmt_cache_size' bytes of storage; increase this mysqld variable and try again + + public final static int ER_MULTI_UPDATE_KEY_CONFLICT = 1706; //SQLSTATE: HY000 Message: Primary key/partition key update is not allowed since the table is updated both as '%s' and '%s'. + public final static int ER_TABLE_NEEDS_REBUILD = 1707; //SQLSTATE: HY000 Message: Table rebuild required. Please do "ALTER TABLE `%s` FORCE" or dump/reload to fix it! + public final static int WARN_OPTION_BELOW_LIMIT = 1708; //SQLSTATE: HY000 Message: The value of '%s' should be no less than the value of '%s' + public final static int ER_INDEX_COLUMN_TOO_LONG = 1709; //SQLSTATE: HY000 Message: Index column size too large. The maximum column size is %lu bytes. + public final static int ER_ERROR_IN_TRIGGER_BODY = 1710; //SQLSTATE: HY000 Message: Trigger '%s' has an error in its body: '%s' + public final static int ER_ERROR_IN_UNKNOWN_TRIGGER_BODY = 1711; //SQLSTATE: HY000 Message: Unknown trigger has an error in its body: '%s' + public final static int ER_INDEX_CORRUPT = 1712; //SQLSTATE: HY000 Message: Index %s is corrupted + public final static int ER_UNDO_RECORD_TOO_BIG = 1713; //SQLSTATE: HY000 Message: Undo log record is too big. + public final static int ER_BINLOG_UNSAFE_INSERT_IGNORE_SELECT = 1714; //SQLSTATE: HY000 Message: INSERT IGNORE... SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_INSERT_SELECT_UPDATE = 1715; //SQLSTATE: HY000 Message: INSERT... SELECT... ON DUPLICATE KEY UPDATE is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are updated. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_REPLACE_SELECT = 1716; //SQLSTATE: HY000 Message: REPLACE... SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are replaced. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_CREATE_IGNORE_SELECT = 1717; //SQLSTATE: HY000 Message: CREATE... IGNORE SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT = 1718; //SQLSTATE: HY000 Message: CREATE... REPLACE SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are replaced. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_UPDATE_IGNORE = 1719; //SQLSTATE: HY000 Message: UPDATE IGNORE is unsafe because the order in which rows are updated determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave. + public final static int ER_PLUGIN_NO_UNINSTALL = 1720; //SQLSTATE: HY000 Message: Plugin '%s' is marked as not dynamically uninstallable. You have to stop the server to uninstall it. + public final static int ER_PLUGIN_NO_INSTALL = 1721; //SQLSTATE: HY000 Message: Plugin '%s' is marked as not dynamically installable. You have to stop the server to install it. + public final static int ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT = 1722; //SQLSTATE: HY000 Message: Statements writing to a table with an auto-increment column after selecting from another table are unsafe because the order in which rows are retrieved determines what (if any) rows will be written. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC = 1723; //SQLSTATE: HY000 Message: CREATE TABLE... SELECT... on a table with an auto-increment column is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are inserted. This order cannot be predicted and may differ on master and the slave. + public final static int ER_BINLOG_UNSAFE_INSERT_TWO_KEYS = 1724; //SQLSTATE: HY000 Message: INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe + public final static int ER_TABLE_IN_FK_CHECK = 1725; //SQLSTATE: HY000 Message: Table is being used in foreign key check. + public final static int ER_UNSUPPORTED_ENGINE = 1726; //SQLSTATE: HY000 Message: Storage engine '%s' does not support system tables. [%s.%s] + public final static int ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST = 1727; //SQLSTATE: HY000 Message: INSERT into autoincrement field which is not the first part in the composed primary key is unsafe. + public final static int ER_CANNOT_LOAD_FROM_TABLE_V2 = 1728; //SQLSTATE: HY000 Message: Cannot load from %s.%s. The table is probably corrupted + public final static int ER_MASTER_DELAY_VALUE_OUT_OF_RANGE = 1729; //SQLSTATE: HY000 Message: The requested value %u for the master delay exceeds the maximum %u + public final static int ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT = 1730; //SQLSTATE: HY000 Message: Only Format_description_log_event and row events are allowed in BINLOG statements (but %s was provided) + public final static int ER_PARTITION_EXCHANGE_DIFFERENT_OPTION = 1731; //SQLSTATE: HY000 Message: Non matching attribute '%s' between partition and table + public final static int ER_PARTITION_EXCHANGE_PART_TABLE = 1732; //SQLSTATE: HY000 Message: Table to exchange with partition is partitioned: '%s' + public final static int ER_PARTITION_EXCHANGE_TEMP_TABLE = 1733; //SQLSTATE: HY000 Message: Table to exchange with partition is temporary: '%s' + public final static int ER_PARTITION_INSTEAD_OF_SUBPARTITION = 1734; //SQLSTATE: HY000 Message: Subpartitioned table, use subpartition instead of partition + public final static int ER_UNKNOWN_PARTITION = 1735; //SQLSTATE: HY000 Message: Unknown partition '%s' in table '%s' + public final static int ER_TABLES_DIFFERENT_METADATA = 1736; //SQLSTATE: HY000 Message: Tables have different definitions + public final static int ER_ROW_DOES_NOT_MATCH_PARTITION = 1737; //SQLSTATE: HY000 Message: Found a row that does not match the partition + public final static int ER_BINLOG_CACHE_SIZE_GREATER_THAN_MAX = 1738; //SQLSTATE: HY000 Message: Option binlog_cache_size (%lu) is greater than max_binlog_cache_size (%lu); setting binlog_cache_size equal to max_binlog_cache_size. + public final static int ER_WARN_INDEX_NOT_APPLICABLE = 1739; //SQLSTATE: HY000 Message: Cannot use %s access on index '%s' due to type or collation conversion on field '%s' + public final static int ER_PARTITION_EXCHANGE_FOREIGN_KEY = 1740; //SQLSTATE: HY000 Message: Table to exchange with partition has foreign key references: '%s' + public final static int ER_NO_SUCH_KEY_VALUE = 1741; //SQLSTATE: HY000 Message: Key value '%s' was not found in table '%s.%s' + public final static int ER_RPL_INFO_DATA_TOO_LONG = 1742; //SQLSTATE: HY000 Message: Data for column '%s' too long + public final static int ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE = 1743; //SQLSTATE: HY000 Message: Replication event checksum verification failed while reading from network. + public final static int ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE = 1744; //SQLSTATE: HY000 Message: Replication event checksum verification failed while reading from a log file. + public final static int ER_BINLOG_STMT_CACHE_SIZE_GREATER_THAN_MAX = 1745; //SQLSTATE: HY000 Message: Option binlog_stmt_cache_size (%lu) is greater than max_binlog_stmt_cache_size (%lu); setting binlog_stmt_cache_size equal to max_binlog_stmt_cache_size. + public final static int ER_CANT_UPDATE_TABLE_IN_CREATE_TABLE_SELECT = 1746; //SQLSTATE: HY000 Message: Can't update table '%s' while '%s' is being created. + public final static int ER_PARTITION_CLAUSE_ON_NONPARTITIONED = 1747; //SQLSTATE: HY000 Message: PARTITION () clause on non partitioned table + public final static int ER_ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET = 1748; //SQLSTATE: HY000 Message: Found a row not matching the given partition set + public final static int ER_NO_SUCH_PARTITION__UNUSED = 1749; //SQLSTATE: HY000 Message: partition '%s' doesn't exist + public final static int ER_CHANGE_RPL_INFO_REPOSITORY_FAILURE = 1750; //SQLSTATE: HY000 Message: Failure while changing the type of replication repository: %s. + public final static int ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_CREATED_TEMP_TABLE = 1751; //SQLSTATE: HY000 Message: The creation of some temporary tables could not be rolled back. + public final static int ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_DROPPED_TEMP_TABLE = 1752; //SQLSTATE: HY000 Message: Some temporary tables were dropped, but these operations could not be rolled back. + public final static int ER_MTS_FEATURE_IS_NOT_SUPPORTED = 1753; //SQLSTATE: HY000 Message: %s is not supported in multi-threaded slave mode. %s + public final static int ER_MTS_UPDATED_DBS_GREATER_MAX = 1754; //SQLSTATE: HY000 Message: The number of modified databases exceeds the maximum %d; the database names will not be included in the replication event metadata. + public final static int ER_MTS_CANT_PARALLEL = 1755; //SQLSTATE: HY000 Message: Cannot execute the current event group in the parallel mode. Encountered event %s, relay-log name %s, position %s which prevents execution of this event group in parallel mode. Reason: %s. + public final static int ER_MTS_INCONSISTENT_DATA = 1756; //SQLSTATE: HY000 Message: %s + public final static int ER_FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING = 1757; //SQLSTATE: HY000 Message: FULLTEXT index is not supported for partitioned tables. + public final static int ER_DA_INVALID_CONDITION_NUMBER = 1758; //SQLSTATE: 35000 Message: Invalid condition number + public final static int ER_INSECURE_PLAIN_TEXT = 1759; //SQLSTATE: HY000 Message: Sending passwords in plain text without SSL/TLS is extremely insecure. + public final static int ER_INSECURE_CHANGE_MASTER = 1760; //SQLSTATE: HY000 Message: Storing MySQL user name or password information in the master.info repository is not secure and is therefore not recommended. Please see the MySQL Manual for more about this issue and possible alternatives. + public final static int ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO = 1761; //SQLSTATE: 23000 Message: Foreign key constraint for table '%s', record '%s' would lead to a duplicate entry in table '%s', key '%s' + public final static int ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO = 1762; //SQLSTATE: 23000 Message: Foreign key constraint for table '%s', record '%s' would lead to a duplicate entry in a child table + public final static int ER_SQLTHREAD_WITH_SECURE_SLAVE = 1763; //SQLSTATE: HY000 Message: Setting authentication options is not possible when only the Slave SQL Thread is being started. + public final static int ER_TABLE_HAS_NO_FT = 1764; //SQLSTATE: HY000 Message: The table does not have FULLTEXT index to support this query + public final static int ER_VARIABLE_NOT_SETTABLE_IN_SF_OR_TRIGGER = 1765; //SQLSTATE: HY000 Message: The system variable %s cannot be set in stored functions or triggers. + public final static int ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION = 1766; //SQLSTATE: HY000 Message: The system variable %s cannot be set when there is an ongoing transaction. + public final static int ER_GTID_NEXT_IS_NOT_IN_GTID_NEXT_LIST = 1767; //SQLSTATE: HY000 Message: The system variable @@SESSION.GTID_NEXT has the value %s, which is not listed in @@SESSION.GTID_NEXT_LIST. + public final static int ER_CANT_CHANGE_GTID_NEXT_IN_TRANSACTION_WHEN_GTID_NEXT_LIST_IS_NULL = 1768; //SQLSTATE: HY000 Message: When @@SESSION.GTID_NEXT_LIST == NULL, the system variable @@SESSION.GTID_NEXT cannot change inside a transaction. + public final static int ER_SET_STATEMENT_CANNOT_INVOKE_FUNCTION = 1769; //SQLSTATE: HY000 Message: The statement 'SET %s' cannot invoke a stored function. + public final static int ER_GTID_NEXT_CANT_BE_AUTOMATIC_IF_GTID_NEXT_LIST_IS_NON_NULL = 1770; //SQLSTATE: HY000 Message: The system variable @@SESSION.GTID_NEXT cannot be 'AUTOMATIC' when @@SESSION.GTID_NEXT_LIST is non-NULL. + public final static int ER_SKIPPING_LOGGED_TRANSACTION = 1771; //SQLSTATE: HY000 Message: Skipping transaction %s because it has already been executed and logged. + public final static int ER_MALFORMED_GTID_SET_SPECIFICATION = 1772; //SQLSTATE: HY000 Message: Malformed GTID set specification '%s'. + public final static int ER_MALFORMED_GTID_SET_ENCODING = 1773; //SQLSTATE: HY000 Message: Malformed GTID set encoding. + public final static int ER_MALFORMED_GTID_SPECIFICATION = 1774; //SQLSTATE: HY000 Message: Malformed GTID specification '%s'. + public final static int ER_GNO_EXHAUSTED = 1775; //SQLSTATE: HY000 Message: Impossible to generate Global Transaction Identifier: the integer component reached the maximal value. Restart the server with a new server_uuid. + public final static int ER_BAD_SLAVE_AUTO_POSITION = 1776; //SQLSTATE: HY000 Message: Parameters MASTER_LOG_FILE, MASTER_LOG_POS, RELAY_LOG_FILE and RELAY_LOG_POS cannot be set when MASTER_AUTO_POSITION is active. + public final static int ER_AUTO_POSITION_REQUIRES_GTID_MODE_ON = 1777; //SQLSTATE: HY000 Message: CHANGE MASTER TO MASTER_AUTO_POSITION = 1 can only be executed when GTID_MODE = ON. + public final static int ER_CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET = 1778; //SQLSTATE: HY000 Message: Cannot execute statements with implicit commit inside a transaction when GTID_NEXT != AUTOMATIC or GTID_NEXT_LIST != NULL. + public final static int ER_GTID_MODE_2_OR_3_REQUIRES_ENFORCE_GTID_CONSISTENCY_ON = 1779; //SQLSTATE: HY000 Message: GTID_MODE = ON or GTID_MODE = UPGRADE_STEP_2 requires ENFORCE_GTID_CONSISTENCY = 1. + public final static int ER_GTID_MODE_REQUIRES_BINLOG = 1780; //SQLSTATE: HY000 Message: GTID_MODE = ON or UPGRADE_STEP_1 or UPGRADE_STEP_2 requires --log-bin and --log-slave-updates. + public final static int ER_CANT_SET_GTID_NEXT_TO_GTID_WHEN_GTID_MODE_IS_OFF = 1781; //SQLSTATE: HY000 Message: GTID_NEXT cannot be set to UUID:NUMBER when GTID_MODE = OFF. + public final static int ER_CANT_SET_GTID_NEXT_TO_ANONYMOUS_WHEN_GTID_MODE_IS_ON = 1782; //SQLSTATE: HY000 Message: GTID_NEXT cannot be set to ANONYMOUS when GTID_MODE = ON. + public final static int ER_CANT_SET_GTID_NEXT_LIST_TO_NON_NULL_WHEN_GTID_MODE_IS_OFF = 1783; //SQLSTATE: HY000 Message: GTID_NEXT_LIST cannot be set to a non-NULL value when GTID_MODE = OFF. + public final static int ER_FOUND_GTID_EVENT_WHEN_GTID_MODE_IS_OFF = 1784; //SQLSTATE: HY000 Message: Found a Gtid_log_event or Previous_gtids_log_event when GTID_MODE = OFF. + public final static int ER_GTID_UNSAFE_NON_TRANSACTIONAL_TABLE = 1785; //SQLSTATE: HY000 Message: When ENFORCE_GTID_CONSISTENCY = 1, updates to non-transactional tables can only be done in either autocommitted statements or single-statement transactions, and never in the same statement as updates to transactional tables. + public final static int ER_GTID_UNSAFE_CREATE_SELECT = 1786; //SQLSTATE: HY000 Message: CREATE TABLE ... SELECT is forbidden when ENFORCE_GTID_CONSISTENCY = 1. + public final static int ER_GTID_UNSAFE_CREATE_DROP_TEMPORARY_TABLE_IN_TRANSACTION = 1787; //SQLSTATE: HY000 Message: When ENFORCE_GTID_CONSISTENCY = 1, the statements CREATE TEMPORARY TABLE and DROP TEMPORARY TABLE can be executed in a non-transactional context only, and require that AUTOCOMMIT = 1. + public final static int ER_GTID_MODE_CAN_ONLY_CHANGE_ONE_STEP_AT_A_TIME = 1788; //SQLSTATE: HY000 Message: The value of GTID_MODE can only change one step at a time: OFF <-> UPGRADE_STEP_1 <-> UPGRADE_STEP_2 <-> ON. Also note that this value must be stepped up or down simultaneously on all servers; see the Manual for instructions. + public final static int ER_MASTER_HAS_PURGED_REQUIRED_GTIDS = 1789; //SQLSTATE: HY000 Message: The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires. + public final static int ER_CANT_SET_GTID_NEXT_WHEN_OWNING_GTID = 1790; //SQLSTATE: HY000 Message: GTID_NEXT cannot be changed by a client that owns a GTID. The client owns %s. Ownership is released on COMMIT or ROLLBACK. + public final static int ER_UNKNOWN_EXPLAIN_FORMAT = 1791; //SQLSTATE: HY000 Message: Unknown EXPLAIN format name: '%s' + public final static int ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION = 1792; //SQLSTATE: 25006 Message: Cannot execute statement in a READ ONLY transaction. + public final static int ER_TOO_LONG_TABLE_PARTITION_COMMENT = 1793; //SQLSTATE: HY000 Message: Comment for table partition '%s' is too long (max = %lu) + public final static int ER_SLAVE_CONFIGURATION = 1794; //SQLSTATE: HY000 Message: Slave is not configured or failed to initialize properly. You must at least set --server-id to enable either a master or a slave. Additional error messages can be found in the MySQL error log. + public final static int ER_INNODB_FT_LIMIT = 1795; //SQLSTATE: HY000 Message: InnoDB presently supports one FULLTEXT index creation at a time + public final static int ER_INNODB_NO_FT_TEMP_TABLE = 1796; //SQLSTATE: HY000 Message: Cannot create FULLTEXT index on temporary InnoDB table + public final static int ER_INNODB_FT_WRONG_DOCID_COLUMN = 1797; //SQLSTATE: HY000 Message: Column '%s' is of wrong type for an InnoDB FULLTEXT index + public final static int ER_INNODB_FT_WRONG_DOCID_INDEX = 1798; //SQLSTATE: HY000 Message: Index '%s' is of wrong type for an InnoDB FULLTEXT index + public final static int ER_INNODB_ONLINE_LOG_TOO_BIG = 1799; //SQLSTATE: HY000 Message: Creating index '%s' required more than 'innodb_online_alter_log_max_size' bytes of modification log. Please try again. + public final static int ER_UNKNOWN_ALTER_ALGORITHM = 1800; //SQLSTATE: HY000 Message: Unknown ALGORITHM '%s' + public final static int ER_UNKNOWN_ALTER_LOCK = 1801; //SQLSTATE: HY000 Message: Unknown LOCK type '%s' + public final static int ER_MTS_CHANGE_MASTER_CANT_RUN_WITH_GAPS = 1802; //SQLSTATE: HY000 Message: CHANGE MASTER cannot be executed when the slave was stopped with an error or killed in MTS mode. Consider using RESET SLAVE or START SLAVE UNTIL. + public final static int ER_MTS_RECOVERY_FAILURE = 1803; //SQLSTATE: HY000 Message: Cannot recover after SLAVE errored out in parallel execution mode. Additional error messages can be found in the MySQL error log. + public final static int ER_MTS_RESET_WORKERS = 1804; //SQLSTATE: HY000 Message: Cannot clean up worker info tables. Additional error messages can be found in the MySQL error log. + public final static int ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2 = 1805; //SQLSTATE: HY000 Message: Column count of %s.%s is wrong. Expected %d, found %d. The table is probably corrupted + public final static int ER_SLAVE_SILENT_RETRY_TRANSACTION = 1806; //SQLSTATE: HY000 Message: Slave must silently retry current transaction + public final static int ER_DISCARD_FK_CHECKS_RUNNING = 1807; //SQLSTATE: HY000 Message: There is a foreign key check running on table '%s'. Cannot discard the table. + public final static int ER_TABLE_SCHEMA_MISMATCH = 1808; //SQLSTATE: HY000 Message: Schema mismatch (%s) + public final static int ER_TABLE_IN_SYSTEM_TABLESPACE = 1809; //SQLSTATE: HY000 Message: Table '%s' in system tablespace + public final static int ER_IO_READ_ERROR = 1810; //SQLSTATE: HY000 Message: IO Read error: (%lu, %s) %s + public final static int ER_IO_WRITE_ERROR = 1811; //SQLSTATE: HY000 Message: IO Write error: (%lu, %s) %s + public final static int ER_TABLESPACE_MISSING = 1812; //SQLSTATE: HY000 Message: Tablespace is missing for table '%s' + public final static int ER_TABLESPACE_EXISTS = 1813; //SQLSTATE: HY000 Message: Tablespace for table '%s' exists. Please DISCARD the tablespace before IMPORT. + public final static int ER_TABLESPACE_DISCARDED = 1814; //SQLSTATE: HY000 Message: Tablespace has been discarded for table '%s' + public final static int ER_INTERNAL_ERROR = 1815; //SQLSTATE: HY000 Message: Internal error: %s + public final static int ER_INNODB_IMPORT_ERROR = 1816; //SQLSTATE: HY000 Message: ALTER TABLE '%s' IMPORT TABLESPACE failed with error %lu : '%s' + public final static int ER_INNODB_INDEX_CORRUPT = 1817; //SQLSTATE: HY000 Message: Index corrupt: %s + public final static int ER_INVALID_YEAR_COLUMN_LENGTH = 1818; //SQLSTATE: HY000 Message: YEAR(%lu) column type is deprecated. Creating YEAR(4) column instead. + public final static int ER_NOT_VALID_PASSWORD = 1819; //SQLSTATE: HY000 Message: Your password does not satisfy the current policy requirements + public final static int ER_MUST_CHANGE_PASSWORD = 1820; //SQLSTATE: HY000 Message: You must SET PASSWORD before executing this statement + public final static int ER_FK_NO_INDEX_CHILD = 1821; //SQLSTATE: HY000 Message: Failed to add the foreign key constaint. Missing index for constraint '%s' in the foreign table '%s' + public final static int ER_FK_NO_INDEX_PARENT = 1822; //SQLSTATE: HY000 Message: Failed to add the foreign key constaint. Missing index for constraint '%s' in the referenced table '%s' + public final static int ER_FK_FAIL_ADD_SYSTEM = 1823; //SQLSTATE: HY000 Message: Failed to add the foreign key constraint '%s' to system tables + public final static int ER_FK_CANNOT_OPEN_PARENT = 1824; //SQLSTATE: HY000 Message: Failed to open the referenced table '%s' + public final static int ER_FK_INCORRECT_OPTION = 1825; //SQLSTATE: HY000 Message: Failed to add the foreign key constraint on table '%s'. Incorrect options in FOREIGN KEY constraint '%s' + public final static int ER_FK_DUP_NAME = 1826; //SQLSTATE: HY000 Message: Duplicate foreign key constraint name '%s' + public final static int ER_PASSWORD_FORMAT = 1827; //SQLSTATE: HY000 Message: The password hash doesn't have the expected format. Check if the correct password algorithm is being used with the PASSWORD() function. + public final static int ER_FK_COLUMN_CANNOT_DROP = 1828; //SQLSTATE: HY000 Message: Cannot drop column '%s': needed in a foreign key constraint '%s' + public final static int ER_FK_COLUMN_CANNOT_DROP_CHILD = 1829; //SQLSTATE: HY000 Message: Cannot drop column '%s': needed in a foreign key constraint '%s' of table '%s' + public final static int ER_FK_COLUMN_NOT_NULL = 1830; //SQLSTATE: HY000 Message: Column '%s' cannot be NOT NULL: needed in a foreign key constraint '%s' SET NULL + public final static int ER_DUP_INDEX = 1831; //SQLSTATE: HY000 Message: Duplicate index '%s' defined on the table '%s.%s'. This is deprecated and will be disallowed in a future release. + public final static int ER_FK_COLUMN_CANNOT_CHANGE = 1832; //SQLSTATE: HY000 Message: Cannot change column '%s': used in a foreign key constraint '%s' + public final static int ER_FK_COLUMN_CANNOT_CHANGE_CHILD = 1833; //SQLSTATE: HY000 Message: Cannot change column '%s': used in a foreign key constraint '%s' of table '%s' + public final static int ER_FK_CANNOT_DELETE_PARENT = 1834; //SQLSTATE: HY000 Message: Cannot delete rows from table which is parent in a foreign key constraint '%s' of table '%s' + public final static int ER_MALFORMED_PACKET = 1835; //SQLSTATE: HY000 Message: Malformed communication packet. + public final static int ER_READ_ONLY_MODE = 1836; //SQLSTATE: HY000 Message: Running in read-only mode + public final static int ER_GTID_NEXT_TYPE_UNDEFINED_GROUP = 1837; //SQLSTATE: HY000 Message: When GTID_NEXT is set to a GTID, you must explicitly set it again after a COMMIT or ROLLBACK. If you see this error message in the slave SQL thread, it means that a table in the current transaction is transactional on the master and non-transactional on the slave. In a client connection, it means that you executed SET GTID_NEXT before a transaction and forgot to set GTID_NEXT to a different identifier or to 'AUTOMATIC' after COMMIT or ROLLBACK. Current GTID_NEXT is '%s'. + public final static int ER_VARIABLE_NOT_SETTABLE_IN_SP = 1838; //SQLSTATE: HY000 Message: The system variable %s cannot be set in stored procedures. + public final static int ER_CANT_SET_GTID_PURGED_WHEN_GTID_MODE_IS_OFF = 1839; //SQLSTATE: HY000 Message: GTID_PURGED can only be set when GTID_MODE = ON. + public final static int ER_CANT_SET_GTID_PURGED_WHEN_GTID_EXECUTED_IS_NOT_EMPTY = 1840; //SQLSTATE: HY000 Message: GTID_PURGED can only be set when GTID_EXECUTED is empty. + public final static int ER_CANT_SET_GTID_PURGED_WHEN_OWNED_GTIDS_IS_NOT_EMPTY = 1841; //SQLSTATE: HY000 Message: GTID_PURGED can only be set when there are no ongoing transactions (not even in other clients). + public final static int ER_GTID_PURGED_WAS_CHANGED = 1842; //SQLSTATE: HY000 Message: GTID_PURGED was changed from '%s' to '%s'. + public final static int ER_GTID_EXECUTED_WAS_CHANGED = 1843; //SQLSTATE: HY000 Message: GTID_EXECUTED was changed from '%s' to '%s'. + public final static int ER_BINLOG_STMT_MODE_AND_NO_REPL_TABLES = 1844; //SQLSTATE: HY000 Message: Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT, and both replicated and non replicated tables are written to. + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED = 1845; //SQLSTATE: 0A000 Message: %s is not supported for this operation. Try %s. + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON = 1846; //SQLSTATE: 0A000 Message: %s is not supported. Reason: %s. Try %s. + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COPY = 1847; //SQLSTATE: HY000 Message: COPY algorithm requires a lock + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION = 1848; //SQLSTATE: HY000 Message: Partition specific operations do not yet support LOCK/ALGORITHM + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME = 1849; //SQLSTATE: HY000 Message: Columns participating in a foreign key are renamed + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE = 1850; //SQLSTATE: HY000 Message: Cannot change column type INPLACE + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK = 1851; //SQLSTATE: HY000 Message: Adding foreign keys needs foreign_key_checks=OFF + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_IGNORE = 1852; //SQLSTATE: HY000 Message: Creating unique indexes with IGNORE requires COPY algorithm to remove duplicate rows + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK = 1853; //SQLSTATE: HY000 Message: Dropping a primary key is not allowed without also adding a new primary key + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC = 1854; //SQLSTATE: HY000 Message: Adding an auto-increment column requires a lock + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS = 1855; //SQLSTATE: HY000 Message: Cannot replace hidden FTS_DOC_ID with a user-visible one + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS = 1856; //SQLSTATE: HY000 Message: Cannot drop or rename FTS_DOC_ID + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS = 1857; //SQLSTATE: HY000 Message: Fulltext index creation requires a lock + public final static int ER_SQL_SLAVE_SKIP_COUNTER_NOT_SETTABLE_IN_GTID_MODE = 1858; //SQLSTATE: HY000 Message: sql_slave_skip_counter can not be set when the server is running with GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction + public final static int ER_DUP_UNKNOWN_IN_INDEX = 1859; //SQLSTATE: 23000 Message: Duplicate entry for key '%s' + public final static int ER_IDENT_CAUSES_TOO_LONG_PATH = 1860; //SQLSTATE: HY000 Message: Long database name and identifier for object resulted in path length exceeding %d characters. Path: '%s'. + public final static int ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL = 1861; //SQLSTATE: HY000 Message: cannot silently convert NULL values, as required in this SQL_MODE; was introduced in 5.7.1. + public final static int ER_MUST_CHANGE_PASSWORD_LOGIN = 1862; //SQLSTATE: HY000 Message: Your password has expired. To log in you must change it using a client that supports expired passwords. was introduced in 5.7.1. + public final static int ER_ROW_IN_WRONG_PARTITION = 1863; //SQLSTATE: HY000 Message: Found a row in wrong partition %s; was introduced in 5.7.1. + public final static int ER_MTS_EVENT_BIGGER_PENDING_JOBS_SIZE_MAX = 1864; //SQLSTATE: HY000 Message: Cannot schedule event %s, relay-log name %s, position %s to Worker thread because its size %lu exceeds %lu of slave_pending_jobs_size_max.; was introduced in 5.7.2. + + public final static int ER_INNODB_NO_FT_USES_PARSER = 1865; //SQLSTATE: HY000 Message: Cannot CREATE FULLTEXT INDEX WITH PARSER on InnoDB table; was introduced in 5.7.2. + public final static int ER_BINLOG_LOGICAL_CORRUPTION = 1866; //SQLSTATE: HY000 Message: The binary log file '%s' is logically corrupted: %s; was introduced in 5.7.2. + public final static int ER_WARN_PURGE_LOG_IN_USE = 1867; //SQLSTATE: HY000 Message: file %s was not purged because it was being read by %d thread(s), purged only %d out of %d files. was introduced in 5.7.2. + public final static int ER_WARN_PURGE_LOG_IS_ACTIVE = 1868; //SQLSTATE: HY000 Message: file %s was not purged because it is the active log file.; was introduced in 5.7.2. + public final static int ER_AUTO_INCREMENT_CONFLICT = 1869; //SQLSTATE: HY000 Message: Auto-increment value in UPDATE conflicts with internally generated values; was introduced in 5.7.2. + public final static int WARN_ON_BLOCKHOLE_IN_RBR = 1870; //SQLSTATE: HY000 Message: Row events are not logged for %s statements that modify BLACKHOLE tables in row format. Table(s): '%s'; was introduced in 5.7.2. + public final static int ER_SLAVE_MI_INIT_REPOSITORY = 1871; //SQLSTATE: HY000 Message: Slave failed to initialize master info structure from the repository; was introduced in 5.7.2. + public final static int ER_SLAVE_RLI_INIT_REPOSITORY = 1872; //SQLSTATE: HY000 Message: Slave failed to initialize relay log info structure from the repository; was introduced in 5.7.2. + public final static int ER_ACCESS_DENIED_CHANGE_USER_ERROR = 1873; //SQLSTATE: 28000 Message: Access denied trying to change to user '%s'@'%s' (using password: %s). Disconnecting. was introduced in 5.7.2. + public final static int ER_INNODB_READ_ONLY = 1874; //SQLSTATE: HY000 Message: InnoDB is in read only mode.; was introduced in 5.7.2. + public final static int ER_STOP_SLAVE_SQL_THREAD_TIMEOUT = 1875; //SQLSTATE: HY000 Message: STOP SLAVE command execution is incomplete: Slave SQL thread got the stop signal, thread is busy, SQL thread will stop once the current task is complete.; was introduced in 5.7.2. + public final static int ER_STOP_SLAVE_IO_THREAD_TIMEOUT = 1876; //SQLSTATE: HY000 Message: STOP SLAVE command execution is incomplete: Slave IO thread got the stop signal, thread is busy, IO thread will stop once the current task is complete.; was introduced in 5.7.2. + public final static int ER_TABLE_CORRUPT = 1877; //SQLSTATE: HY000 Message: Operation cannot be performed. The table '%s.%s' is missing, corrupt or contains bad data.; was introduced in 5.7.2. + public final static int ER_TEMP_FILE_WRITE_FAILURE = 1878; //SQLSTATE: HY000 Message: Temporary file write failure.; was introduced in 5.7.3. + public final static int ER_INNODB_FT_AUX_NOT_HEX_ID = 1879; //SQLSTATE: HY000 Message: Upgrade index name failed, please use create index(alter table) algorithm copy to rebuild index.; was introduced in 5.7.4. + public final static int ER_OLD_TEMPORALS_UPGRADED = 1880; //SQLSTATE: HY000 Message: TIME/TIMESTAMP/DATETIME columns of old format have been upgraded to the new format.; was introduced in 5.7.4. + public final static int ER_INNODB_FORCED_RECOVERY = 1881; //SQLSTATE: HY000 Message: Operation not allowed when innodb_forced_recovery > 0.; was introduced in 5.7.4. + public final static int ER_AES_INVALID_IV = 1882; //SQLSTATE: HY000 Message: The initialization vector supplied to %s is too short. Must be at least %d bytes long; was introduced in 5.7.4. + public final static int ER_FILE_CORRUPT = 1883; //SQLSTATE: HY000 Message: File %s is corrupted + public final static int ER_ERROR_ON_MASTER = 1884; //SQLSTATE: HY000 Message: Query partially completed on the master (error on master: %d) and was aborted. There is a chance that your master is inconsistent at this point. If you are sure that your master is ok, run this query manually on the slave and then restart the slave with SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE;. Query:'%s' + public final static int ER_INCONSISTENT_ERROR = 1885; //SQLSTATE: HY000 Message: Query caused different errors on master and slave. Error on master: message (format)='%s' error code=%d; Error on slave:actual message='%s', error code=%d. Default database:'%s'. Query:'%s' + public final static int ER_STORAGE_ENGINE_NOT_LOADED = 1886; //SQLSTATE: HY000 Message: Storage engine for table '%s'.'%s' is not loaded. + public final static int ER_GET_STACKED_DA_WITHOUT_ACTIVE_HANDLER = 1887; //SQLSTATE: 0Z002 Message: GET STACKED DIAGNOSTICS when handler not active + public final static int ER_WARN_LEGACY_SYNTAX_CONVERTED = 1888; //SQLSTATE: HY000 Message: %s is no longer supported. The statement was converted to %s. + public final static int ER_BINLOG_UNSAFE_FULLTEXT_PLUGIN = 1889; //SQLSTATE: HY000 Message: Statement is unsafe because it uses a fulltext parser plugin which may not return the same value on the slave.; was introduced in 5.7.1. + public final static int ER_CANNOT_DISCARD_TEMPORARY_TABLE = 1890; //SQLSTATE: HY000 Message: Cannot DISCARD/IMPORT tablespace associated with temporary table; was introduced in 5.7.1. + public final static int ER_FK_DEPTH_EXCEEDED = 1891; //SQLSTATE: HY000 Message: Foreign key cascade delete/update exceeds max depth of %d.; was introduced in 5.7.2. + public final static int ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE_V2 = 1892; //SQLSTATE: HY000 Message: Column count of %s.%s is wrong. Expected %d, found %d. Created with MySQL %d, now running %d. Please use mysql_upgrade to fix this error.; was introduced in 5.7.2. + public final static int ER_WARN_TRIGGER_DOESNT_HAVE_CREATED = 1893; //SQLSTATE: HY000 Message: Trigger %s.%s.%s does not have CREATED attribute.; was introduced in 5.7.2. + public final static int ER_REFERENCED_TRG_DOES_NOT_EXIST = 1894; //SQLSTATE: HY000 Message: Referenced trigger '%s' for the given action time and event type does not exist.; was introduced in 5.7.2. + public final static int ER_EXPLAIN_NOT_SUPPORTED = 1895; //SQLSTATE: HY000 Message: EXPLAIN FOR CONNECTION command is supported only for SELECT/UPDATE/INSERT/DELETE/REPLACE; was introduced in 5.7.2. + public final static int ER_INVALID_FIELD_SIZE = 1896; //SQLSTATE: HY000 Message: Invalid size for column '%s'.; was introduced in 5.7.2. + public final static int ER_MISSING_HA_CREATE_OPTION = 1897; //SQLSTATE: HY000 Message: Table storage engine '%s' found required create option missing; was introduced in 5.7.2. + public final static int ER_ENGINE_OUT_OF_MEMORY = 1898; //SQLSTATE: HY000 Message: Out of memory in storage engine '%s'.; was introduced in 5.7.3. + public final static int ER_PASSWORD_EXPIRE_ANONYMOUS_USER = 1899; //SQLSTATE: HY000 Message: The password for anonymous user cannot be expired.; was introduced in 5.7.3. + public final static int ER_SLAVE_SQL_THREAD_MUST_STOP = 1900; //SQLSTATE: HY000 Message: This operation cannot be performed with a running slave sql thread; run STOP SLAVE SQL_THREAD first; was introduced in 5.7.3. + public final static int ER_NO_FT_MATERIALIZED_SUBQUERY = 1901; //SQLSTATE: HY000 Message: Cannot create FULLTEXT index on materialized subquery; was introduced in 5.7.4. + public final static int ER_INNODB_UNDO_LOG_FULL = 1902; //SQLSTATE: HY000 Message: Undo Log error: %s; was introduced in 5.7.4. + public final static int ER_INVALID_ARGUMENT_FOR_LOGARITHM = 1903; //SQLSTATE: 2201E Message: Invalid argument for logarithm; was introduced in 5.7.4. + public final static int ER_SLAVE_IO_THREAD_MUST_STOP = 1904; //SQLSTATE: HY000 Message: This operation cannot be performed with a running slave io thread; run STOP SLAVE IO_THREAD first.; was introduced in 5.7.4. + public final static int ER_WARN_OPEN_TEMP_TABLES_MUST_BE_ZERO = 1905; //SQLSTATE: HY000 Message: This operation may not be safe when the slave has temporary tables. The tables will be kept open until the server restarts or until the tables are deleted by any replicated DROP statement. Suggest to wait until slave_open_temp_tables = 0.; was introduced in 5.7.4. + public final static int ER_WARN_ONLY_MASTER_LOG_FILE_NO_POS = 1906; //SQLSTATE: HY000 Message: CHANGE MASTER TO with a MASTER_LOG_FILE clause but no MASTER_LOG_POS clause may not be safe. The old position value may not be valid for the new binary log file.; was introduced in 5.7.4. + public final static int ER_QUERY_TIMEOUT = 1907; //SQLSTATE: HY000 Message: Query execution was interrupted, max_statement_time exceeded; was introduced in 5.7.4. + public final static int ER_NON_RO_SELECT_DISABLE_TIMER = 1908; //SQLSTATE: HY000 Message: Select is not a read only statement, disabling timer; was introduced in 5.7.4. + public final static int ER_DUP_LIST_ENTRY = 1909; //SQLSTATE: HY000 Message: Duplicate entry '%s'.; was introduced in 5.7.4. + public final static int ER_SQL_MODE_NO_EFFECT = 1910; //SQLSTATE: HY000 Message: '%s' mode no longer has any effect. Use STRICT_ALL_TABLES or STRICT_TRANS_TABLES instead.; was introduced in 5.7.4. + + public static final int ER_X_SERVICE_ERROR = 5010; + public static final int ER_X_SESSION = 5011; + public static final int ER_X_INVALID_ARGUMENT = 5012; + public static final int ER_X_MISSING_ARGUMENT = 5013; + public static final int ER_X_BAD_INSERT_DATA = 5014; + public static final int ER_X_CMD_NUM_ARGUMENTS = 5015; + public static final int ER_X_CMD_ARGUMENT_TYPE = 5016; + public static final int ER_X_CMD_ARGUMENT_VALUE = 5017; + public static final int ER_X_BAD_UPDATE_DATA = 5050; + public static final int ER_X_BAD_TYPE_OF_UPDATE = 5051; + public static final int ER_X_BAD_COLUMN_TO_UPDATE = 5052; + public static final int ER_X_BAD_MEMBER_TO_UPDATE = 5053; + public static final int ER_X_BAD_STATEMENT_ID = 5110; + public static final int ER_X_BAD_CURSOR_ID = 5111; + public static final int ER_X_BAD_SCHEMA = 5112; + public static final int ER_X_BAD_TABLE = 5113; + public static final int ER_X_BAD_PROJECTION = 5114; + public static final int ER_X_DOC_ID_MISSING = 5115; + public static final int ER_X_DOC_ID_DUPLICATE = 5116; + public static final int ER_X_DOC_REQUIRED_FIELD_MISSING = 5117; + public static final int ER_X_PROJ_BAD_KEY_NAME = 5120; + public static final int ER_X_BAD_DOC_PATH = 5121; + public static final int ER_X_CURSOR_EXISTS = 5122; + public static final int ER_X_EXPR_BAD_OPERATOR = 5150; + public static final int ER_X_EXPR_BAD_NUM_ARGS = 5151; + public static final int ER_X_EXPR_MISSING_ARG = 5152; + public static final int ER_X_EXPR_BAD_TYPE_VALUE = 5153; + public static final int ER_X_EXPR_BAD_VALUE = 5154; + public static final int ER_X_EXPR_BAD_REGEX = 5155; + public static final int ER_X_INVALID_COLLECTION = 5156; + public static final int ER_X_INVALID_ADMIN_COMMAND = 5157; + public static final int ER_X_EXPECT_NOT_OPEN = 5158; + public static final int ER_X_EXPECT_FAILED = 5159; + public static final int ER_X_EXPECT_BAD_CONDITION = 5160; + public static final int ER_X_EXPECT_BAD_CONDITION_VALUE = 5161; + public static final int ER_X_INVALID_NAMESPACE = 5162; + public static final int ER_X_BAD_NOTICE = 5163; + public static final int ER_X_CANNOT_DISABLE_NOTICE = 5164; + + // Connector/J-specific errors outside the space of server errors. + public static final int ERROR_CODE_NULL_LOAD_BALANCED_CONNECTION = 1000001; + public static final int ERROR_CODE_REPLICATION_CONNECTION_WITH_NO_HOSTS = 1000002; + + // SQL-92 + public static final String SQL_STATE_WARNING = "01000"; + public static final String SQL_STATE_DISCONNECT_ERROR = "01002"; + public static final String SQL_STATE_DATE_TRUNCATED = "01004"; + public static final String SQL_STATE_PRIVILEGE_NOT_REVOKED = "01006"; + public static final String SQL_STATE_NO_DATA = "02000"; + public static final String SQL_STATE_WRONG_NO_OF_PARAMETERS = "07001"; + public static final String SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE = "08001"; + public static final String SQL_STATE_CONNECTION_IN_USE = "08002"; + public static final String SQL_STATE_CONNECTION_NOT_OPEN = "08003"; + public static final String SQL_STATE_CONNECTION_REJECTED = "08004"; + public static final String SQL_STATE_CONNECTION_FAILURE = "08006"; + public static final String SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN = "08007"; + public static final String SQL_STATE_COMMUNICATION_LINK_FAILURE = "08S01"; + public static final String SQL_STATE_FEATURE_NOT_SUPPORTED = "0A000"; + public static final String SQL_STATE_CARDINALITY_VIOLATION = "21000"; + public static final String SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST = "21S01"; + public static final String SQL_STATE_STRING_DATA_RIGHT_TRUNCATION = "22001"; + public static final String SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE = "22003"; + public static final String SQL_STATE_INVALID_DATETIME_FORMAT = "22007"; + public static final String SQL_STATE_DATETIME_FIELD_OVERFLOW = "22008"; + public static final String SQL_STATE_DIVISION_BY_ZERO = "22012"; + public static final String SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST = "22018"; + public static final String SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION = "23000"; + public static final String SQL_STATE_INVALID_CURSOR_STATE = "24000"; + public static final String SQL_STATE_INVALID_TRANSACTION_STATE = "25000"; + public static final String SQL_STATE_INVALID_AUTH_SPEC = "28000"; + public static final String SQL_STATE_INVALID_TRANSACTION_TERMINATION = "2D000"; + public static final String SQL_STATE_INVALID_CONDITION_NUMBER = "35000"; + public static final String SQL_STATE_INVALID_CATALOG_NAME = "3D000"; + public static final String SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE = "40001"; + public static final String SQL_STATE_SYNTAX_ERROR = "42000"; + public static final String SQL_STATE_ER_TABLE_EXISTS_ERROR = "42S01"; + public static final String SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND = "42S02"; + public static final String SQL_STATE_ER_NO_SUCH_INDEX = "42S12"; + public static final String SQL_STATE_ER_DUP_FIELDNAME = "42S21"; + public static final String SQL_STATE_ER_BAD_FIELD_ERROR = "42S22"; + + // SQL-99 + public static final String SQL_STATE_INVALID_CONNECTION_ATTRIBUTE = "01S00"; + public static final String SQL_STATE_ERROR_IN_ROW = "01S01"; + public static final String SQL_STATE_NO_ROWS_UPDATED_OR_DELETED = "01S03"; + public static final String SQL_STATE_MORE_THAN_ONE_ROW_UPDATED_OR_DELETED = "01S04"; + public static final String SQL_STATE_RESIGNAL_WHEN_HANDLER_NOT_ACTIVE = "0K000"; + public static final String SQL_STATE_STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER = "0Z002"; + public static final String SQL_STATE_CASE_NOT_FOUND_FOR_CASE_STATEMENT = "20000"; + public static final String SQL_STATE_NULL_VALUE_NOT_ALLOWED = "22004"; + public static final String SQL_STATE_INVALID_LOGARITHM_ARGUMENT = "2201E"; + public static final String SQL_STATE_ACTIVE_SQL_TRANSACTION = "25001"; + public static final String SQL_STATE_READ_ONLY_SQL_TRANSACTION = "25006"; + public static final String SQL_STATE_SRE_PROHIBITED_SQL_STATEMENT_ATTEMPTED = "2F003"; + public static final String SQL_STATE_SRE_FUNCTION_EXECUTED_NO_RETURN_STATEMENT = "2F005"; + public static final String SQL_STATE_ER_QUERY_INTERRUPTED = "70100"; // non-standard ? + public static final String SQL_STATE_BASE_TABLE_OR_VIEW_ALREADY_EXISTS = "S0001"; + public static final String SQL_STATE_BASE_TABLE_NOT_FOUND = "S0002"; + public static final String SQL_STATE_INDEX_ALREADY_EXISTS = "S0011"; + public static final String SQL_STATE_INDEX_NOT_FOUND = "S0012"; + public static final String SQL_STATE_COLUMN_ALREADY_EXISTS = "S0021"; + public static final String SQL_STATE_COLUMN_NOT_FOUND = "S0022"; + public static final String SQL_STATE_NO_DEFAULT_FOR_COLUMN = "S0023"; + public static final String SQL_STATE_GENERAL_ERROR = "S1000"; + public static final String SQL_STATE_MEMORY_ALLOCATION_FAILURE = "S1001"; + public static final String SQL_STATE_INVALID_COLUMN_NUMBER = "S1002"; + public static final String SQL_STATE_ILLEGAL_ARGUMENT = "S1009"; + public static final String SQL_STATE_DRIVER_NOT_CAPABLE = "S1C00"; + public static final String SQL_STATE_TIMEOUT_EXPIRED = "S1T00"; + public static final String SQL_STATE_CLI_SPECIFIC_CONDITION = "HY000"; + public static final String SQL_STATE_MEMORY_ALLOCATION_ERROR = "HY001"; + public static final String SQL_STATE_XA_RBROLLBACK = "XA100"; + public static final String SQL_STATE_XA_RBDEADLOCK = "XA102"; + public static final String SQL_STATE_XA_RBTIMEOUT = "XA106"; + public static final String SQL_STATE_XA_RMERR = "XAE03"; + public static final String SQL_STATE_XAER_NOTA = "XAE04"; + public static final String SQL_STATE_XAER_INVAL = "XAE05"; + public static final String SQL_STATE_XAER_RMFAIL = "XAE07"; + public static final String SQL_STATE_XAER_DUPID = "XAE08"; + public static final String SQL_STATE_XAER_OUTSIDE = "XAE09"; + + public static final String SQL_STATE_BAD_SSL_PARAMS = "08000"; + + private static Map sqlStateMessages; + + private static Map mysqlToSql99State; + + static { + + sqlStateMessages = new HashMap<>(); + sqlStateMessages.put(SQL_STATE_DISCONNECT_ERROR, Messages.getString("SQLError.35")); + sqlStateMessages.put(SQL_STATE_DATE_TRUNCATED, Messages.getString("SQLError.36")); + sqlStateMessages.put(SQL_STATE_PRIVILEGE_NOT_REVOKED, Messages.getString("SQLError.37")); + sqlStateMessages.put(SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, Messages.getString("SQLError.38")); + sqlStateMessages.put(SQL_STATE_ERROR_IN_ROW, Messages.getString("SQLError.39")); + sqlStateMessages.put(SQL_STATE_NO_ROWS_UPDATED_OR_DELETED, Messages.getString("SQLError.40")); + sqlStateMessages.put(SQL_STATE_MORE_THAN_ONE_ROW_UPDATED_OR_DELETED, Messages.getString("SQLError.41")); + sqlStateMessages.put(SQL_STATE_WRONG_NO_OF_PARAMETERS, Messages.getString("SQLError.42")); + sqlStateMessages.put(SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, Messages.getString("SQLError.43")); + sqlStateMessages.put(SQL_STATE_CONNECTION_IN_USE, Messages.getString("SQLError.44")); + sqlStateMessages.put(SQL_STATE_CONNECTION_NOT_OPEN, Messages.getString("SQLError.45")); + sqlStateMessages.put(SQL_STATE_CONNECTION_REJECTED, Messages.getString("SQLError.46")); + sqlStateMessages.put(SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, Messages.getString("SQLError.47")); + sqlStateMessages.put(SQL_STATE_COMMUNICATION_LINK_FAILURE, Messages.getString("SQLError.48")); + sqlStateMessages.put(SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST, Messages.getString("SQLError.49")); + sqlStateMessages.put(SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE, Messages.getString("SQLError.50")); + sqlStateMessages.put(SQL_STATE_DATETIME_FIELD_OVERFLOW, Messages.getString("SQLError.51")); + sqlStateMessages.put(SQL_STATE_DIVISION_BY_ZERO, Messages.getString("SQLError.52")); + sqlStateMessages.put(SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE, Messages.getString("SQLError.53")); + sqlStateMessages.put(SQL_STATE_INVALID_AUTH_SPEC, Messages.getString("SQLError.54")); + sqlStateMessages.put(SQL_STATE_SYNTAX_ERROR, Messages.getString("SQLError.55")); + sqlStateMessages.put(SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND, Messages.getString("SQLError.56")); + sqlStateMessages.put(SQL_STATE_BASE_TABLE_OR_VIEW_ALREADY_EXISTS, Messages.getString("SQLError.57")); + sqlStateMessages.put(SQL_STATE_BASE_TABLE_NOT_FOUND, Messages.getString("SQLError.58")); + sqlStateMessages.put(SQL_STATE_INDEX_ALREADY_EXISTS, Messages.getString("SQLError.59")); + sqlStateMessages.put(SQL_STATE_INDEX_NOT_FOUND, Messages.getString("SQLError.60")); + sqlStateMessages.put(SQL_STATE_COLUMN_ALREADY_EXISTS, Messages.getString("SQLError.61")); + sqlStateMessages.put(SQL_STATE_COLUMN_NOT_FOUND, Messages.getString("SQLError.62")); + sqlStateMessages.put(SQL_STATE_NO_DEFAULT_FOR_COLUMN, Messages.getString("SQLError.63")); + sqlStateMessages.put(SQL_STATE_GENERAL_ERROR, Messages.getString("SQLError.64")); + sqlStateMessages.put(SQL_STATE_MEMORY_ALLOCATION_FAILURE, Messages.getString("SQLError.65")); + sqlStateMessages.put(SQL_STATE_INVALID_COLUMN_NUMBER, Messages.getString("SQLError.66")); + sqlStateMessages.put(SQL_STATE_ILLEGAL_ARGUMENT, Messages.getString("SQLError.67")); + sqlStateMessages.put(SQL_STATE_DRIVER_NOT_CAPABLE, Messages.getString("SQLError.68")); + sqlStateMessages.put(SQL_STATE_TIMEOUT_EXPIRED, Messages.getString("SQLError.69")); + + mysqlToSql99State = new HashMap<>(); + + mysqlToSql99State.put(MysqlErrorNumbers.ER_SELECT_REDUCED, SQL_STATE_WARNING); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WARN_TOO_FEW_RECORDS, SQL_STATE_WARNING); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WARN_TOO_MANY_RECORDS, SQL_STATE_WARNING); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WARN_DATA_TRUNCATED, SQL_STATE_WARNING); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WARN_NULL_TO_NOTNULL, SQL_STATE_NULL_VALUE_NOT_ALLOWED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WARN_DATA_OUT_OF_RANGE, SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_UNINIT_VAR, SQL_STATE_WARNING); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SIGNAL_WARN, SQL_STATE_WARNING); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_FETCH_NO_DATA, SQL_STATE_NO_DATA); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SIGNAL_NOT_FOUND, SQL_STATE_NO_DATA); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CON_COUNT_ERROR, SQL_STATE_CONNECTION_REJECTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NOT_SUPPORTED_AUTH_MODE, SQL_STATE_CONNECTION_REJECTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BAD_HOST_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_HANDSHAKE_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UNKNOWN_COM_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SERVER_SHUTDOWN, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_FORCING_CLOSE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_IPSOCK_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ABORTING_CONNECTION, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_PACKET_TOO_LARGE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_READ_ERROR_FROM_PIPE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_FCNTL_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_PACKETS_OUT_OF_ORDER, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_UNCOMPRESS_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_READ_ERROR, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_READ_INTERRUPTED, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_ERROR_ON_WRITE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NET_WRITE_INTERRUPTED, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NEW_ABORTING_CONNECTION, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_MASTER_NET_READ, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_MASTER_NET_WRITE, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CONNECT_TO_MASTER, SQL_STATE_COMMUNICATION_LINK_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BADSELECT, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BADSTATEMENT, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_SUBSELECT_NYI, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_NO_RETSET, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ALTER_OPERATION_NOT_SUPPORTED, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, SQL_STATE_FEATURE_NOT_SUPPORTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DBACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BAD_DB_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_FIELD_WITH_GROUP, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_GROUP_FIELD, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_SUM_SELECT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_LONG_IDENT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_KEYNAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_FIELD_SPEC, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_PARSE_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_EMPTY_QUERY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NONUNIQ_TABLE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_INVALID_DEFAULT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_MULTIPLE_PRI_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_MANY_KEYS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_MANY_KEY_PARTS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_LONG_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_KEY_COLUMN_DOES_NOT_EXITS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BLOB_USED_AS_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_BIG_FIELDLENGTH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_AUTO_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_FIELD_TERMINATORS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BLOBS_AND_NO_TERMINATED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_REMOVE_ALL_FIELDS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_DROP_FIELD_OR_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BLOB_CANT_HAVE_DEFAULT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_DB_NAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_TABLE_NAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_BIG_SELECT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UNKNOWN_PROCEDURE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_PARAMCOUNT_TO_PROCEDURE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_FIELD_SPECIFIED_TWICE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UNSUPPORTED_EXTENSION, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TABLE_MUST_HAVE_COLUMNS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UNKNOWN_CHARACTER_SET, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_BIG_ROWSIZE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_OUTER_JOIN, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NULL_COLUMN_IN_INDEX, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_PASSWORD_ANONYMOUS_USER, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_PASSWORD_NOT_ALLOWED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_PASSWORD_NO_MATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_REGEXP_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_MIX_OF_GROUP_FUNC_AND_FIELDS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NONEXISTING_GRANT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TABLEACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_COLUMNACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ILLEGAL_GRANT_FOR_TABLE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_GRANT_WRONG_HOST_OR_USER, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NONEXISTING_TABLE_GRANT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NOT_ALLOWED_COMMAND, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SYNTAX_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_LONG_STRING, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TABLE_CANT_HANDLE_BLOB, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TABLE_CANT_HANDLE_AUTO_INCREMENT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_COLUMN_NAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_KEY_COLUMN, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BLOB_KEY_WITHOUT_LENGTH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_PRIMARY_CANT_HAVE_NULL, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_MANY_ROWS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_REQUIRES_PRIMARY_KEY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_KEY_DOES_NOT_EXITS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CHECK_NO_SUCH_TABLE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CHECK_NOT_IMPLEMENTED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_MANY_USER_CONNECTIONS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_PERMISSION_TO_CREATE_USER, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_USER_LIMIT_REACHED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SPECIFIC_ACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_DEFAULT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_VALUE_FOR_VAR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_TYPE_FOR_VAR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_USE_OPTION_HERE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NOT_SUPPORTED_YET, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_FK_DEF, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DERIVED_MUST_HAVE_ALIAS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TABLENAME_NOT_ALLOWED_HERE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SPATIAL_CANT_HAVE_NULL, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_COLLATION_CHARSET_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_NAME_FOR_INDEX, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_NAME_FOR_CATALOG, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UNKNOWN_STORAGE_ENGINE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_ALREADY_EXISTS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_DOES_NOT_EXIST, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_LILABEL_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_LABEL_REDEFINE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_LABEL_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BADRETURN, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UPDATE_LOG_DEPRECATED_IGNORED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UPDATE_LOG_DEPRECATED_TRANSLATED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_WRONG_NO_OF_ARGS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_COND_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_NORETURN, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BAD_CURSOR_QUERY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BAD_CURSOR_SELECT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_CURSOR_MISMATCH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_UNDECLARED_VAR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_DUP_PARAM, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_DUP_VAR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_DUP_COND, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_DUP_CURS, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_VARCOND_AFTER_CURSHNDLR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_CURSOR_AFTER_HANDLER, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_PROCACCESS_DENIED_ERROR, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NONEXISTING_PROC_GRANT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BAD_SQLSTATE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_CREATE_USER_WITH_GRANT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_DUP_HANDLER, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_NOT_VAR_ARG, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_BIG_SCALE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_BIG_PRECISION, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_M_BIGGER_THAN_D, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_LONG_BODY, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TOO_BIG_DISPLAYWIDTH, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_BAD_VAR_SHADOW, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_WRONG_NAME, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_NO_AGGREGATE, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_MAX_PREPARED_STMT_COUNT_REACHED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NON_GROUPING_FIELD_USED, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_PARAMETERS_TO_NATIVE_FCT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_PARAMETERS_TO_STORED_FCT, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_FUNC_INEXISTENT_NAME_COLLISION, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_SIGNAL_SET, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SPATIAL_MUST_HAVE_GEOM_COL, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TRUNCATE_ILLEGAL_FK, SQL_STATE_SYNTAX_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, SQL_STATE_CARDINALITY_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_OPERAND_COLUMNS, SQL_STATE_CARDINALITY_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SUBQUERY_NO_1_ROW, SQL_STATE_CARDINALITY_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_KEY, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BAD_NULL_ERROR, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NON_UNIQ_ERROR, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_ENTRY, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_UNIQUE, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_REFERENCED_ROW, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ROW_IS_REFERENCED, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ROW_IS_REFERENCED_2, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_REFERENCED_ROW_2, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_FOREIGN_DUPLICATE_KEY, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_ENTRY_WITH_KEY_NAME, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_UNKNOWN_IN_INDEX, SQL_STATE_INTEGRITY_CONSTRAINT_VIOLATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DATA_TOO_LONG, SQL_STATE_STRING_DATA_RIGHT_TRUNCATION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_CREATE_GEOMETRY_OBJECT, SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DATA_OUT_OF_RANGE, SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TRUNCATED_WRONG_VALUE, SQL_STATE_INVALID_DATETIME_FORMAT); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ILLEGAL_VALUE_FOR_TYPE, SQL_STATE_INVALID_DATETIME_FORMAT); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DATETIME_FUNCTION_OVERFLOW, SQL_STATE_DATETIME_FIELD_OVERFLOW); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DIVISION_BY_ZERO, SQL_STATE_DIVISION_BY_ZERO); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_CURSOR_ALREADY_OPEN, SQL_STATE_INVALID_CURSOR_STATE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_CURSOR_NOT_OPEN, SQL_STATE_INVALID_CURSOR_STATE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_DO_THIS_DURING_AN_TRANSACTION, SQL_STATE_INVALID_TRANSACTION_STATE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_READ_ONLY_TRANSACTION, SQL_STATE_INVALID_TRANSACTION_STATE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ACCESS_DENIED_ERROR, SQL_STATE_INVALID_AUTH_SPEC); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ACCESS_DENIED_NO_PASSWORD_ERROR, SQL_STATE_INVALID_AUTH_SPEC); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ACCESS_DENIED_CHANGE_USER_ERROR, SQL_STATE_INVALID_AUTH_SPEC); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DA_INVALID_CONDITION_NUMBER, SQL_STATE_INVALID_CONDITION_NUMBER); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_DB_ERROR, SQL_STATE_INVALID_CATALOG_NAME); + mysqlToSql99State.put(MysqlErrorNumbers.ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER, SQL_STATE_RESIGNAL_WHEN_HANDLER_NOT_ACTIVE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_GET_STACKED_DA_WITHOUT_ACTIVE_HANDLER, SQL_STATE_STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_CASE_NOT_FOUND, SQL_STATE_CASE_NOT_FOUND_FOR_CASE_STATEMENT); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_VALUE_COUNT, SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST); + mysqlToSql99State.put(MysqlErrorNumbers.ER_WRONG_VALUE_COUNT_ON_ROW, SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST); + mysqlToSql99State.put(MysqlErrorNumbers.ER_INVALID_USE_OF_NULL, SQL_STATE_NULL_VALUE_NOT_ALLOWED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_INVALID_ARGUMENT_FOR_LOGARITHM, SQL_STATE_INVALID_LOGARITHM_ARGUMENT); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_CHANGE_TX_ISOLATION, SQL_STATE_ACTIVE_SQL_TRANSACTION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, SQL_STATE_READ_ONLY_SQL_TRANSACTION); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_NO_RECURSIVE_CREATE, SQL_STATE_SRE_PROHIBITED_SQL_STATEMENT_ATTEMPTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_SP_NORETURNEND, SQL_STATE_SRE_FUNCTION_EXECUTED_NO_RETURN_STATEMENT); + mysqlToSql99State.put(MysqlErrorNumbers.ER_TABLE_EXISTS_ERROR, SQL_STATE_ER_TABLE_EXISTS_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BAD_TABLE_ERROR, SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND); + mysqlToSql99State.put(MysqlErrorNumbers.ER_UNKNOWN_TABLE, SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_SUCH_TABLE, SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND); + mysqlToSql99State.put(MysqlErrorNumbers.ER_NO_SUCH_INDEX, SQL_STATE_ER_NO_SUCH_INDEX); + mysqlToSql99State.put(MysqlErrorNumbers.ER_DUP_FIELDNAME, SQL_STATE_ER_DUP_FIELDNAME); + mysqlToSql99State.put(MysqlErrorNumbers.ER_BAD_FIELD_ERROR, SQL_STATE_ER_BAD_FIELD_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_ILLEGAL_REFERENCE, SQL_STATE_ER_BAD_FIELD_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_QUERY_INTERRUPTED, SQL_STATE_ER_QUERY_INTERRUPTED); + mysqlToSql99State.put(MysqlErrorNumbers.ER_OUTOFMEMORY, SQL_STATE_MEMORY_ALLOCATION_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_OUT_OF_SORTMEMORY, SQL_STATE_MEMORY_ALLOCATION_ERROR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XA_RBROLLBACK, SQL_STATE_XA_RBROLLBACK); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XA_RBDEADLOCK, SQL_STATE_XA_RBDEADLOCK); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XA_RBTIMEOUT, SQL_STATE_XA_RBTIMEOUT); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XA_RMERR, SQL_STATE_XA_RMERR); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XAER_NOTA, SQL_STATE_XAER_NOTA); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XAER_INVAL, SQL_STATE_XAER_INVAL); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XAER_RMFAIL, SQL_STATE_XAER_RMFAIL); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XAER_DUPID, SQL_STATE_XAER_DUPID); + mysqlToSql99State.put(MysqlErrorNumbers.ER_XAER_OUTSIDE, SQL_STATE_XAER_OUTSIDE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_LOCK_WAIT_TIMEOUT, SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE); + mysqlToSql99State.put(MysqlErrorNumbers.ER_LOCK_DEADLOCK, SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE); + } + + public static void dumpSqlStatesMappingsAsXml() throws Exception { + TreeMap allErrorNumbers = new TreeMap<>(); + Map mysqlErrorNumbersToNames = new HashMap<>(); + + // Integer errorNumber = null; + + // + // First create a list of all 'known' error numbers that are mapped. + // + for (Integer errorNumber : mysqlToSql99State.keySet()) { + allErrorNumbers.put(errorNumber, errorNumber); + } + + // + // Now create a list of the actual MySQL error numbers we know about + // + java.lang.reflect.Field[] possibleFields = MysqlErrorNumbers.class.getDeclaredFields(); + + for (int i = 0; i < possibleFields.length; i++) { + String fieldName = possibleFields[i].getName(); + + if (fieldName.startsWith("ER_")) { + mysqlErrorNumbersToNames.put(possibleFields[i].get(null), fieldName); + } + } + + System.out.println(""); + + for (Integer errorNumber : allErrorNumbers.keySet()) { + String sql92State = mysqlToSql99(errorNumber.intValue()); + + System.out.println(" "); + } + + System.out.println(""); + } + + public static String get(String stateCode) { + return sqlStateMessages.get(stateCode); + } + + private static String mysqlToSql99(int errno) { + Integer err = Integer.valueOf(errno); + + if (mysqlToSql99State.containsKey(err)) { + return mysqlToSql99State.get(err); + } + + return SQL_STATE_CLI_SPECIFIC_CONDITION; + } + + /** + * Map MySQL error codes to SQL-99 error codes + * + * @param errno + * the MySQL error code + * + * @return the corresponding SQL-99 error code + */ + public static String mysqlToSqlState(int errno) { + return mysqlToSql99(errno); + } + + private MysqlErrorNumbers() { + // prevent instantiation + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/NumberOutOfRange.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/NumberOutOfRange.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/NumberOutOfRange.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +/** + * Indicates that a number was out of the expected range. + */ +public class NumberOutOfRange extends DataReadException { + private static final long serialVersionUID = -61091413023651438L; + + public NumberOutOfRange(String msg) { + super(msg); + setSQLState("22003"); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/OperationCancelledException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/OperationCancelledException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/OperationCancelledException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +import com.mysql.cj.Messages; + +public class OperationCancelledException extends CJException { + + private static final long serialVersionUID = 9001418688349454695L; + + public OperationCancelledException() { + super(Messages.getString("MySQLStatementCancelledException.0")); + } + + public OperationCancelledException(String message) { + super(message); + } + + public OperationCancelledException(Throwable cause) { + super(cause); + } + + public OperationCancelledException(String message, Throwable cause) { + super(message, cause); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/PasswordExpiredException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/PasswordExpiredException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/PasswordExpiredException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +/** + * Equivalent to SQLSTATE ER_MUST_CHANGE_PASSWORD = 1820 + * "You must SET PASSWORD before executing this statement" + * + * Server entered to sandbox morde when this failure happens. + */ +public class PasswordExpiredException extends CJException { + + private static final long serialVersionUID = -3807215681364413250L; + + public PasswordExpiredException() { + super(); + setVendorCode(MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD); + } + + public PasswordExpiredException(String message) { + super(message); + setVendorCode(MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD); + } + + public PasswordExpiredException(String message, Throwable cause) { + super(message, cause); + setVendorCode(MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD); + } + + public PasswordExpiredException(Throwable cause) { + super(cause); + setVendorCode(MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD); + } + + protected PasswordExpiredException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + setVendorCode(MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/PropertyNotModifiableException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/PropertyNotModifiableException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/PropertyNotModifiableException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +import com.mysql.cj.conf.ModifiableProperty; +import com.mysql.cj.conf.ReadableProperty; + +/** + * {@link ModifiableProperty} attempted on {@link ReadableProperty} + */ +public class PropertyNotModifiableException extends CJException { + + private static final long serialVersionUID = -8001652264426656450L; + + public PropertyNotModifiableException() { + super(); + } + + public PropertyNotModifiableException(String message) { + super(message); + } + + public PropertyNotModifiableException(String message, Throwable cause) { + super(message, cause); + } + + public PropertyNotModifiableException(Throwable cause) { + super(cause); + } + + protected PropertyNotModifiableException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/RSAException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/RSAException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/RSAException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +public class RSAException extends CJException { + + private static final long serialVersionUID = -1878681511263159173L; + + public RSAException() { + super(); + } + + public RSAException(String message) { + super(message); + } + + public RSAException(String message, Throwable cause) { + super(message, cause); + } + + public RSAException(Throwable cause) { + super(cause); + } + + protected RSAException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/SSLParamsException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/SSLParamsException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/SSLParamsException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +public class SSLParamsException extends CJException { + + private static final long serialVersionUID = -6597843374954727858L; + + public SSLParamsException() { + super(); + setSQLState("08000"); + } + + public SSLParamsException(String message) { + super(message); + setSQLState("08000"); + } + + public SSLParamsException(String message, Throwable cause) { + super(message, cause); + setSQLState("08000"); + } + + public SSLParamsException(Throwable cause) { + super(cause); + setSQLState("08000"); + } + + public SSLParamsException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + setSQLState("08000"); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/StatementIsClosedException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/StatementIsClosedException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/StatementIsClosedException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +/** + * Operation attempted on already closed Statement + */ +public class StatementIsClosedException extends CJException { + + private static final long serialVersionUID = -4214028635985851906L; + + public StatementIsClosedException() { + super(); + setSQLState("S1009"); + } + + public StatementIsClosedException(String message) { + super(message); + setSQLState("S1009"); + } + + public StatementIsClosedException(String message, Throwable cause) { + super(message, cause); + setSQLState("S1009"); + } + + public StatementIsClosedException(Throwable cause) { + super(cause); + setSQLState("S1009"); + } + + protected StatementIsClosedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + setSQLState("S1009"); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/StreamingNotifiable.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/StreamingNotifiable.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/StreamingNotifiable.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +public interface StreamingNotifiable { + + void setWasStreamingResults(); + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/UnableToConnectException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/UnableToConnectException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/UnableToConnectException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +public class UnableToConnectException extends CJException { + + private static final long serialVersionUID = 6824175447292574109L; + + public UnableToConnectException() { + super(); + setSQLState("08001"); + } + + public UnableToConnectException(String message) { + super(message); + setSQLState("08001"); + } + + public UnableToConnectException(String message, Throwable cause) { + super(message, cause); + setSQLState("08001"); + } + + public UnableToConnectException(Throwable cause) { + super(cause); + setSQLState("08001"); + } + + public UnableToConnectException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + setSQLState("08001"); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/WrongArgumentException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/WrongArgumentException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/exceptions/WrongArgumentException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.exceptions; + +public class WrongArgumentException extends CJException { + + private static final long serialVersionUID = 3991597077197801820L; + + public WrongArgumentException() { + super(); + setSQLState("S1009"); + } + + public WrongArgumentException(String message) { + super(message); + setSQLState("S1009"); + } + + public WrongArgumentException(String message, Throwable cause) { + super(message, cause); + setSQLState("S1009"); + } + + public WrongArgumentException(Throwable cause) { + super(cause); + setSQLState("S1009"); + } + + public WrongArgumentException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + setSQLState("S1009"); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/interceptors/QueryInterceptor.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/interceptors/QueryInterceptor.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/interceptors/QueryInterceptor.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.interceptors; + +import java.util.Properties; +import java.util.function.Supplier; + +import com.mysql.cj.MysqlConnection; +import com.mysql.cj.Query; +import com.mysql.cj.log.Log; +import com.mysql.cj.protocol.Message; +import com.mysql.cj.protocol.Resultset; +import com.mysql.cj.protocol.ServerSession; + +/** + * Implement this interface to be placed "in between" query execution, so that you can influence it. + * + * QueryInterceptors are "chainable" when configured by the user, the results returned by the "current" interceptor will be passed on to the next on in the + * chain, from left-to-right order, as specified by the user in the driver configuration property "queryInterceptors". + */ +public interface QueryInterceptor { + + /** + * Called once per connection that wants to use the interceptor + * + * The properties are the same ones passed in in the URL or arguments to + * Driver.connect() or DriverManager.getConnection(). + * + * @param conn + * the connection for which this interceptor is being created + * @param props + * configuration values as passed to the connection. Note that + * in order to support javax.sql.DataSources, configuration properties specific + * to an interceptor must be passed via setURL() on the + * DataSource. QueryInterceptor properties are not exposed via + * accessor/mutator methods on DataSources. + * @param log + * logger + * @return {@link QueryInterceptor} + */ + QueryInterceptor init(MysqlConnection conn, Properties props, Log log); + + /** + * Called before the given query is going to be sent to the server for processing. + * + * Interceptors are free to return a result set (which must implement the + * interface {@link Resultset}), and if so, + * the server will not execute the query, and the given result set will be + * returned to the application instead. + * + * This method will be called while the connection-level mutex is held, so + * it will only be called from one thread at a time. + * + * @param sql + * the Supplier for SQL representation of the query + * @param interceptedQuery + * the actual {@link Query} instance being intercepted + * @param + * {@link Resultset} object + * + * @return a {@link Resultset} that should be returned to the application instead + * of results that are created from actual execution of the intercepted + * query. + */ + T preProcess(Supplier sql, Query interceptedQuery); + + /** + * Called before the given query packet is going to be sent to the server for processing. + * + * Interceptors are free to return a PacketPayload, and if so, + * the server will not execute the query, and the given PacketPayload will be + * returned to the application instead. + * + * This method will be called while the connection-level mutex is held, so + * it will only be called from one thread at a time. + * + * @param queryPacket + * original {@link Message} + * @param + * {@link Message} object + * @return processed {@link Message} + */ + default M preProcess(M queryPacket) { + return null; + } + + /** + * Should the driver execute this interceptor only for the + * "original" top-level query, and not put it in the execution + * path for queries that may be executed from other interceptors? + * + * If an interceptor issues queries using the connection it was created for, + * and does not return true for this method, it must ensure + * that it does not cause infinite recursion. + * + * @return true if the driver should ensure that this interceptor is only + * executed for the top-level "original" query. + */ + boolean executeTopLevelOnly(); + + /** + * Called by the driver when this extension should release any resources + * it is holding and cleanup internally before the connection is + * closed. + */ + void destroy(); + + /** + * Called after the given query has been sent to the server for processing. + * + * Interceptors are free to inspect the "original" result set, and if a + * different result set is returned by the interceptor, it is used in place + * of the "original" result set. + * + * This method will be called while the connection-level mutex is held, so + * it will only be called from one thread at a time. + * + * @param sql + * the Supplier for SQL representation of the query + * @param interceptedQuery + * the actual {@link Query} instance being intercepted + * @param originalResultSet + * a {@link Resultset} created from query execution + * @param serverSession + * {@link ServerSession} object after the query execution + * @param + * {@link Resultset} object + * + * @return a {@link Resultset} that should be returned to the application instead + * of results that are created from actual execution of the intercepted + * query. + */ + T postProcess(Supplier sql, Query interceptedQuery, T originalResultSet, ServerSession serverSession); + + /** + * Called after the given query packet has been sent to the server for processing. + * + * Interceptors are free to return either a different PacketPayload than the originalResponsePacket or null. + * + * This method will be called while the connection-level mutex is held, so + * it will only be called from one thread at a time. + * + * @param queryPacket + * query {@link Message} + * @param originalResponsePacket + * response {@link Message} + * @param + * {@link Message} object + * @return {@link Message} + */ + default M postProcess(M queryPacket, M originalResponsePacket) { + return null; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/AbandonedConnectionCleanupThread.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/AbandonedConnectionCleanupThread.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/AbandonedConnectionCleanupThread.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.lang.ref.Reference; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +import com.mysql.cj.jdbc.NonRegisteringDriver.ConnectionPhantomReference; + +/** + * This class implements a thread that is responsible for closing abandoned MySQL connections, i.e., connections that are not explicitly closed. + * There is only one instance of this class and there is a single thread to do this task. This thread's executor is statically referenced in this same class. + */ +public class AbandonedConnectionCleanupThread implements Runnable { + private static final ExecutorService cleanupThreadExcecutorService; + static Thread threadRef = null; + + static { + cleanupThreadExcecutorService = Executors.newSingleThreadExecutor(new ThreadFactory() { + public Thread newThread(Runnable r) { + Thread t = new Thread(r, "Abandoned connection cleanup thread"); + t.setDaemon(true); + // Tie the thread's context ClassLoader to the ClassLoader that loaded the class instead of inheriting the context ClassLoader from the current + // thread, which would happen by default. + // Application servers may use this information if they attempt to shutdown this thread. By leaving the default context ClassLoader this thread + // could end up being shut down even when it is shared by other applications and, being it statically initialized, thus, never restarted again. + t.setContextClassLoader(AbandonedConnectionCleanupThread.class.getClassLoader()); + return threadRef = t; + } + }); + cleanupThreadExcecutorService.execute(new AbandonedConnectionCleanupThread()); + } + + private AbandonedConnectionCleanupThread() { + } + + public void run() { + for (;;) { + try { + checkContextClassLoaders(); + Reference ref = NonRegisteringDriver.refQueue.remove(5000); + if (ref != null) { + try { + ((ConnectionPhantomReference) ref).cleanup(); + } finally { + NonRegisteringDriver.connectionPhantomRefs.remove(ref); + } + } + + } catch (InterruptedException e) { + threadRef = null; + return; + + } catch (Exception ex) { + // Nowhere to really log this. + } + } + } + + /** + * Checks if the thread's context ClassLoader is active. This is usually true but some application managers implement a life-cycle mechanism in their + * ClassLoaders that is linked to the corresponding application's life-cycle. As such, a stopped/ended application will have a ClassLoader unable to load + * anything and, eventually, they throw an exception when trying to do so. When this happens, this thread has no point in being alive anymore. + */ + private void checkContextClassLoaders() { + try { + threadRef.getContextClassLoader().getResource(""); + } catch (Throwable e) { + // Shutdown no matter what. + uncheckedShutdown(); + } + } + + /** + * Checks if the context ClassLoaders from this and the caller thread are the same. + * + * @return true if both threads share the same context ClassLoader, false otherwise + */ + private static boolean consistentClassLoaders() { + ClassLoader callerCtxClassLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader threadCtxClassLoader = threadRef.getContextClassLoader(); + return callerCtxClassLoader != null && threadCtxClassLoader != null && callerCtxClassLoader == threadCtxClassLoader; + } + + /** + * Performs a checked shutdown, i.e., the context ClassLoaders from this and the caller thread are checked for consistency prior to performing the shutdown + * operation. + */ + public static void checkedShutdown() { + shutdown(true); + } + + /** + * Performs an unchecked shutdown, i.e., the shutdown is performed independently of the context ClassLoaders from the involved threads. + */ + public static void uncheckedShutdown() { + shutdown(false); + } + + /** + * Shuts down this thread either checking or not the context ClassLoaders from the involved threads. + * + * @param checked + * does a checked shutdown if true, unchecked otherwise + */ + private static void shutdown(boolean checked) { + if (checked && !consistentClassLoaders()) { + // This thread can't be shutdown from the current thread's context ClassLoader. Doing so would most probably prevent from restarting this thread + // later on. An unchecked shutdown can still be done if needed by calling shutdown(false). + return; + } + cleanupThreadExcecutorService.shutdownNow(); + } + + /** + * Shuts down this thread. + * + * @deprecated use {@link #checkedShutdown()} instead. + */ + @Deprecated + public static void shutdown() { + checkedShutdown(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/Blob.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/Blob.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/Blob.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; + +import com.mysql.cj.Constants; +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; +import com.mysql.cj.protocol.OutputStreamWatcher; +import com.mysql.cj.protocol.WatchableOutputStream; +import com.mysql.cj.protocol.WatchableStream; + +/** + * The representation (mapping) in the JavaTM programming language of an SQL BLOB value. An SQL BLOB is a built-in type that stores a Binary Large Object + * as a column value in a row of a database table. The driver implements Blob using an SQL locator(BLOB), which means that a Blob object contains a logical + * pointer to the SQL BLOB data rather than the data itself. A Blob object is valid for the duration of the transaction in which is was created. Methods in + * the interfaces ResultSet, CallableStatement, and PreparedStatement, such as getBlob and setBlob allow a programmer to access an SQL BLOB value. The Blob + * interface provides methods for getting the length of an SQL BLOB (Binary Large Object) value, for materializing a BLOB value on the client, and for + * determining the position of a pattern of bytes within a BLOB value. This class is new in the JDBC 2.0 API. + */ +public class Blob implements java.sql.Blob, OutputStreamWatcher { + + // + // This is a real brain-dead implementation of BLOB. Once I add streamability to the I/O for MySQL this will be more efficiently implemented + // (except for the position() method, ugh). + // + + /** The binary data that makes up this BLOB */ + private byte[] binaryData = null; + private boolean isClosed = false; + private ExceptionInterceptor exceptionInterceptor; + + /** + * Creates a Blob without data + */ + Blob(ExceptionInterceptor exceptionInterceptor) { + setBinaryData(Constants.EMPTY_BYTE_ARRAY); + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * Creates a BLOB encapsulating the given binary data + * + * @param data + */ + public Blob(byte[] data, ExceptionInterceptor exceptionInterceptor) { + setBinaryData(data); + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * Creates an updatable BLOB that can update in-place (not implemented yet). + * + * @param data + * @param creatorResultSetToSet + * @param columnIndexToSet + */ + Blob(byte[] data, ResultSetInternalMethods creatorResultSetToSet, int columnIndexToSet) { + setBinaryData(data); + } + + private synchronized byte[] getBinaryData() { + return this.binaryData; + } + + /** + * Retrieves the BLOB designated by this Blob instance as a stream. + * + * @return this BLOB represented as a binary stream of bytes. + * + * @throws SQLException + * if a database error occurs + */ + public synchronized java.io.InputStream getBinaryStream() throws SQLException { + checkClosed(); + + return new ByteArrayInputStream(getBinaryData()); + } + + /** + * Returns as an array of bytes, part or all of the BLOB value that this + * Blob object designates. + * + * @param pos + * where to start the part of the BLOB + * @param length + * the length of the part of the BLOB you want returned. + * + * @return the bytes stored in the blob starting at position pos and having a length of length. + * + * @throws SQLException + * if a database error occurs + */ + public synchronized byte[] getBytes(long pos, int length) throws SQLException { + checkClosed(); + + if (pos < 1) { + throw SQLError.createSQLException(Messages.getString("Blob.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + pos--; + + if (pos > this.binaryData.length) { + throw SQLError.createSQLException(Messages.getString("Blob.3"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + if (pos + length > this.binaryData.length) { + throw SQLError.createSQLException(Messages.getString("Blob.4"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + byte[] newData = new byte[length]; + System.arraycopy(getBinaryData(), (int) (pos), newData, 0, length); + + return newData; + } + + /** + * Returns the number of bytes in the BLOB value designated by this Blob + * object. + * + * @return the length of this blob + * + * @throws SQLException + * if a database error occurs + */ + public synchronized long length() throws SQLException { + checkClosed(); + + return getBinaryData().length; + } + + /** + * @see java.sql.Blob#position(byte[], long) + */ + public synchronized long position(byte[] pattern, long start) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * Finds the position of the given pattern in this BLOB. + * + * @param pattern + * the pattern to find + * @param start + * where to start finding the pattern + * + * @return the position where the pattern is found in the BLOB, -1 if not + * found + * + * @throws SQLException + * if a database error occurs + */ + public synchronized long position(java.sql.Blob pattern, long start) throws SQLException { + checkClosed(); + + return position(pattern.getBytes(0, (int) pattern.length()), start); + } + + private synchronized void setBinaryData(byte[] newBinaryData) { + this.binaryData = newBinaryData; + } + + /** + * @see Blob#setBinaryStream(long) + */ + public synchronized OutputStream setBinaryStream(long indexToWriteAt) throws SQLException { + checkClosed(); + + if (indexToWriteAt < 1) { + throw SQLError.createSQLException(Messages.getString("Blob.0"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + WatchableOutputStream bytesOut = new WatchableOutputStream(); + bytesOut.setWatcher(this); + + if (indexToWriteAt > 0) { + bytesOut.write(this.binaryData, 0, (int) (indexToWriteAt - 1)); + } + + return bytesOut; + } + + /** + * @see Blob#setBytes(long, byte[]) + */ + public synchronized int setBytes(long writeAt, byte[] bytes) throws SQLException { + checkClosed(); + + return setBytes(writeAt, bytes, 0, bytes.length); + } + + /** + * @see Blob#setBytes(long, byte[], int, int) + */ + public synchronized int setBytes(long writeAt, byte[] bytes, int offset, int length) throws SQLException { + checkClosed(); + + OutputStream bytesOut = setBinaryStream(writeAt); + + try { + bytesOut.write(bytes, offset, length); + } catch (IOException ioEx) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("Blob.1"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + sqlEx.initCause(ioEx); + + throw sqlEx; + } finally { + try { + bytesOut.close(); + } catch (IOException doNothing) { + // do nothing + } + } + + return length; + } + + public synchronized void streamClosed(byte[] byteData) { + this.binaryData = byteData; + } + + public synchronized void streamClosed(WatchableStream out) { + int streamSize = out.size(); + + if (streamSize < this.binaryData.length) { + out.write(this.binaryData, streamSize, this.binaryData.length - streamSize); + } + + this.binaryData = out.toByteArray(); + } + + /** + * Truncates the BLOB value that this Blob object represents to be len bytes in length. + *

+ * Note: If the value specified for len is greater then the length+1 of the BLOB value then the behavior is undefined. Some + * JDBC drivers may throw a SQLException while other drivers may support this operation. + * + * @param len + * the length, in bytes, to which the BLOB value + * that this Blob object represents should be truncated + * @exception SQLException + * if there is an error accessing the BLOB value or if len is less than 0 + * @exception SQLFeatureNotSupportedException + * if the JDBC driver does not support + * this method + * @since 1.4 + */ + public synchronized void truncate(long len) throws SQLException { + checkClosed(); + + if (len < 0) { + throw SQLError.createSQLException(Messages.getString("Blob.5"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + if (len > this.binaryData.length) { + throw SQLError.createSQLException(Messages.getString("Blob.6"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + // TODO: Do this without copying byte[]s by maintaining some end pointer on the original data + + byte[] newData = new byte[(int) len]; + System.arraycopy(getBinaryData(), 0, newData, 0, (int) len); + this.binaryData = newData; + } + + /** + * This method frees the Blob object and releases the resources that + * it holds. The object is invalid once the free method is called. + *

+ * After free has been called, any attempt to invoke a method other than free will result in a SQLException being + * thrown. If free is called multiple times, the subsequent calls to free are treated as a no-op. + *

+ * + * @throws SQLException + * if an error occurs releasing + * the Blob's resources + * @exception SQLFeatureNotSupportedException + * if the JDBC driver does not support + * this method + * @since 1.6 + */ + + public synchronized void free() throws SQLException { + this.binaryData = null; + this.isClosed = true; + } + + /** + * Returns an InputStream object that contains a partial Blob value, + * starting with the byte specified by pos, which is length bytes in length. + * + * @param pos + * the offset to the first byte of the partial value to be retrieved. + * The first byte in the Blob is at position 1 + * @param length + * the length in bytes of the partial value to be retrieved + * @return InputStream through which the partial Blob value can be read. + * @throws SQLException + * if pos is less than 1 or if pos is greater than the number of bytes + * in the Blob or if pos + length is greater than the number of bytes + * in the Blob + * + * @exception SQLFeatureNotSupportedException + * if the JDBC driver does not support + * this method + * @since 1.6 + */ + public synchronized InputStream getBinaryStream(long pos, long length) throws SQLException { + checkClosed(); + + if (pos < 1) { + throw SQLError.createSQLException(Messages.getString("Blob.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + pos--; + + if (pos > this.binaryData.length) { + throw SQLError.createSQLException(Messages.getString("Blob.6"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + if (pos + length > this.binaryData.length) { + throw SQLError.createSQLException(Messages.getString("Blob.4"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + return new ByteArrayInputStream(getBinaryData(), (int) pos, (int) length); + } + + private synchronized void checkClosed() throws SQLException { + if (this.isClosed) { + throw SQLError.createSQLException(Messages.getString("Blob.7"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/BlobFromLocator.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/BlobFromLocator.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/BlobFromLocator.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,664 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import com.mysql.cj.Messages; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.result.ResultSetImpl; +import com.mysql.cj.result.Field; + +/** + * The representation (mapping) in the JavaTM programming language of an SQL BLOB value. An SQL BLOB is a built-in type that stores a Binary Large Object + * as a column value in a row of a database table. The driver implements Blob using an SQL locator(BLOB), which means that a Blob object contains a logical + * pointer to the SQL BLOB data rather than the data itself. A Blob object is valid for the duration of the transaction in which is was created. Methods in + * the interfaces ResultSet, CallableStatement, and PreparedStatement, such as getBlob and setBlob allow a programmer to access an SQL BLOB value. The Blob + * interface provides methods for getting the length of an SQL BLOB (Binary Large Object) value, for materializing a BLOB value on the client, and for + * determining the position of a pattern of bytes within a BLOB value. This class is new in the JDBC 2.0 API. + */ +public class BlobFromLocator implements java.sql.Blob { + private List primaryKeyColumns = null; + + private List primaryKeyValues = null; + + /** The ResultSet that created this BLOB */ + private ResultSetImpl creatorResultSet; + + private String blobColumnName = null; + + private String tableName = null; + + private int numColsInResultSet = 0; + + private int numPrimaryKeys = 0; + + private String quotedId; + + private ExceptionInterceptor exceptionInterceptor; + + /** + * Creates an updatable BLOB that can update in-place + */ + public BlobFromLocator(ResultSetImpl creatorResultSetToSet, int blobColumnIndex, ExceptionInterceptor exceptionInterceptor) throws SQLException { + this.exceptionInterceptor = exceptionInterceptor; + this.creatorResultSet = creatorResultSetToSet; + + Field[] fields = this.creatorResultSet.getMetadata().getFields(); + this.numColsInResultSet = fields.length; + this.quotedId = this.creatorResultSet.getSession().getIdentifierQuoteString(); + + if (this.numColsInResultSet > 1) { + this.primaryKeyColumns = new ArrayList<>(); + this.primaryKeyValues = new ArrayList<>(); + + for (int i = 0; i < this.numColsInResultSet; i++) { + if (fields[i].isPrimaryKey()) { + StringBuilder keyName = new StringBuilder(); + keyName.append(this.quotedId); + + String originalColumnName = fields[i].getOriginalName(); + + if ((originalColumnName != null) && (originalColumnName.length() > 0)) { + keyName.append(originalColumnName); + } else { + keyName.append(fields[i].getName()); + } + + keyName.append(this.quotedId); + + this.primaryKeyColumns.add(keyName.toString()); + this.primaryKeyValues.add(this.creatorResultSet.getString(i + 1)); + } + } + } else { + notEnoughInformationInQuery(); + } + + this.numPrimaryKeys = this.primaryKeyColumns.size(); + + if (this.numPrimaryKeys == 0) { + notEnoughInformationInQuery(); + } + + if (fields[0].getOriginalTableName() != null) { + StringBuilder tableNameBuffer = new StringBuilder(); + + String databaseName = fields[0].getDatabaseName(); + + if ((databaseName != null) && (databaseName.length() > 0)) { + tableNameBuffer.append(this.quotedId); + tableNameBuffer.append(databaseName); + tableNameBuffer.append(this.quotedId); + tableNameBuffer.append('.'); + } + + tableNameBuffer.append(this.quotedId); + tableNameBuffer.append(fields[0].getOriginalTableName()); + tableNameBuffer.append(this.quotedId); + + this.tableName = tableNameBuffer.toString(); + } else { + StringBuilder tableNameBuffer = new StringBuilder(); + + tableNameBuffer.append(this.quotedId); + tableNameBuffer.append(fields[0].getTableName()); + tableNameBuffer.append(this.quotedId); + + this.tableName = tableNameBuffer.toString(); + } + + this.blobColumnName = this.quotedId + this.creatorResultSet.getString(blobColumnIndex) + this.quotedId; + } + + private void notEnoughInformationInQuery() throws SQLException { + throw SQLError.createSQLException(Messages.getString("Blob.8"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + + /** + * @see Blob#setBinaryStream(long) + */ + public OutputStream setBinaryStream(long indexToWriteAt) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * Retrieves the BLOB designated by this Blob instance as a stream. + * + * @return this BLOB represented as a binary stream of bytes. + * + * @throws SQLException + * if a database error occurs + */ + public java.io.InputStream getBinaryStream() throws SQLException { + // TODO: Make fetch size configurable + return new BufferedInputStream(new LocatorInputStream(), + this.creatorResultSet.getSession().getPropertySet().getMemorySizeReadableProperty(PropertyDefinitions.PNAME_locatorFetchBufferSize).getValue()); + } + + /** + * @see Blob#setBytes(long, byte[], int, int) + */ + public int setBytes(long writeAt, byte[] bytes, int offset, int length) throws SQLException { + java.sql.PreparedStatement pStmt = null; + + if ((offset + length) > bytes.length) { + length = bytes.length - offset; + } + + byte[] bytesToWrite = new byte[length]; + System.arraycopy(bytes, offset, bytesToWrite, 0, length); + + // FIXME: Needs to use identifiers for column/table names + StringBuilder query = new StringBuilder("UPDATE "); + query.append(this.tableName); + query.append(" SET "); + query.append(this.blobColumnName); + query.append(" = INSERT("); + query.append(this.blobColumnName); + query.append(", "); + query.append(writeAt); + query.append(", "); + query.append(length); + query.append(", ?) WHERE "); + + query.append(this.primaryKeyColumns.get(0)); + query.append(" = ?"); + + for (int i = 1; i < this.numPrimaryKeys; i++) { + query.append(" AND "); + query.append(this.primaryKeyColumns.get(i)); + query.append(" = ?"); + } + + try { + // FIXME: Have this passed in instead + pStmt = this.creatorResultSet.getConnection().prepareStatement(query.toString()); + + pStmt.setBytes(1, bytesToWrite); + + for (int i = 0; i < this.numPrimaryKeys; i++) { + pStmt.setString(i + 2, this.primaryKeyValues.get(i)); + } + + int rowsUpdated = pStmt.executeUpdate(); + + if (rowsUpdated != 1) { + throw SQLError.createSQLException(Messages.getString("Blob.9"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } finally { + if (pStmt != null) { + try { + pStmt.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + pStmt = null; + } + } + + return (int) length(); + } + + /** + * @see Blob#setBytes(long, byte[]) + */ + public int setBytes(long writeAt, byte[] bytes) throws SQLException { + return setBytes(writeAt, bytes, 0, bytes.length); + } + + /** + * Returns as an array of bytes, part or all of the BLOB value that this + * Blob object designates. + * + * @param pos + * where to start the part of the BLOB + * @param length + * the length of the part of the BLOB you want returned. + * + * @return the bytes stored in the blob starting at position pos and having a length of length. + * + * @throws SQLException + * if a database error occurs + */ + public byte[] getBytes(long pos, int length) throws SQLException { + java.sql.PreparedStatement pStmt = null; + + try { + + pStmt = createGetBytesStatement(); + + return getBytesInternal(pStmt, pos, length); + } finally { + if (pStmt != null) { + try { + pStmt.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + pStmt = null; + } + } + } + + /** + * Returns the number of bytes in the BLOB value designated by this Blob + * object. + * + * @return the length of this blob + * + * @throws SQLException + * if a database error occurs + */ + public long length() throws SQLException { + java.sql.ResultSet blobRs = null; + java.sql.PreparedStatement pStmt = null; + + // FIXME: Needs to use identifiers for column/table names + StringBuilder query = new StringBuilder("SELECT LENGTH("); + query.append(this.blobColumnName); + query.append(") FROM "); + query.append(this.tableName); + query.append(" WHERE "); + + query.append(this.primaryKeyColumns.get(0)); + query.append(" = ?"); + + for (int i = 1; i < this.numPrimaryKeys; i++) { + query.append(" AND "); + query.append(this.primaryKeyColumns.get(i)); + query.append(" = ?"); + } + + try { + // FIXME: Have this passed in instead + pStmt = this.creatorResultSet.getConnection().prepareStatement(query.toString()); + + for (int i = 0; i < this.numPrimaryKeys; i++) { + pStmt.setString(i + 1, this.primaryKeyValues.get(i)); + } + + blobRs = pStmt.executeQuery(); + + if (blobRs.next()) { + return blobRs.getLong(1); + } + + throw SQLError.createSQLException(Messages.getString("Blob.9"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } finally { + if (blobRs != null) { + try { + blobRs.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + blobRs = null; + } + + if (pStmt != null) { + try { + pStmt.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + pStmt = null; + } + } + } + + /** + * Finds the position of the given pattern in this BLOB. + * + * @param pattern + * the pattern to find + * @param start + * where to start finding the pattern + * + * @return the position where the pattern is found in the BLOB, -1 if not + * found + * + * @throws SQLException + * if a database error occurs + */ + public long position(java.sql.Blob pattern, long start) throws SQLException { + return position(pattern.getBytes(0, (int) pattern.length()), start); + } + + /** + * @see java.sql.Blob#position(byte[], long) + */ + public long position(byte[] pattern, long start) throws SQLException { + java.sql.ResultSet blobRs = null; + java.sql.PreparedStatement pStmt = null; + + // FIXME: Needs to use identifiers for column/table names + StringBuilder query = new StringBuilder("SELECT LOCATE("); + query.append("?, "); + query.append(this.blobColumnName); + query.append(", "); + query.append(start); + query.append(") FROM "); + query.append(this.tableName); + query.append(" WHERE "); + + query.append(this.primaryKeyColumns.get(0)); + query.append(" = ?"); + + for (int i = 1; i < this.numPrimaryKeys; i++) { + query.append(" AND "); + query.append(this.primaryKeyColumns.get(i)); + query.append(" = ?"); + } + + try { + // FIXME: Have this passed in instead + pStmt = this.creatorResultSet.getConnection().prepareStatement(query.toString()); + pStmt.setBytes(1, pattern); + + for (int i = 0; i < this.numPrimaryKeys; i++) { + pStmt.setString(i + 2, this.primaryKeyValues.get(i)); + } + + blobRs = pStmt.executeQuery(); + + if (blobRs.next()) { + return blobRs.getLong(1); + } + + throw SQLError.createSQLException(Messages.getString("Blob.9"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } finally { + if (blobRs != null) { + try { + blobRs.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + blobRs = null; + } + + if (pStmt != null) { + try { + pStmt.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + pStmt = null; + } + } + } + + /** + * @see Blob#truncate(long) + */ + public void truncate(long length) throws SQLException { + java.sql.PreparedStatement pStmt = null; + + // FIXME: Needs to use identifiers for column/table names + StringBuilder query = new StringBuilder("UPDATE "); + query.append(this.tableName); + query.append(" SET "); + query.append(this.blobColumnName); + query.append(" = LEFT("); + query.append(this.blobColumnName); + query.append(", "); + query.append(length); + query.append(") WHERE "); + + query.append(this.primaryKeyColumns.get(0)); + query.append(" = ?"); + + for (int i = 1; i < this.numPrimaryKeys; i++) { + query.append(" AND "); + query.append(this.primaryKeyColumns.get(i)); + query.append(" = ?"); + } + + try { + // FIXME: Have this passed in instead + pStmt = this.creatorResultSet.getConnection().prepareStatement(query.toString()); + + for (int i = 0; i < this.numPrimaryKeys; i++) { + pStmt.setString(i + 1, this.primaryKeyValues.get(i)); + } + + int rowsUpdated = pStmt.executeUpdate(); + + if (rowsUpdated != 1) { + throw SQLError.createSQLException(Messages.getString("Blob.9"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } + } finally { + if (pStmt != null) { + try { + pStmt.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + pStmt = null; + } + } + } + + java.sql.PreparedStatement createGetBytesStatement() throws SQLException { + StringBuilder query = new StringBuilder("SELECT SUBSTRING("); + + query.append(this.blobColumnName); + query.append(", "); + query.append("?"); + query.append(", "); + query.append("?"); + query.append(") FROM "); + query.append(this.tableName); + query.append(" WHERE "); + + query.append(this.primaryKeyColumns.get(0)); + query.append(" = ?"); + + for (int i = 1; i < this.numPrimaryKeys; i++) { + query.append(" AND "); + query.append(this.primaryKeyColumns.get(i)); + query.append(" = ?"); + } + + return this.creatorResultSet.getConnection().prepareStatement(query.toString()); + } + + byte[] getBytesInternal(java.sql.PreparedStatement pStmt, long pos, int length) throws SQLException { + + java.sql.ResultSet blobRs = null; + + try { + + pStmt.setLong(1, pos); + pStmt.setInt(2, length); + + for (int i = 0; i < this.numPrimaryKeys; i++) { + pStmt.setString(i + 3, this.primaryKeyValues.get(i)); + } + + blobRs = pStmt.executeQuery(); + + if (blobRs.next()) { + return ((com.mysql.cj.jdbc.result.ResultSetImpl) blobRs).getBytes(1); + } + + throw SQLError.createSQLException(Messages.getString("Blob.9"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, this.exceptionInterceptor); + } finally { + if (blobRs != null) { + try { + blobRs.close(); + } catch (SQLException sqlEx) { + // do nothing + } + + blobRs = null; + } + } + } + + class LocatorInputStream extends InputStream { + long currentPositionInBlob = 0; + + long length = 0; + + java.sql.PreparedStatement pStmt = null; + + LocatorInputStream() throws SQLException { + this.length = length(); + this.pStmt = createGetBytesStatement(); + } + + @SuppressWarnings("synthetic-access") + LocatorInputStream(long pos, long len) throws SQLException { + this.length = pos + len; + this.currentPositionInBlob = pos; + long blobLength = length(); + + if (pos + len > blobLength) { + throw SQLError.createSQLException( + Messages.getString("Blob.invalidStreamLength", new Object[] { Long.valueOf(blobLength), Long.valueOf(pos), Long.valueOf(len) }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, BlobFromLocator.this.exceptionInterceptor); + } + + if (pos < 1) { + throw SQLError.createSQLException(Messages.getString("Blob.invalidStreamPos"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + BlobFromLocator.this.exceptionInterceptor); + } + + if (pos > blobLength) { + throw SQLError.createSQLException(Messages.getString("Blob.invalidStreamPos"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + BlobFromLocator.this.exceptionInterceptor); + } + } + + @Override + public int read() throws IOException { + if (this.currentPositionInBlob + 1 > this.length) { + return -1; + } + + try { + byte[] asBytes = getBytesInternal(this.pStmt, (this.currentPositionInBlob++) + 1, 1); + + if (asBytes == null) { + return -1; + } + + return asBytes[0]; + } catch (SQLException sqlEx) { + throw new IOException(sqlEx.toString()); + } + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (this.currentPositionInBlob + 1 > this.length) { + return -1; + } + + try { + byte[] asBytes = getBytesInternal(this.pStmt, (this.currentPositionInBlob) + 1, len); + + if (asBytes == null) { + return -1; + } + + System.arraycopy(asBytes, 0, b, off, asBytes.length); + + this.currentPositionInBlob += asBytes.length; + + return asBytes.length; + } catch (SQLException sqlEx) { + throw new IOException(sqlEx.toString()); + } + } + + @Override + public int read(byte[] b) throws IOException { + if (this.currentPositionInBlob + 1 > this.length) { + return -1; + } + + try { + byte[] asBytes = getBytesInternal(this.pStmt, (this.currentPositionInBlob) + 1, b.length); + + if (asBytes == null) { + return -1; + } + + System.arraycopy(asBytes, 0, b, 0, asBytes.length); + + this.currentPositionInBlob += asBytes.length; + + return asBytes.length; + } catch (SQLException sqlEx) { + throw new IOException(sqlEx.toString()); + } + } + + @Override + public void close() throws IOException { + if (this.pStmt != null) { + try { + this.pStmt.close(); + } catch (SQLException sqlEx) { + throw new IOException(sqlEx.toString()); + } + } + + super.close(); + } + } + + public void free() throws SQLException { + this.creatorResultSet = null; + this.primaryKeyColumns = null; + this.primaryKeyValues = null; + } + + public InputStream getBinaryStream(long pos, long length) throws SQLException { + return new LocatorInputStream(pos, length); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/CallableStatement.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/CallableStatement.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/CallableStatement.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,2623 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.JDBCType; +import java.sql.NClob; +import java.sql.ParameterMetaData; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLType; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Wrapper; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.mysql.cj.Messages; +import com.mysql.cj.MysqlType; +import com.mysql.cj.PreparedQuery; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.FeatureNotAvailableException; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.result.ResultSetImpl; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; +import com.mysql.cj.protocol.a.result.ByteArrayRow; +import com.mysql.cj.protocol.a.result.ResultsetRowsStatic; +import com.mysql.cj.result.DefaultColumnDefinition; +import com.mysql.cj.result.Field; +import com.mysql.cj.result.Row; +import com.mysql.cj.util.StringUtils; +import com.mysql.cj.util.Util; + +/** + * Representation of stored procedures for JDBC + */ +public class CallableStatement extends ClientPreparedStatement implements java.sql.CallableStatement { + + protected static class CallableStatementParam { + + int index; + + int inOutModifier; + + boolean isIn; + + boolean isOut; + + int jdbcType; + + short nullability; + + String paramName; + + int precision; + + int scale; + + String typeName; + + MysqlType desiredMysqlType = MysqlType.UNKNOWN; + + CallableStatementParam(String name, int idx, boolean in, boolean out, int jdbcType, String typeName, int precision, int scale, short nullability, + int inOutModifier) { + this.paramName = name; + this.isIn = in; + this.isOut = out; + this.index = idx; + + this.jdbcType = jdbcType; + this.typeName = typeName; + this.precision = precision; + this.scale = scale; + this.nullability = nullability; + this.inOutModifier = inOutModifier; + } + + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } + + public class CallableStatementParamInfo implements ParameterMetaData { + String catalogInUse; + + boolean isFunctionCall; + + String nativeSql; + + int numParameters; + + List parameterList; + + Map parameterMap; + + /** + * synchronized externally in checkReadOnlyProcedure() + */ + boolean isReadOnlySafeProcedure = false; + + /** + * synchronized externally in checkReadOnlyProcedure() + */ + boolean isReadOnlySafeChecked = false; + + /** + * Constructor that converts a full list of parameter metadata into one + * that only represents the placeholders present in the {CALL ()}. + * + * @param fullParamInfo + * the metadata for all parameters for this stored + * procedure or function. + */ + CallableStatementParamInfo(CallableStatementParamInfo fullParamInfo) { + this.nativeSql = ((PreparedQuery) CallableStatement.this.query).getOriginalSql(); + this.catalogInUse = CallableStatement.this.getCurrentCatalog(); + this.isFunctionCall = fullParamInfo.isFunctionCall; + @SuppressWarnings("synthetic-access") + int[] localParameterMap = CallableStatement.this.placeholderToParameterIndexMap; + int parameterMapLength = localParameterMap.length; + + this.isReadOnlySafeProcedure = fullParamInfo.isReadOnlySafeProcedure; + this.isReadOnlySafeChecked = fullParamInfo.isReadOnlySafeChecked; + this.parameterList = new ArrayList<>(fullParamInfo.numParameters); + this.parameterMap = new HashMap<>(fullParamInfo.numParameters); + + if (this.isFunctionCall) { + // Take the return value + this.parameterList.add(fullParamInfo.parameterList.get(0)); + } + + int offset = this.isFunctionCall ? 1 : 0; + + for (int i = 0; i < parameterMapLength; i++) { + if (localParameterMap[i] != 0) { + CallableStatementParam param = fullParamInfo.parameterList.get(localParameterMap[i] + offset); + + this.parameterList.add(param); + this.parameterMap.put(param.paramName, param); + } + } + + this.numParameters = this.parameterList.size(); + } + + @SuppressWarnings("synthetic-access") + CallableStatementParamInfo(java.sql.ResultSet paramTypesRs) throws SQLException { + boolean hadRows = paramTypesRs.last(); + + this.nativeSql = ((PreparedQuery) CallableStatement.this.query).getOriginalSql(); + this.catalogInUse = CallableStatement.this.getCurrentCatalog(); + this.isFunctionCall = CallableStatement.this.callingStoredFunction; + + if (hadRows) { + this.numParameters = paramTypesRs.getRow(); + + this.parameterList = new ArrayList<>(this.numParameters); + this.parameterMap = new HashMap<>(this.numParameters); + + paramTypesRs.beforeFirst(); + + addParametersFromDBMD(paramTypesRs); + } else { + this.numParameters = 0; + } + + if (this.isFunctionCall) { + this.numParameters += 1; + } + } + + private void addParametersFromDBMD(java.sql.ResultSet paramTypesRs) throws SQLException { + int i = 0; + + while (paramTypesRs.next()) { + String paramName = paramTypesRs.getString(4); + int inOutModifier; + switch (paramTypesRs.getInt(5)) { + case DatabaseMetaData.procedureColumnIn: + inOutModifier = ParameterMetaData.parameterModeIn; + break; + case DatabaseMetaData.procedureColumnInOut: + inOutModifier = ParameterMetaData.parameterModeInOut; + break; + case DatabaseMetaData.procedureColumnOut: + case DatabaseMetaData.procedureColumnReturn: + inOutModifier = ParameterMetaData.parameterModeOut; + break; + default: + inOutModifier = ParameterMetaData.parameterModeUnknown; + } + + boolean isOutParameter = false; + boolean isInParameter = false; + + if (i == 0 && this.isFunctionCall) { + isOutParameter = true; + isInParameter = false; + } else if (inOutModifier == java.sql.DatabaseMetaData.procedureColumnInOut) { + isOutParameter = true; + isInParameter = true; + } else if (inOutModifier == java.sql.DatabaseMetaData.procedureColumnIn) { + isOutParameter = false; + isInParameter = true; + } else if (inOutModifier == java.sql.DatabaseMetaData.procedureColumnOut) { + isOutParameter = true; + isInParameter = false; + } + + int jdbcType = paramTypesRs.getInt(6); + String typeName = paramTypesRs.getString(7); + int precision = paramTypesRs.getInt(8); + int scale = paramTypesRs.getInt(10); + short nullability = paramTypesRs.getShort(12); + + CallableStatementParam paramInfoToAdd = new CallableStatementParam(paramName, i++, isInParameter, isOutParameter, jdbcType, typeName, precision, + scale, nullability, inOutModifier); + + this.parameterList.add(paramInfoToAdd); + this.parameterMap.put(paramName, paramInfoToAdd); + } + } + + protected void checkBounds(int paramIndex) throws SQLException { + int localParamIndex = paramIndex - 1; + + if ((paramIndex < 0) || (localParamIndex >= this.numParameters)) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.11", new Object[] { paramIndex, this.numParameters }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + CallableStatementParam getParameter(int index) { + return this.parameterList.get(index); + } + + CallableStatementParam getParameter(String name) { + return this.parameterMap.get(name); + } + + public String getParameterClassName(int arg0) throws SQLException { + String mysqlTypeName = getParameterTypeName(arg0); + + MysqlType mysqlType = MysqlType.getByName(mysqlTypeName); + switch (mysqlType) { + case YEAR: + if (!CallableStatement.this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_yearIsDateType).getValue()) { + return Short.class.getName(); + } + // TODO Adjust for pseudo-boolean ? + //if (length == 1) { + // if (propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_transformedBitIsBoolean).getValue()) { + // return MysqlType.BOOLEAN; + // } else if (propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_tinyInt1isBit).getValue()) { + // return MysqlType.BIT; + // } + //} + return mysqlType.getClassName(); + + default: + return mysqlType.getClassName(); + } + + } + + public int getParameterCount() throws SQLException { + if (this.parameterList == null) { + return 0; + } + + return this.parameterList.size(); + } + + public int getParameterMode(int arg0) throws SQLException { + checkBounds(arg0); + + return getParameter(arg0 - 1).inOutModifier; + } + + public int getParameterType(int arg0) throws SQLException { + checkBounds(arg0); + + return getParameter(arg0 - 1).jdbcType; + } + + public String getParameterTypeName(int arg0) throws SQLException { + checkBounds(arg0); + + return getParameter(arg0 - 1).typeName; + } + + public int getPrecision(int arg0) throws SQLException { + checkBounds(arg0); + + return getParameter(arg0 - 1).precision; + } + + public int getScale(int arg0) throws SQLException { + checkBounds(arg0); + + return getParameter(arg0 - 1).scale; + } + + public int isNullable(int arg0) throws SQLException { + checkBounds(arg0); + + return getParameter(arg0 - 1).nullability; + } + + public boolean isSigned(int arg0) throws SQLException { + checkBounds(arg0); + + return false; + } + + Iterator iterator() { + return this.parameterList.iterator(); + } + + int numberOfParameters() { + return this.numParameters; + } + + /** + * @see java.sql.Wrapper#isWrapperFor(Class) + */ + public boolean isWrapperFor(Class iface) throws SQLException { + checkClosed(); + + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + /** + * @see java.sql.Wrapper#unwrap(Class) + */ + public T unwrap(Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("Common.UnableToUnwrap", new Object[] { iface.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + } + + private final static int NOT_OUTPUT_PARAMETER_INDICATOR = Integer.MIN_VALUE; + + private final static String PARAMETER_NAMESPACE_PREFIX = "@com_mysql_jdbc_outparam_"; + + private static String mangleParameterName(String origParameterName) { + if (origParameterName == null) { + return null; + } + + int offset = 0; + + if (origParameterName.length() > 0 && origParameterName.charAt(0) == '@') { + offset = 1; + } + + StringBuilder paramNameBuf = new StringBuilder(PARAMETER_NAMESPACE_PREFIX.length() + origParameterName.length()); + paramNameBuf.append(PARAMETER_NAMESPACE_PREFIX); + paramNameBuf.append(origParameterName.substring(offset)); + + return paramNameBuf.toString(); + } + + private boolean callingStoredFunction = false; + + private ResultSetInternalMethods functionReturnValueResults; + + private boolean hasOutputParams = false; + + private ResultSetInternalMethods outputParameterResults; + + protected boolean outputParamWasNull = false; + + private int[] parameterIndexToRsIndex; + + protected CallableStatementParamInfo paramInfo; + + private CallableStatementParam returnValueParam; + + private boolean noAccessToProcedureBodies; + + /** + * Creates a new CallableStatement + * + * @param conn + * the connection creating this statement + * @param paramInfo + * the SQL to prepare + * + * @throws SQLException + * if an error occurs + */ + public CallableStatement(JdbcConnection conn, CallableStatementParamInfo paramInfo) throws SQLException { + super(conn, paramInfo.nativeSql, paramInfo.catalogInUse); + + this.paramInfo = paramInfo; + this.callingStoredFunction = this.paramInfo.isFunctionCall; + + if (this.callingStoredFunction) { + ((PreparedQuery) this.query).setParameterCount(((PreparedQuery) this.query).getParameterCount() + 1); + } + + this.retrieveGeneratedKeys = true; // not provided for in the JDBC spec + + this.noAccessToProcedureBodies = conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_noAccessToProcedureBodies).getValue(); + } + + /** + * Creates a callable statement instance + */ + + protected static CallableStatement getInstance(JdbcConnection conn, String sql, String catalog, boolean isFunctionCall) throws SQLException { + return new CallableStatement(conn, sql, catalog, isFunctionCall); + } + + /** + * Creates a callable statement instance + */ + + protected static CallableStatement getInstance(JdbcConnection conn, CallableStatementParamInfo paramInfo) throws SQLException { + return new CallableStatement(conn, paramInfo); + } + + private int[] placeholderToParameterIndexMap; + + private void generateParameterMap() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.paramInfo == null) { + return; + } + + // if the user specified some parameters as literals, we need to provide a map from the specified placeholders to the actual parameter numbers + + int parameterCountFromMetaData = this.paramInfo.getParameterCount(); + + // Ignore the first ? if this is a stored function, it doesn't count + + if (this.callingStoredFunction) { + parameterCountFromMetaData--; + } + + PreparedQuery q = ((PreparedQuery) this.query); + if (this.paramInfo != null && q.getParameterCount() != parameterCountFromMetaData) { + this.placeholderToParameterIndexMap = new int[q.getParameterCount()]; + + int startPos = this.callingStoredFunction ? StringUtils.indexOfIgnoreCase(q.getOriginalSql(), "SELECT") + : StringUtils.indexOfIgnoreCase(q.getOriginalSql(), "CALL"); + + if (startPos != -1) { + int parenOpenPos = q.getOriginalSql().indexOf('(', startPos + 4); + + if (parenOpenPos != -1) { + int parenClosePos = StringUtils.indexOfIgnoreCase(parenOpenPos, q.getOriginalSql(), ")", "'", "'", StringUtils.SEARCH_MODE__ALL); + + if (parenClosePos != -1) { + List parsedParameters = StringUtils.split(q.getOriginalSql().substring(parenOpenPos + 1, parenClosePos), ",", "'\"", "'\"", + true); + + int numParsedParameters = parsedParameters.size(); + + // sanity check + + if (numParsedParameters != q.getParameterCount()) { + // bail? + } + + int placeholderCount = 0; + + for (int i = 0; i < numParsedParameters; i++) { + if (((String) parsedParameters.get(i)).equals("?")) { + this.placeholderToParameterIndexMap[placeholderCount++] = i; + } + } + } + } + } + } + } + } + + /** + * Creates a new CallableStatement + * + * @param conn + * the connection creating this statement + * @param sql + * the SQL to prepare + * @param catalog + * the current catalog + * + * @throws SQLException + * if an error occurs + */ + public CallableStatement(JdbcConnection conn, String sql, String catalog, boolean isFunctionCall) throws SQLException { + super(conn, sql, catalog); + + this.callingStoredFunction = isFunctionCall; + + if (!this.callingStoredFunction) { + if (!StringUtils.startsWithIgnoreCaseAndWs(sql, "CALL")) { + // not really a stored procedure call + fakeParameterTypes(false); + } else { + determineParameterTypes(); + } + + generateParameterMap(); + } else { + determineParameterTypes(); + generateParameterMap(); + + ((PreparedQuery) this.query).setParameterCount(((PreparedQuery) this.query).getParameterCount() + 1); + } + + this.retrieveGeneratedKeys = true; // not provided for in the JDBC spec + this.noAccessToProcedureBodies = conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_noAccessToProcedureBodies).getValue(); + } + + @Override + public void addBatch() throws SQLException { + setOutParams(); + + super.addBatch(); + } + + private CallableStatementParam checkIsOutputParam(int paramIndex) throws SQLException { + + synchronized (checkClosed().getConnectionMutex()) { + if (this.callingStoredFunction) { + if (paramIndex == 1) { + + if (this.returnValueParam == null) { + this.returnValueParam = new CallableStatementParam("", 0, false, true, MysqlType.VARCHAR.getJdbcType(), "VARCHAR", 0, 0, + java.sql.DatabaseMetaData.attributeNullableUnknown, java.sql.DatabaseMetaData.procedureColumnReturn); + } + + return this.returnValueParam; + } + + // Move to position in output result set + paramIndex--; + } + + checkParameterIndexBounds(paramIndex); + + int localParamIndex = paramIndex - 1; + + if (this.placeholderToParameterIndexMap != null) { + localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; + } + + CallableStatementParam paramDescriptor = this.paramInfo.getParameter(localParamIndex); + + // We don't have reliable metadata in this case, trust the caller + + if (this.noAccessToProcedureBodies) { + paramDescriptor.isOut = true; + paramDescriptor.isIn = true; + paramDescriptor.inOutModifier = java.sql.DatabaseMetaData.procedureColumnInOut; + } else if (!paramDescriptor.isOut) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.9", new Object[] { paramIndex }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + this.hasOutputParams = true; + + return paramDescriptor; + } + } + + /** + * @param paramIndex + * + * @throws SQLException + */ + private void checkParameterIndexBounds(int paramIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.paramInfo.checkBounds(paramIndex); + } + } + + /** + * Checks whether or not this statement is supposed to be providing + * streamable result sets...If output parameters are registered, the driver + * can not stream the results. + * + * @throws SQLException + */ + private void checkStreamability() throws SQLException { + if (this.hasOutputParams && createStreamingResultSet()) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.14"), MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, + getExceptionInterceptor()); + } + } + + @Override + public void clearParameters() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + super.clearParameters(); + + try { + if (this.outputParameterResults != null) { + this.outputParameterResults.close(); + } + } finally { + this.outputParameterResults = null; + } + } + } + + /** + * Used to fake up some metadata when we don't have access to + * SHOW CREATE PROCEDURE or mysql.proc. + * + * @throws SQLException + * if we can't build the metadata. + */ + private void fakeParameterTypes(boolean isReallyProcedure) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + String encoding = this.connection.getSession().getServerSession().getCharacterSetMetadata(); + int collationIndex = this.connection.getSession().getServerSession().getMetadataCollationIndex(); + Field[] fields = new Field[13]; + + fields[0] = new Field("", "PROCEDURE_CAT", collationIndex, encoding, MysqlType.CHAR, 0); + fields[1] = new Field("", "PROCEDURE_SCHEM", collationIndex, encoding, MysqlType.CHAR, 0); + fields[2] = new Field("", "PROCEDURE_NAME", collationIndex, encoding, MysqlType.CHAR, 0); + fields[3] = new Field("", "COLUMN_NAME", collationIndex, encoding, MysqlType.CHAR, 0); + fields[4] = new Field("", "COLUMN_TYPE", collationIndex, encoding, MysqlType.CHAR, 0); + fields[5] = new Field("", "DATA_TYPE", collationIndex, encoding, MysqlType.SMALLINT, 0); + fields[6] = new Field("", "TYPE_NAME", collationIndex, encoding, MysqlType.CHAR, 0); + fields[7] = new Field("", "PRECISION", collationIndex, encoding, MysqlType.INT, 0); + fields[8] = new Field("", "LENGTH", collationIndex, encoding, MysqlType.INT, 0); + fields[9] = new Field("", "SCALE", collationIndex, encoding, MysqlType.SMALLINT, 0); + fields[10] = new Field("", "RADIX", collationIndex, encoding, MysqlType.SMALLINT, 0); + fields[11] = new Field("", "NULLABLE", collationIndex, encoding, MysqlType.SMALLINT, 0); + fields[12] = new Field("", "REMARKS", collationIndex, encoding, MysqlType.CHAR, 0); + + String procName = isReallyProcedure ? extractProcedureName() : null; + + byte[] procNameAsBytes = null; + + procNameAsBytes = procName == null ? null : StringUtils.getBytes(procName, "UTF-8"); + + ArrayList resultRows = new ArrayList<>(); + + for (int i = 0; i < ((PreparedQuery) this.query).getParameterCount(); i++) { + byte[][] row = new byte[13][]; + row[0] = null; // PROCEDURE_CAT + row[1] = null; // PROCEDURE_SCHEM + row[2] = procNameAsBytes; // PROCEDURE/NAME + row[3] = s2b(String.valueOf(i)); // COLUMN_NAME + + row[4] = s2b(String.valueOf(java.sql.DatabaseMetaData.procedureColumnIn)); + + row[5] = s2b(String.valueOf(MysqlType.VARCHAR.getJdbcType())); // DATA_TYPE + row[6] = s2b(MysqlType.VARCHAR.getName()); // TYPE_NAME + row[7] = s2b(Integer.toString(65535)); // PRECISION + row[8] = s2b(Integer.toString(65535)); // LENGTH + row[9] = s2b(Integer.toString(0)); // SCALE + row[10] = s2b(Integer.toString(10)); // RADIX + + row[11] = s2b(Integer.toString(java.sql.DatabaseMetaData.procedureNullableUnknown)); // nullable + + row[12] = null; + + resultRows.add(new ByteArrayRow(row, getExceptionInterceptor())); + } + + java.sql.ResultSet paramTypesRs = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(resultRows, new DefaultColumnDefinition(fields))); + + convertGetProcedureColumnsToInternalDescriptors(paramTypesRs); + } + } + + private void determineParameterTypes() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + java.sql.ResultSet paramTypesRs = null; + + try { + //Bug#57022, we need to check for db.SPname notation first and pass on only SPname + String procName = extractProcedureName(); + String quotedId = this.session.getIdentifierQuoteString(); + + List parseList = StringUtils.splitDBdotName(procName, "", quotedId, this.session.getServerSession().isNoBackslashEscapesSet()); + String tmpCatalog = ""; + //There *should* be 2 rows, if any. + if (parseList.size() == 2) { + tmpCatalog = (String) parseList.get(0); + procName = (String) parseList.get(1); + } else { + //keep values as they are + } + + java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); + + boolean useCatalog = false; + + if (tmpCatalog.length() <= 0) { + useCatalog = true; + } + + paramTypesRs = dbmd.getProcedureColumns(useCatalog ? this.getCurrentCatalog() : tmpCatalog/* null */, null, procName, "%"); + + boolean hasResults = false; + try { + if (paramTypesRs.next()) { + paramTypesRs.previous(); + hasResults = true; + } + } catch (Exception e) { + // paramTypesRs is empty, proceed with fake params. swallow, was expected + } + if (hasResults) { + convertGetProcedureColumnsToInternalDescriptors(paramTypesRs); + } else { + fakeParameterTypes(true); + } + } finally { + SQLException sqlExRethrow = null; + + if (paramTypesRs != null) { + try { + paramTypesRs.close(); + } catch (SQLException sqlEx) { + sqlExRethrow = sqlEx; + } + + paramTypesRs = null; + } + + if (sqlExRethrow != null) { + throw sqlExRethrow; + } + } + } + } + + private void convertGetProcedureColumnsToInternalDescriptors(java.sql.ResultSet paramTypesRs) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.paramInfo = new CallableStatementParamInfo(paramTypesRs); + } + } + + @Override + public boolean execute() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + boolean returnVal = false; + + checkStreamability(); + + setInOutParamsOnServer(); + setOutParams(); + + returnVal = super.execute(); + + if (this.callingStoredFunction) { + this.functionReturnValueResults = this.results; + this.functionReturnValueResults.next(); + this.results = null; + } + + // TODO There is something strange here: + // From ResultSetRegressionTest.testBug14562(): + // + // $ CREATE TABLE testBug14562 (row_order INT, signed_field MEDIUMINT, unsigned_field MEDIUMINT UNSIGNED) + // $ INSERT INTO testBug14562 VALUES (1, -8388608, 0), (2, 8388607, 16777215) + // $ CREATE PROCEDURE sp_testBug14562_1 (OUT param_1 MEDIUMINT, OUT param_2 MEDIUMINT UNSIGNED) + // BEGIN + // SELECT signed_field, unsigned_field INTO param_1, param_2 FROM testBug14562 WHERE row_order=1; + // END + // $ CALL sp_testBug14562_1(@com_mysql_jdbc_outparam_param_1, @com_mysql_jdbc_outparam_param_2) + // $ SELECT @com_mysql_jdbc_outparam_param_1,@com_mysql_jdbc_outparam_param_2 + // + // ResultSet metadata returns BIGINT for @com_mysql_jdbc_outparam_param_1 and @com_mysql_jdbc_outparam_param_2 + // instead of expected MEDIUMINT. I wonder what happens to other types... + retrieveOutParams(); + + if (!this.callingStoredFunction) { + return returnVal; + } + + // Functions can't return results + return false; + } + } + + @Override + public java.sql.ResultSet executeQuery() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + checkStreamability(); + + java.sql.ResultSet execResults = null; + + setInOutParamsOnServer(); + setOutParams(); + + execResults = super.executeQuery(); + + retrieveOutParams(); + + return execResults; + } + } + + @Override + public int executeUpdate() throws SQLException { + return Util.truncateAndConvertToInt(executeLargeUpdate()); + } + + private String extractProcedureName() throws SQLException { + String sanitizedSql = StringUtils.stripComments(((PreparedQuery) this.query).getOriginalSql(), "`\"'", "`\"'", true, false, true, true); + + // TODO: Do this with less memory allocation + int endCallIndex = StringUtils.indexOfIgnoreCase(sanitizedSql, "CALL "); + int offset = 5; + + if (endCallIndex == -1) { + endCallIndex = StringUtils.indexOfIgnoreCase(sanitizedSql, "SELECT "); + offset = 7; + } + + if (endCallIndex != -1) { + StringBuilder nameBuf = new StringBuilder(); + + String trimmedStatement = sanitizedSql.substring(endCallIndex + offset).trim(); + + int statementLength = trimmedStatement.length(); + + for (int i = 0; i < statementLength; i++) { + char c = trimmedStatement.charAt(i); + + if (Character.isWhitespace(c) || (c == '(') || (c == '?')) { + break; + } + nameBuf.append(c); + + } + + return nameBuf.toString(); + } + + throw SQLError.createSQLException(Messages.getString("CallableStatement.1"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + /** + * Adds 'at' symbol to beginning of parameter names if needed. + * + * @param paramNameIn + * the parameter name to 'fix' + * + * @return the parameter name with an 'a' prepended, if needed + * + * @throws SQLException + * if the parameter name is null or empty. + */ + protected String fixParameterName(String paramNameIn) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (paramNameIn == null) { + paramNameIn = "nullpn"; + } + + if (this.noAccessToProcedureBodies) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.23"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + return mangleParameterName(paramNameIn); + } + } + + /** + * @see java.sql.CallableStatement#getArray(int) + */ + public Array getArray(int i) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(i); + + Array retValue = rs.getArray(mapOutputParameterIndexToRsIndex(i)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getArray(java.lang.String) + */ + public Array getArray(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Array retValue = rs.getArray(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBigDecimal(int) + */ + public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + BigDecimal retValue = rs.getBigDecimal(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @param parameterIndex + * @param scale + * + * @throws SQLException + * + * @see java.sql.CallableStatement#getBigDecimal(int, int) + * @deprecated + */ + @Deprecated + public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + BigDecimal retValue = rs.getBigDecimal(mapOutputParameterIndexToRsIndex(parameterIndex), scale); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBigDecimal(java.lang.String) + */ + public BigDecimal getBigDecimal(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + BigDecimal retValue = rs.getBigDecimal(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBlob(int) + */ + public Blob getBlob(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Blob retValue = rs.getBlob(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBlob(java.lang.String) + */ + public Blob getBlob(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Blob retValue = rs.getBlob(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBoolean(int) + */ + public boolean getBoolean(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + boolean retValue = rs.getBoolean(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBoolean(java.lang.String) + */ + public boolean getBoolean(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + boolean retValue = rs.getBoolean(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getByte(int) + */ + public byte getByte(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + byte retValue = rs.getByte(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getByte(java.lang.String) + */ + public byte getByte(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + byte retValue = rs.getByte(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBytes(int) + */ + public byte[] getBytes(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + byte[] retValue = rs.getBytes(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getBytes(java.lang.String) + */ + public byte[] getBytes(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + byte[] retValue = rs.getBytes(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getClob(int) + */ + public Clob getClob(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Clob retValue = rs.getClob(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getClob(java.lang.String) + */ + public Clob getClob(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Clob retValue = rs.getClob(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getDate(int) + */ + public Date getDate(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Date retValue = rs.getDate(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getDate(int, java.util.Calendar) + */ + public Date getDate(int parameterIndex, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Date retValue = rs.getDate(mapOutputParameterIndexToRsIndex(parameterIndex), cal); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getDate(java.lang.String) + */ + public Date getDate(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Date retValue = rs.getDate(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getDate(java.lang.String, java.util.Calendar) + */ + public Date getDate(String parameterName, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Date retValue = rs.getDate(fixParameterName(parameterName), cal); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getDouble(int) + */ + public double getDouble(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + double retValue = rs.getDouble(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getDouble(java.lang.String) + */ + public double getDouble(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + double retValue = rs.getDouble(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getFloat(int) + */ + public float getFloat(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + float retValue = rs.getFloat(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getFloat(java.lang.String) + */ + public float getFloat(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + float retValue = rs.getFloat(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getInt(int) + */ + public int getInt(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + int retValue = rs.getInt(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getInt(java.lang.String) + */ + public int getInt(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + int retValue = rs.getInt(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getLong(int) + */ + public long getLong(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + long retValue = rs.getLong(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getLong(java.lang.String) + */ + public long getLong(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + long retValue = rs.getLong(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + protected int getNamedParamIndex(String paramName, boolean forOut) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.noAccessToProcedureBodies) { + throw SQLError.createSQLException("No access to parameters by name when connection has been configured not to access procedure bodies", + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if ((paramName == null) || (paramName.length() == 0)) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + CallableStatementParam namedParamInfo; + if (this.paramInfo == null || (namedParamInfo = this.paramInfo.getParameter(paramName)) == null) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.3", new Object[] { paramName }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (forOut && !namedParamInfo.isOut) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.5", new Object[] { paramName }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (this.placeholderToParameterIndexMap == null) { + return namedParamInfo.index + 1; // JDBC indices are 1-based + } + + for (int i = 0; i < this.placeholderToParameterIndexMap.length; i++) { + if (this.placeholderToParameterIndexMap[i] == namedParamInfo.index) { + return i + 1; + } + } + + throw SQLError.createSQLException(Messages.getString("CallableStatement.6", new Object[] { paramName }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * @see java.sql.CallableStatement#getObject(int) + */ + public Object getObject(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + CallableStatementParam paramDescriptor = checkIsOutputParam(parameterIndex); + + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Object retVal = rs.getObjectStoredProc(mapOutputParameterIndexToRsIndex(parameterIndex), paramDescriptor.desiredMysqlType.getJdbcType()); + + this.outputParamWasNull = rs.wasNull(); + + return retVal; + } + } + + /** + * @see java.sql.CallableStatement#getObject(int, java.util.Map) + */ + public Object getObject(int parameterIndex, Map> map) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Object retVal = rs.getObject(mapOutputParameterIndexToRsIndex(parameterIndex), map); + + this.outputParamWasNull = rs.wasNull(); + + return retVal; + } + } + + /** + * @see java.sql.CallableStatement#getObject(java.lang.String) + */ + public Object getObject(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Object retValue = rs.getObject(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getObject(java.lang.String, java.util.Map) + */ + public Object getObject(String parameterName, Map> map) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Object retValue = rs.getObject(fixParameterName(parameterName), map); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + public T getObject(int parameterIndex, Class type) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + // remove cast once 1.5, 1.6 EOL'd + T retVal = ((ResultSetImpl) rs).getObject(mapOutputParameterIndexToRsIndex(parameterIndex), type); + + this.outputParamWasNull = rs.wasNull(); + + return retVal; + } + } + + public T getObject(String parameterName, Class type) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + T retValue = ((ResultSetImpl) rs).getObject(fixParameterName(parameterName), type); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * Returns the ResultSet that holds the output parameters, or throws an + * appropriate exception if none exist, or they weren't returned. + * + * @return the ResultSet that holds the output parameters + * + * @throws SQLException + * if no output parameters were defined, or if no output + * parameters were returned. + */ + protected ResultSetInternalMethods getOutputParameters(int paramIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.outputParamWasNull = false; + + if (paramIndex == 1 && this.callingStoredFunction && this.returnValueParam != null) { + return this.functionReturnValueResults; + } + + if (this.outputParameterResults == null) { + if (this.paramInfo.numberOfParameters() == 0) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.7"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + throw SQLError.createSQLException(Messages.getString("CallableStatement.8"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + + return this.outputParameterResults; + } + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.placeholderToParameterIndexMap == null) { + return this.paramInfo; + } + + return new CallableStatementParamInfo(this.paramInfo); + } + } + + /** + * @see java.sql.CallableStatement#getRef(int) + */ + public Ref getRef(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Ref retValue = rs.getRef(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getRef(java.lang.String) + */ + public Ref getRef(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Ref retValue = rs.getRef(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getShort(int) + */ + public short getShort(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + short retValue = rs.getShort(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getShort(java.lang.String) + */ + public short getShort(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + short retValue = rs.getShort(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getString(int) + */ + public String getString(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + String retValue = rs.getString(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getString(java.lang.String) + */ + public String getString(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + String retValue = rs.getString(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTime(int) + */ + public Time getTime(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Time retValue = rs.getTime(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTime(int, java.util.Calendar) + */ + public Time getTime(int parameterIndex, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Time retValue = rs.getTime(mapOutputParameterIndexToRsIndex(parameterIndex), cal); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTime(java.lang.String) + */ + public Time getTime(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Time retValue = rs.getTime(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTime(java.lang.String, java.util.Calendar) + */ + public Time getTime(String parameterName, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Time retValue = rs.getTime(fixParameterName(parameterName), cal); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTimestamp(int) + */ + public Timestamp getTimestamp(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Timestamp retValue = rs.getTimestamp(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTimestamp(int, java.util.Calendar) + */ + public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Timestamp retValue = rs.getTimestamp(mapOutputParameterIndexToRsIndex(parameterIndex), cal); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTimestamp(java.lang.String) + */ + public Timestamp getTimestamp(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Timestamp retValue = rs.getTimestamp(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getTimestamp(java.lang.String, java.util.Calendar) + */ + public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Timestamp retValue = rs.getTimestamp(fixParameterName(parameterName), cal); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getURL(int) + */ + public URL getURL(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + URL retValue = rs.getURL(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + /** + * @see java.sql.CallableStatement#getURL(java.lang.String) + */ + public URL getURL(String parameterName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + URL retValue = rs.getURL(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + } + + protected int mapOutputParameterIndexToRsIndex(int paramIndex) throws SQLException { + + synchronized (checkClosed().getConnectionMutex()) { + if (this.returnValueParam != null && paramIndex == 1) { + return 1; + } + + checkParameterIndexBounds(paramIndex); + + int localParamIndex = paramIndex - 1; + + if (this.placeholderToParameterIndexMap != null) { + localParamIndex = this.placeholderToParameterIndexMap[localParamIndex]; + } + + int rsIndex = this.parameterIndexToRsIndex[localParamIndex]; + + if (rsIndex == NOT_OUTPUT_PARAMETER_INDICATOR) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.21", new Object[] { paramIndex }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + return rsIndex + 1; + } + } + + protected void registerOutParameter(int parameterIndex, MysqlType mysqlType) throws SQLException { + CallableStatementParam paramDescriptor = checkIsOutputParam(parameterIndex); + paramDescriptor.desiredMysqlType = mysqlType; + } + + public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { + try { + MysqlType mt = MysqlType.getByJdbcType(sqlType); + registerOutParameter(parameterIndex, mt); + } catch (FeatureNotAvailableException nae) { + throw SQLError.createSQLFeatureNotSupportedException(Messages.getString("Statement.UnsupportedSQLType") + JDBCType.valueOf(sqlType), + MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor()); + } + } + + public void registerOutParameter(int parameterIndex, SQLType sqlType) throws SQLException { + if (sqlType instanceof MysqlType) { + registerOutParameter(parameterIndex, (MysqlType) sqlType); + } else { + registerOutParameter(parameterIndex, sqlType.getVendorTypeNumber()); + } + } + + protected void registerOutParameter(int parameterIndex, MysqlType mysqlType, @SuppressWarnings("unused") int scale) throws SQLException { + registerOutParameter(parameterIndex, mysqlType); // TODO is that correct that we ignore scale? + } + + public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException { + registerOutParameter(parameterIndex, sqlType); // TODO is that correct that we ignore scale? + } + + public void registerOutParameter(int parameterIndex, SQLType sqlType, int scale) throws SQLException { + if (sqlType instanceof MysqlType) { + registerOutParameter(parameterIndex, (MysqlType) sqlType, scale); + } else { + registerOutParameter(parameterIndex, sqlType.getVendorTypeNumber(), scale); + } + } + + protected void registerOutParameter(int parameterIndex, MysqlType mysqlType, @SuppressWarnings("unused") String typeName) throws SQLException { + registerOutParameter(parameterIndex, mysqlType); // TODO is that correct that we ignore typeName? + } + + public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException { + try { + MysqlType mt = MysqlType.getByJdbcType(sqlType); + registerOutParameter(parameterIndex, mt, typeName); + } catch (FeatureNotAvailableException nae) { + throw SQLError.createSQLFeatureNotSupportedException(Messages.getString("Statement.UnsupportedSQLType") + JDBCType.valueOf(sqlType), + MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor()); + } + } + + public void registerOutParameter(int parameterIndex, SQLType sqlType, String typeName) throws SQLException { + if (sqlType instanceof MysqlType) { + registerOutParameter(parameterIndex, (MysqlType) sqlType, typeName); + } else { + registerOutParameter(parameterIndex, sqlType.getVendorTypeNumber(), typeName); + } + } + + /** + * @see java.sql.CallableStatement#registerOutParameter(java.lang.String, int) + */ + public void registerOutParameter(String parameterName, int sqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + registerOutParameter(getNamedParamIndex(parameterName, true), sqlType); + } + } + + public void registerOutParameter(String parameterName, SQLType sqlType) throws SQLException { + if (sqlType instanceof MysqlType) { + registerOutParameter(getNamedParamIndex(parameterName, true), (MysqlType) sqlType); + } else { + registerOutParameter(getNamedParamIndex(parameterName, true), sqlType.getVendorTypeNumber()); + } + } + + /** + * @see java.sql.CallableStatement#registerOutParameter(java.lang.String, int, int) + */ + public void registerOutParameter(String parameterName, int sqlType, int scale) throws SQLException { + registerOutParameter(getNamedParamIndex(parameterName, true), sqlType, scale); + } + + public void registerOutParameter(String parameterName, SQLType sqlType, int scale) throws SQLException { + if (sqlType instanceof MysqlType) { + registerOutParameter(getNamedParamIndex(parameterName, true), (MysqlType) sqlType, scale); + } else { + registerOutParameter(getNamedParamIndex(parameterName, true), sqlType.getVendorTypeNumber(), scale); + } + } + + /** + * @see java.sql.CallableStatement#registerOutParameter(java.lang.String, int, java.lang.String) + */ + public void registerOutParameter(String parameterName, int sqlType, String typeName) throws SQLException { + registerOutParameter(getNamedParamIndex(parameterName, true), sqlType, typeName); + } + + public void registerOutParameter(String parameterName, SQLType sqlType, String typeName) throws SQLException { + if (sqlType instanceof MysqlType) { + registerOutParameter(getNamedParamIndex(parameterName, true), (MysqlType) sqlType, typeName); + } else { + registerOutParameter(parameterName, sqlType.getVendorTypeNumber(), typeName); + } + } + + /** + * Issues a second query to retrieve all output parameters. + * + * @throws SQLException + * if an error occurs. + */ + private void retrieveOutParams() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + int numParameters = this.paramInfo.numberOfParameters(); + + this.parameterIndexToRsIndex = new int[numParameters]; + + for (int i = 0; i < numParameters; i++) { + this.parameterIndexToRsIndex[i] = NOT_OUTPUT_PARAMETER_INDICATOR; + } + + int localParamIndex = 0; + + if (numParameters > 0) { + StringBuilder outParameterQuery = new StringBuilder("SELECT "); + + boolean firstParam = true; + boolean hadOutputParams = false; + + for (Iterator paramIter = this.paramInfo.iterator(); paramIter.hasNext();) { + CallableStatementParam retrParamInfo = paramIter.next(); + + if (retrParamInfo.isOut) { + hadOutputParams = true; + + this.parameterIndexToRsIndex[retrParamInfo.index] = localParamIndex++; + + if (retrParamInfo.paramName == null) { + retrParamInfo.paramName = "nullnp" + retrParamInfo.index; + } + + String outParameterName = mangleParameterName(retrParamInfo.paramName); + + if (!firstParam) { + outParameterQuery.append(","); + } else { + firstParam = false; + } + + if (!outParameterName.startsWith("@")) { + outParameterQuery.append('@'); + } + + outParameterQuery.append(outParameterName); + } + } + + if (hadOutputParams) { + // We can't use 'ourself' to execute this query, or any pending result sets would be overwritten + java.sql.Statement outParameterStmt = null; + java.sql.ResultSet outParamRs = null; + + try { + outParameterStmt = this.connection.createStatement(); + outParamRs = outParameterStmt.executeQuery(outParameterQuery.toString()); + this.outputParameterResults = this.resultSetFactory.createFromResultsetRows(outParamRs.getConcurrency(), outParamRs.getType(), + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) outParamRs).getRows()); // note, doesn't work for updatable result sets + + if (!this.outputParameterResults.next()) { + this.outputParameterResults.close(); + this.outputParameterResults = null; + } + } finally { + if (outParameterStmt != null) { + outParameterStmt.close(); + } + } + } else { + this.outputParameterResults = null; + } + } else { + this.outputParameterResults = null; + } + } + } + + /** + * @see java.sql.CallableStatement#setAsciiStream(java.lang.String, java.io.InputStream, int) + */ + public void setAsciiStream(String parameterName, InputStream x, int length) throws SQLException { + setAsciiStream(getNamedParamIndex(parameterName, false), x, length); + } + + /** + * @see java.sql.CallableStatement#setBigDecimal(java.lang.String, java.math.BigDecimal) + */ + public void setBigDecimal(String parameterName, BigDecimal x) throws SQLException { + setBigDecimal(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setBinaryStream(java.lang.String, java.io.InputStream, int) + */ + public void setBinaryStream(String parameterName, InputStream x, int length) throws SQLException { + setBinaryStream(getNamedParamIndex(parameterName, false), x, length); + } + + /** + * @see java.sql.CallableStatement#setBoolean(java.lang.String, boolean) + */ + public void setBoolean(String parameterName, boolean x) throws SQLException { + setBoolean(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setByte(java.lang.String, byte) + */ + public void setByte(String parameterName, byte x) throws SQLException { + setByte(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setBytes(java.lang.String, byte[]) + */ + public void setBytes(String parameterName, byte[] x) throws SQLException { + setBytes(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setCharacterStream(java.lang.String, java.io.Reader, int) + */ + public void setCharacterStream(String parameterName, Reader reader, int length) throws SQLException { + setCharacterStream(getNamedParamIndex(parameterName, false), reader, length); + } + + /** + * @see java.sql.CallableStatement#setDate(java.lang.String, java.sql.Date) + */ + public void setDate(String parameterName, Date x) throws SQLException { + setDate(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setDate(java.lang.String, java.sql.Date, java.util.Calendar) + */ + public void setDate(String parameterName, Date x, Calendar cal) throws SQLException { + setDate(getNamedParamIndex(parameterName, false), x, cal); + } + + /** + * @see java.sql.CallableStatement#setDouble(java.lang.String, double) + */ + public void setDouble(String parameterName, double x) throws SQLException { + setDouble(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setFloat(java.lang.String, float) + */ + public void setFloat(String parameterName, float x) throws SQLException { + setFloat(getNamedParamIndex(parameterName, false), x); + } + + private void setInOutParamsOnServer() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.paramInfo.numParameters > 0) { + for (Iterator paramIter = this.paramInfo.iterator(); paramIter.hasNext();) { + + CallableStatementParam inParamInfo = paramIter.next(); + + if (inParamInfo.isOut && inParamInfo.isIn) { + if (inParamInfo.paramName == null) { + inParamInfo.paramName = "nullnp" + inParamInfo.index; + } + + String inOutParameterName = mangleParameterName(inParamInfo.paramName); + StringBuilder queryBuf = new StringBuilder(4 + inOutParameterName.length() + 1 + 1); + queryBuf.append("SET "); + queryBuf.append(inOutParameterName); + queryBuf.append("=?"); + + ClientPreparedStatement setPstmt = null; + + try { + setPstmt = ((Wrapper) this.connection.clientPrepareStatement(queryBuf.toString())).unwrap(ClientPreparedStatement.class); + + if (((PreparedQuery) this.query).getQueryBindings().getBindValues()[inParamInfo.index].isNull()) { + setPstmt.setBytesNoEscapeNoQuotes(1, "NULL".getBytes()); + + } else { + byte[] parameterAsBytes = getBytesRepresentation(inParamInfo.index); + + if (parameterAsBytes != null) { + if (parameterAsBytes.length > 8 && parameterAsBytes[0] == '_' && parameterAsBytes[1] == 'b' && parameterAsBytes[2] == 'i' + && parameterAsBytes[3] == 'n' && parameterAsBytes[4] == 'a' && parameterAsBytes[5] == 'r' + && parameterAsBytes[6] == 'y' && parameterAsBytes[7] == '\'') { + setPstmt.setBytesNoEscapeNoQuotes(1, parameterAsBytes); + } else { + switch (inParamInfo.desiredMysqlType) { + case BIT: + case BINARY: + case GEOMETRY: + case TINYBLOB: + case BLOB: + case MEDIUMBLOB: + case LONGBLOB: + case VARBINARY: + setPstmt.setBytes(1, parameterAsBytes); + break; + default: + // the inherited PreparedStatement methods have already escaped and quoted these parameters + setPstmt.setBytesNoEscape(1, parameterAsBytes); + } + } + } else { + setPstmt.setNull(1, MysqlType.NULL); + } + } + + setPstmt.executeUpdate(); + } finally { + if (setPstmt != null) { + setPstmt.close(); + } + } + } + } + } + } + } + + /** + * @see java.sql.CallableStatement#setInt(java.lang.String, int) + */ + public void setInt(String parameterName, int x) throws SQLException { + setInt(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setLong(java.lang.String, long) + */ + public void setLong(String parameterName, long x) throws SQLException { + setLong(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setNull(java.lang.String, int) + */ + public void setNull(String parameterName, int sqlType) throws SQLException { + setNull(getNamedParamIndex(parameterName, false), sqlType); + } + + /** + * @see java.sql.CallableStatement#setNull(java.lang.String, int, java.lang.String) + */ + public void setNull(String parameterName, int sqlType, String typeName) throws SQLException { + setNull(getNamedParamIndex(parameterName, false), sqlType, typeName); + } + + /** + * @see java.sql.CallableStatement#setObject(java.lang.String, java.lang.Object) + */ + public void setObject(String parameterName, Object x) throws SQLException { + setObject(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setObject(java.lang.String, java.lang.Object, int) + */ + public void setObject(String parameterName, Object x, int targetSqlType) throws SQLException { + setObject(getNamedParamIndex(parameterName, false), x, targetSqlType); + } + + public void setObject(String parameterName, Object x, SQLType targetSqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + setObject(getNamedParamIndex(parameterName, false), x, targetSqlType); + } + } + + /** + * @see java.sql.CallableStatement#setObject(java.lang.String, java.lang.Object, int, int) + */ + public void setObject(String parameterName, Object x, int targetSqlType, int scale) throws SQLException { + setObject(getNamedParamIndex(parameterName, false), x, targetSqlType, scale); + } + + public void setObject(String parameterName, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + setObject(getNamedParamIndex(parameterName, false), x, targetSqlType, scaleOrLength); + } + } + + private void setOutParams() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.paramInfo.numParameters > 0) { + for (Iterator paramIter = this.paramInfo.iterator(); paramIter.hasNext();) { + CallableStatementParam outParamInfo = paramIter.next(); + + if (!this.callingStoredFunction && outParamInfo.isOut) { + + if (outParamInfo.paramName == null) { + outParamInfo.paramName = "nullnp" + outParamInfo.index; + } + + String outParameterName = mangleParameterName(outParamInfo.paramName); + + int outParamIndex = 0; + + if (this.placeholderToParameterIndexMap == null) { + outParamIndex = outParamInfo.index + 1; + } else { + // Find it, todo: remove this linear search + boolean found = false; + + for (int i = 0; i < this.placeholderToParameterIndexMap.length; i++) { + if (this.placeholderToParameterIndexMap[i] == outParamInfo.index) { + outParamIndex = i + 1; /* JDBC is 1-based */ + found = true; + break; + } + } + + if (!found) { + throw SQLError.createSQLException(Messages.getString("CallableStatement.21", new Object[] { outParamInfo.paramName }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + this.setBytesNoEscapeNoQuotes(outParamIndex, StringUtils.getBytes(outParameterName, this.charEncoding)); + } + } + } + } + } + + /** + * @see java.sql.CallableStatement#setShort(java.lang.String, short) + */ + public void setShort(String parameterName, short x) throws SQLException { + setShort(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setString(java.lang.String, java.lang.String) + */ + public void setString(String parameterName, String x) throws SQLException { + setString(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setTime(java.lang.String, java.sql.Time) + */ + public void setTime(String parameterName, Time x) throws SQLException { + setTime(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setTime(java.lang.String, java.sql.Time, java.util.Calendar) + */ + public void setTime(String parameterName, Time x, Calendar cal) throws SQLException { + setTime(getNamedParamIndex(parameterName, false), x, cal); + } + + /** + * @see java.sql.CallableStatement#setTimestamp(java.lang.String, java.sql.Timestamp) + */ + public void setTimestamp(String parameterName, Timestamp x) throws SQLException { + setTimestamp(getNamedParamIndex(parameterName, false), x); + } + + /** + * @see java.sql.CallableStatement#setTimestamp(java.lang.String, java.sql.Timestamp, java.util.Calendar) + */ + public void setTimestamp(String parameterName, Timestamp x, Calendar cal) throws SQLException { + setTimestamp(getNamedParamIndex(parameterName, false), x, cal); + } + + /** + * @see java.sql.CallableStatement#setURL(java.lang.String, java.net.URL) + */ + public void setURL(String parameterName, URL val) throws SQLException { + setURL(getNamedParamIndex(parameterName, false), val); + } + + /** + * @see java.sql.CallableStatement#wasNull() + */ + public boolean wasNull() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.outputParamWasNull; + } + } + + @Override + public int[] executeBatch() throws SQLException { + return Util.truncateAndConvertToInt(executeLargeBatch()); + + } + + @Override + protected int getParameterIndexOffset() { + if (this.callingStoredFunction) { + return -1; + } + + return super.getParameterIndexOffset(); + } + + public void setAsciiStream(String parameterName, InputStream x) throws SQLException { + setAsciiStream(getNamedParamIndex(parameterName, false), x); + + } + + public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException { + setAsciiStream(getNamedParamIndex(parameterName, false), x, length); + + } + + public void setBinaryStream(String parameterName, InputStream x) throws SQLException { + setBinaryStream(getNamedParamIndex(parameterName, false), x); + + } + + public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException { + setBinaryStream(getNamedParamIndex(parameterName, false), x, length); + + } + + public void setBlob(String parameterName, Blob x) throws SQLException { + setBlob(getNamedParamIndex(parameterName, false), x); + + } + + public void setBlob(String parameterName, InputStream inputStream) throws SQLException { + setBlob(getNamedParamIndex(parameterName, false), inputStream); + + } + + public void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException { + setBlob(getNamedParamIndex(parameterName, false), inputStream, length); + + } + + public void setCharacterStream(String parameterName, Reader reader) throws SQLException { + setCharacterStream(getNamedParamIndex(parameterName, false), reader); + + } + + public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { + setCharacterStream(getNamedParamIndex(parameterName, false), reader, length); + + } + + public void setClob(String parameterName, Clob x) throws SQLException { + setClob(getNamedParamIndex(parameterName, false), x); + + } + + public void setClob(String parameterName, Reader reader) throws SQLException { + setClob(getNamedParamIndex(parameterName, false), reader); + + } + + public void setClob(String parameterName, Reader reader, long length) throws SQLException { + setClob(getNamedParamIndex(parameterName, false), reader, length); + + } + + public void setNCharacterStream(String parameterName, Reader value) throws SQLException { + setNCharacterStream(getNamedParamIndex(parameterName, false), value); + + } + + public void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException { + setNCharacterStream(getNamedParamIndex(parameterName, false), value, length); + + } + + /** + * Check whether the stored procedure alters any data or is safe for read-only usage. + * + * @return true if procedure does not alter data + * @throws SQLException + */ + private boolean checkReadOnlyProcedure() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.noAccessToProcedureBodies) { + return false; + } + + if (this.paramInfo.isReadOnlySafeChecked) { + return this.paramInfo.isReadOnlySafeProcedure; + } + + ResultSet rs = null; + java.sql.PreparedStatement ps = null; + + try { + String procName = extractProcedureName(); + + String catalog = this.getCurrentCatalog(); + + if (procName.indexOf(".") != -1) { + catalog = procName.substring(0, procName.indexOf(".")); + + if (StringUtils.startsWithIgnoreCaseAndWs(catalog, "`") && catalog.trim().endsWith("`")) { + catalog = catalog.substring(1, catalog.length() - 1); + } + + procName = procName.substring(procName.indexOf(".") + 1); + procName = StringUtils.toString(StringUtils.stripEnclosure(StringUtils.getBytes(procName), "`", "`")); + } + ps = this.connection.prepareStatement("SELECT SQL_DATA_ACCESS FROM information_schema.routines WHERE routine_schema = ? AND routine_name = ?"); + ps.setMaxRows(0); + ps.setFetchSize(0); + + ps.setString(1, catalog); + ps.setString(2, procName); + rs = ps.executeQuery(); + if (rs.next()) { + String sqlDataAccess = rs.getString(1); + if ("READS SQL DATA".equalsIgnoreCase(sqlDataAccess) || "NO SQL".equalsIgnoreCase(sqlDataAccess)) { + synchronized (this.paramInfo) { + this.paramInfo.isReadOnlySafeChecked = true; + this.paramInfo.isReadOnlySafeProcedure = true; + } + return true; + } + } + } catch (SQLException e) { + // swallow the Exception + } finally { + if (rs != null) { + rs.close(); + } + if (ps != null) { + ps.close(); + } + + } + this.paramInfo.isReadOnlySafeChecked = false; + this.paramInfo.isReadOnlySafeProcedure = false; + } + return false; + + } + + @Override + protected boolean checkReadOnlySafeStatement() throws SQLException { + return (super.checkReadOnlySafeStatement() || this.checkReadOnlyProcedure()); + } + + public RowId getRowId(int parameterIndex) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + RowId retValue = rs.getRowId(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + public RowId getRowId(String parameterName) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + RowId retValue = rs.getRowId(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + public void setRowId(String parameterName, RowId x) throws SQLException { + setRowId(getNamedParamIndex(parameterName, false), x); + } + + public void setNString(String parameterName, String value) throws SQLException { + setNString(getNamedParamIndex(parameterName, false), value); + } + + public void setNClob(String parameterName, NClob value) throws SQLException { + setNClob(getNamedParamIndex(parameterName, false), value); + + } + + public void setNClob(String parameterName, Reader reader) throws SQLException { + setNClob(getNamedParamIndex(parameterName, false), reader); + + } + + public void setNClob(String parameterName, Reader reader, long length) throws SQLException { + setNClob(getNamedParamIndex(parameterName, false), reader, length); + + } + + public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException { + setSQLXML(getNamedParamIndex(parameterName, false), xmlObject); + + } + + public SQLXML getSQLXML(int parameterIndex) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + SQLXML retValue = rs.getSQLXML(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + + } + + public SQLXML getSQLXML(String parameterName) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + SQLXML retValue = rs.getSQLXML(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + public String getNString(int parameterIndex) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + String retValue = rs.getNString(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + public String getNString(String parameterName) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + String retValue = rs.getNString(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + public Reader getNCharacterStream(int parameterIndex) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Reader retValue = rs.getNCharacterStream(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + public Reader getNCharacterStream(String parameterName) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + Reader retValue = rs.getNCharacterStream(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + /** + * @see java.sql.CallableStatement#getCharacterStream(int) + */ + public Reader getCharacterStream(int parameterIndex) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + Reader retValue = rs.getCharacterStream(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + /** + * @see java.sql.CallableStatement#getCharacterStream(java.lang.String) + */ + public Reader getCharacterStream(String parameterName) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + Reader retValue = rs.getCharacterStream(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + public NClob getNClob(int parameterIndex) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(parameterIndex); + + NClob retValue = rs.getNClob(mapOutputParameterIndexToRsIndex(parameterIndex)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + public NClob getNClob(String parameterName) throws SQLException { + ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be from ?= + + NClob retValue = rs.getNClob(fixParameterName(parameterName)); + + this.outputParamWasNull = rs.wasNull(); + + return retValue; + } + + /** + * Converts the given string to bytes, using the connection's character + * encoding. + * + * @param s + */ + protected byte[] s2b(String s) { + return s == null ? null : StringUtils.getBytes(s, this.charEncoding); + } + + @Override + public long executeLargeUpdate() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + long returnVal = -1; + + checkStreamability(); + + if (this.callingStoredFunction) { + execute(); + + return -1; + } + + setInOutParamsOnServer(); + setOutParams(); + + returnVal = super.executeLargeUpdate(); + + retrieveOutParams(); + + return returnVal; + } + } + + @Override + public long[] executeLargeBatch() throws SQLException { + if (this.hasOutputParams) { + throw SQLError.createSQLException("Can't call executeBatch() on CallableStatement with OUTPUT parameters", + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + return super.executeLargeBatch(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/CallableStatementWrapper.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/CallableStatementWrapper.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/CallableStatementWrapper.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,1885 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.lang.reflect.Proxy; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Ref; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLType; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; + +/** + * Wraps callable statements created by pooled connections. + */ +public class CallableStatementWrapper extends PreparedStatementWrapper implements CallableStatement { + + protected static CallableStatementWrapper getInstance(ConnectionWrapper c, MysqlPooledConnection conn, CallableStatement toWrap) throws SQLException { + return new CallableStatementWrapper(c, conn, toWrap); + } + + /** + * @param c + * @param conn + * @param toWrap + */ + public CallableStatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn, CallableStatement toWrap) { + super(c, conn, toWrap); + } + + public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterIndex, sqlType); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterIndex, sqlType, scale); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public boolean wasNull() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).wasNull(); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; + } + + public String getString(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getString(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public boolean getBoolean(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBoolean(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; + } + + public byte getByte(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getByte(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public short getShort(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getShort(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public int getInt(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getInt(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public long getLong(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getLong(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public float getFloat(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getFloat(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public double getDouble(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getDouble(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + @Deprecated + public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBigDecimal(parameterIndex, scale); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public byte[] getBytes(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBytes(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Date getDate(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getDate(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Time getTime(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTime(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Timestamp getTimestamp(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTimestamp(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Object getObject(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getObject(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBigDecimal(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Object getObject(int parameterIndex, Map> typeMap) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getObject(parameterIndex, typeMap); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public Ref getRef(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getRef(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Blob getBlob(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBlob(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Clob getClob(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getClob(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public Array getArray(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getArray(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public Date getDate(int parameterIndex, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getDate(parameterIndex, cal); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public Time getTime(int parameterIndex, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTime(parameterIndex, cal); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTimestamp(parameterIndex, cal); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public void registerOutParameter(int paramIndex, int sqlType, String typeName) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(paramIndex, sqlType, typeName); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void registerOutParameter(String parameterName, int sqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterName, sqlType); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void registerOutParameter(String parameterName, int sqlType, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterName, sqlType, scale); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void registerOutParameter(String parameterName, int sqlType, String typeName) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterName, sqlType, typeName); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public URL getURL(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getURL(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public void setURL(String parameterName, URL val) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setURL(parameterName, val); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNull(String parameterName, int sqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNull(parameterName, sqlType); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBoolean(String parameterName, boolean x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBoolean(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setByte(String parameterName, byte x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setByte(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setShort(String parameterName, short x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setShort(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setInt(String parameterName, int x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setInt(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setLong(String parameterName, long x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setLong(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setFloat(String parameterName, float x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setFloat(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setDouble(String parameterName, double x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setDouble(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBigDecimal(String parameterName, BigDecimal x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBigDecimal(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setString(String parameterName, String x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setString(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBytes(String parameterName, byte[] x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBytes(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setDate(String parameterName, Date x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setDate(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setTime(String parameterName, Time x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setTime(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setTimestamp(String parameterName, Timestamp x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setTimestamp(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setAsciiStream(String parameterName, InputStream x, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setAsciiStream(parameterName, x, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + } + + public void setBinaryStream(String parameterName, InputStream x, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBinaryStream(parameterName, x, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setObject(String parameterName, Object x, int targetSqlType, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterName, x, targetSqlType, scale); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setObject(String parameterName, Object x, int targetSqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterName, x, targetSqlType); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setObject(String parameterName, Object x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(String parameterName, Reader reader, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setCharacterStream(parameterName, reader, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setDate(String parameterName, Date x, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setDate(parameterName, x, cal); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setTime(String parameterName, Time x, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setTime(parameterName, x, cal); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setTimestamp(String parameterName, Timestamp x, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setTimestamp(parameterName, x, cal); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNull(String parameterName, int sqlType, String typeName) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNull(parameterName, sqlType, typeName); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public String getString(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getString(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public boolean getBoolean(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBoolean(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; + } + + public byte getByte(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getByte(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public short getShort(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getShort(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public int getInt(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getInt(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public long getLong(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getLong(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public float getFloat(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getFloat(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public double getDouble(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getDouble(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public byte[] getBytes(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBytes(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Date getDate(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getDate(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Time getTime(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTime(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Timestamp getTimestamp(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTimestamp(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Object getObject(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getObject(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public BigDecimal getBigDecimal(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBigDecimal(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Object getObject(String parameterName, Map> typeMap) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getObject(parameterName, typeMap); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public Ref getRef(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getRef(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Blob getBlob(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getBlob(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Clob getClob(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getClob(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public Array getArray(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getArray(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public Date getDate(String parameterName, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getDate(parameterName, cal); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public Time getTime(String parameterName, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTime(parameterName, cal); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getTimestamp(parameterName, cal); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public URL getURL(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getURL(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public RowId getRowId(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getRowId(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public RowId getRowId(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getRowId(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public void setRowId(String parameterName, RowId x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setRowId(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNString(String parameterName, String value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNString(parameterName, value); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNCharacterStream(String parameterName, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNCharacterStream(parameterName, reader, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(String parameterName, NClob value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNClob(parameterName, value); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setClob(String parameterName, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setClob(parameterName, reader, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBlob(String parameterName, InputStream x, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBlob(parameterName, x, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(String parameterName, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNClob(parameterName, reader, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public NClob getNClob(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getNClob(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public NClob getNClob(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getNClob(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setSQLXML(parameterName, xmlObject); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public SQLXML getSQLXML(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getSQLXML(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public SQLXML getSQLXML(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getSQLXML(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + return null; + } + + public String getNString(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getNString(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public String getNString(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getNString(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Reader getNCharacterStream(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getNCharacterStream(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Reader getNCharacterStream(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getNCharacterStream(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Reader getCharacterStream(int parameterIndex) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getCharacterStream(parameterIndex); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public Reader getCharacterStream(String parameterName) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((CallableStatement) this.wrappedStmt).getCharacterStream(parameterName); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public void setBlob(String parameterName, Blob x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBlob(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setClob(String parameterName, Clob x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setClob(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setAsciiStream(parameterName, x, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBinaryStream(parameterName, x, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setCharacterStream(parameterName, reader, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setAsciiStream(String parameterName, InputStream x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setAsciiStream(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBinaryStream(String parameterName, InputStream x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBinaryStream(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(String parameterName, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setCharacterStream(parameterName, reader); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNCharacterStream(String parameterName, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNCharacterStream(parameterName, reader); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setClob(String parameterName, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setClob(parameterName, reader); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBlob(String parameterName, InputStream x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setBlob(parameterName, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(String parameterName, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setNClob(parameterName, reader); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public T getObject(int parameterIndex, Class type) throws SQLException { + return null; + } + + public T getObject(String parameterName, Class type) throws SQLException { + return null; + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + + boolean isInstance = iface.isInstance(this); + + if (isInstance) { + return true; + } + + String interfaceClassName = iface.getName(); + + return (interfaceClassName.equals("com.mysql.cj.jdbc.Statement") || interfaceClassName.equals("java.sql.Statement") + || interfaceClassName.equals("java.sql.Wrapper") || interfaceClassName.equals("java.sql.PreparedStatement") + || interfaceClassName.equals("java.sql.CallableStatement")); + } + + @Override + public void close() throws SQLException { + try { + super.close(); + } finally { + this.unwrappedInterfaces = null; + } + } + + @Override + public synchronized T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + if ("java.sql.Statement".equals(iface.getName()) || "java.sql.CallableStatement".equals(iface.getName()) + || "java.sql.PreparedStatement".equals(iface.getName()) || "java.sql.Wrapper.class".equals(iface.getName())) { + return iface.cast(this); + } + + if (this.unwrappedInterfaces == null) { + this.unwrappedInterfaces = new HashMap<>(); + } + + Object cachedUnwrapped = this.unwrappedInterfaces.get(iface); + + if (cachedUnwrapped == null) { + cachedUnwrapped = Proxy.newProxyInstance(this.wrappedStmt.getClass().getClassLoader(), new Class[] { iface }, + new ConnectionErrorFiringInvocationHandler(this.wrappedStmt)); + this.unwrappedInterfaces.put(iface, cachedUnwrapped); + } + + return iface.cast(cachedUnwrapped); + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("Common.UnableToUnwrap", new Object[] { iface.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param sqlType + * @throws SQLException + */ + public void registerOutParameter(int parameterIndex, SQLType sqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterIndex, sqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param sqlType + * @param scale + * @throws SQLException + */ + public void registerOutParameter(int parameterIndex, SQLType sqlType, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterIndex, sqlType, scale); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param sqlType + * @param typeName + * @throws SQLException + */ + public void registerOutParameter(int parameterIndex, SQLType sqlType, String typeName) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterIndex, sqlType, typeName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param sqlType + * @throws SQLException + */ + public void registerOutParameter(String parameterName, SQLType sqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterName, sqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param sqlType + * @param scale + * @throws SQLException + */ + public void registerOutParameter(String parameterName, SQLType sqlType, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterName, sqlType, scale); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param sqlType + * @param typeName + * @throws SQLException + */ + public void registerOutParameter(String parameterName, SQLType sqlType, String typeName) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).registerOutParameter(parameterName, sqlType, typeName); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @throws SQLException + */ + @Override + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterIndex, x, targetSqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + @Override + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param x + * @param targetSqlType + * @throws SQLException + */ + public void setObject(String parameterName, Object x, SQLType targetSqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterName, x, targetSqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterName + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public void setObject(String parameterName, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((CallableStatement) this.wrappedStmt).setObject(parameterName, x, targetSqlType, scaleOrLength); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ClientInfoProvider.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ClientInfoProvider.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ClientInfoProvider.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.util.Properties; + +/** + * Classes that implement this interface and provide a no-args constructor can be used by the driver to store and retrieve client information and/or labels. + * + * The driver will create an instance for each Connection instance, and call initialize() once and only once. When the connection is closed, destroy() will be + * called, and the provider is expected to clean up any resources at this time. + */ +public interface ClientInfoProvider { + /** + * Called once by the driver when it needs to configure the provider. + * + * @param conn + * the connection that the provider belongs too. + * @param configurationProps + * a java.util.Properties instance that contains + * configuration information for the connection. + * @throws SQLException + * if initialization fails. + */ + void initialize(java.sql.Connection conn, Properties configurationProps) throws SQLException; + + /** + * Called once by the driver when the connection this provider instance + * belongs to is being closed. + * + * Implementations are expected to clean up and resources at this point + * in time. + * + * @throws SQLException + * if an error occurs. + */ + void destroy() throws SQLException; + + /** + * Returns the client info for the connection that this provider + * instance belongs to. The connection instance is passed as an argument + * for convenience's sake. + * + * Providers can use the connection to communicate with the database, + * but it will be within the scope of any ongoing transactions, so therefore + * implementations should not attempt to change isolation level, autocommit settings + * or call rollback() or commit() on the connection. + * + * @param conn + * connection object + * @throws SQLException + * if an error occurs + * @return client info as Properties + * @see java.sql.Connection#getClientInfo() + */ + Properties getClientInfo(java.sql.Connection conn) throws SQLException; + + /** + * Returns the client info for the connection that this provider + * instance belongs to. The connection instance is passed as an argument + * for convenience's sake. + * + * Providers can use the connection to communicate with the database, + * but it will be within the scope of any ongoing transactions, so therefore + * implementations should not attempt to change isolation level, autocommit settings + * or call rollback() or commit() on the connection. + * + * @param conn + * connection object + * @param name + * property name + * @throws SQLException + * if an error occurs + * @return the client info by given property name + * @see java.sql.Connection#getClientInfo(java.lang.String) + */ + String getClientInfo(java.sql.Connection conn, String name) throws SQLException; + + /** + * Sets the client info for the connection that this provider + * instance belongs to. The connection instance is passed as an argument + * for convenience's sake. + * + * Providers can use the connection to communicate with the database, + * but it will be within the scope of any ongoing transactions, so therefore + * implementations should not attempt to change isolation level, autocommit settings + * or call rollback() or commit() on the connection. + * + * @param conn + * connection object + * @param properties + * Properties object + * @throws SQLClientInfoException + * if an error occurs + * + * @see java.sql.Connection#setClientInfo(java.util.Properties) + */ + void setClientInfo(java.sql.Connection conn, Properties properties) throws SQLClientInfoException; + + /** + * Sets the client info for the connection that this provider + * instance belongs to. The connection instance is passed as an argument + * for convenience's sake. + * + * Providers can use the connection to communicate with the database, + * but it will be within the scope of any ongoing transactions, so therefore + * implementations should not attempt to change isolation level, autocommit settings + * or call rollback() or commit() on the connection. + * + * @param conn + * connection object + * @param name + * property name + * @param value + * property value + * @throws SQLClientInfoException + * if an error occurs + * + * @see java.sql.Connection#setClientInfo(java.lang.String,java.lang.String) + */ + void setClientInfo(java.sql.Connection conn, String name, String value) throws SQLClientInfoException; +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ClientInfoProviderSP.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ClientInfoProviderSP.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ClientInfoProviderSP.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.util.Enumeration; +import java.util.Properties; + +import com.mysql.cj.conf.PropertyDefinitions; + +public class ClientInfoProviderSP implements ClientInfoProvider { + PreparedStatement setClientInfoSp; + + PreparedStatement getClientInfoSp; + + PreparedStatement getClientInfoBulkSp; + + public synchronized void initialize(java.sql.Connection conn, Properties configurationProps) throws SQLException { + String identifierQuote = ((com.mysql.cj.jdbc.JdbcConnection) conn).getSession().getIdentifierQuoteString(); + String setClientInfoSpName = configurationProps.getProperty(PropertyDefinitions.PNAME_clientInfoSetSPName, "setClientInfo"); + String getClientInfoSpName = configurationProps.getProperty(PropertyDefinitions.PNAME_clientInfoGetSPName, "getClientInfo"); + String getClientInfoBulkSpName = configurationProps.getProperty(PropertyDefinitions.PNAME_clientInfoGetBulkSPName, "getClientInfoBulk"); + String clientInfoCatalog = configurationProps.getProperty(PropertyDefinitions.PNAME_clientInfoCatalog, ""); // "" means use current from connection + + String catalog = "".equals(clientInfoCatalog) ? conn.getCatalog() : clientInfoCatalog; + + this.setClientInfoSp = ((com.mysql.cj.jdbc.JdbcConnection) conn).clientPrepareStatement( + "CALL " + identifierQuote + catalog + identifierQuote + "." + identifierQuote + setClientInfoSpName + identifierQuote + "(?, ?)"); + + this.getClientInfoSp = ((com.mysql.cj.jdbc.JdbcConnection) conn).clientPrepareStatement( + "CALL" + identifierQuote + catalog + identifierQuote + "." + identifierQuote + getClientInfoSpName + identifierQuote + "(?)"); + + this.getClientInfoBulkSp = ((com.mysql.cj.jdbc.JdbcConnection) conn).clientPrepareStatement( + "CALL " + identifierQuote + catalog + identifierQuote + "." + identifierQuote + getClientInfoBulkSpName + identifierQuote + "()"); + } + + public synchronized void destroy() throws SQLException { + if (this.setClientInfoSp != null) { + this.setClientInfoSp.close(); + this.setClientInfoSp = null; + } + + if (this.getClientInfoSp != null) { + this.getClientInfoSp.close(); + this.getClientInfoSp = null; + } + + if (this.getClientInfoBulkSp != null) { + this.getClientInfoBulkSp.close(); + this.getClientInfoBulkSp = null; + } + } + + public synchronized Properties getClientInfo(java.sql.Connection conn) throws SQLException { + ResultSet rs = null; + + Properties props = new Properties(); + + try { + this.getClientInfoBulkSp.execute(); + + rs = this.getClientInfoBulkSp.getResultSet(); + + while (rs.next()) { + props.setProperty(rs.getString(1), rs.getString(2)); + } + } finally { + if (rs != null) { + rs.close(); + } + } + + return props; + } + + public synchronized String getClientInfo(java.sql.Connection conn, String name) throws SQLException { + ResultSet rs = null; + + String clientInfo = null; + + try { + this.getClientInfoSp.setString(1, name); + this.getClientInfoSp.execute(); + + rs = this.getClientInfoSp.getResultSet(); + + if (rs.next()) { + clientInfo = rs.getString(1); + } + } finally { + if (rs != null) { + rs.close(); + } + } + + return clientInfo; + } + + public synchronized void setClientInfo(java.sql.Connection conn, Properties properties) throws SQLClientInfoException { + try { + Enumeration propNames = properties.propertyNames(); + + while (propNames.hasMoreElements()) { + String name = (String) propNames.nextElement(); + String value = properties.getProperty(name); + + setClientInfo(conn, name, value); + } + } catch (SQLException sqlEx) { + SQLClientInfoException clientInfoEx = new SQLClientInfoException(); + clientInfoEx.initCause(sqlEx); + + throw clientInfoEx; + } + } + + public synchronized void setClientInfo(java.sql.Connection conn, String name, String value) throws SQLClientInfoException { + try { + this.setClientInfoSp.setString(1, name); + this.setClientInfoSp.setString(2, value); + this.setClientInfoSp.execute(); + } catch (SQLException sqlEx) { + SQLClientInfoException clientInfoEx = new SQLClientInfoException(); + clientInfoEx.initCause(sqlEx); + + throw clientInfoEx; + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ClientPreparedStatement.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ClientPreparedStatement.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ClientPreparedStatement.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,2000 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URL; +import java.sql.Array; +import java.sql.Clob; +import java.sql.Date; +import java.sql.JDBCType; +import java.sql.NClob; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLType; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Wrapper; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import com.mysql.cj.BindValue; +import com.mysql.cj.CancelQueryTask; +import com.mysql.cj.CharsetMapping; +import com.mysql.cj.ClientPreparedQuery; +import com.mysql.cj.ClientPreparedQueryBindValue; +import com.mysql.cj.ClientPreparedQueryBindings; +import com.mysql.cj.Constants; +import com.mysql.cj.Messages; +import com.mysql.cj.MysqlType; +import com.mysql.cj.NativeSession; +import com.mysql.cj.ParseInfo; +import com.mysql.cj.PreparedQuery; +import com.mysql.cj.Query; +import com.mysql.cj.QueryBindings; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.ReadableProperty; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.FeatureNotAvailableException; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.exceptions.StatementIsClosedException; +import com.mysql.cj.jdbc.exceptions.MySQLStatementCancelledException; +import com.mysql.cj.jdbc.exceptions.MySQLTimeoutException; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping; +import com.mysql.cj.jdbc.result.CachedResultSetMetaData; +import com.mysql.cj.jdbc.result.ResultSetImpl; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; +import com.mysql.cj.jdbc.result.ResultSetMetaData; +import com.mysql.cj.log.ProfilerEvent; +import com.mysql.cj.log.ProfilerEventImpl; +import com.mysql.cj.protocol.ColumnDefinition; +import com.mysql.cj.protocol.Message; +import com.mysql.cj.protocol.a.NativePacketPayload; +import com.mysql.cj.protocol.a.result.ByteArrayRow; +import com.mysql.cj.protocol.a.result.ResultsetRowsStatic; +import com.mysql.cj.result.DefaultColumnDefinition; +import com.mysql.cj.result.Field; +import com.mysql.cj.result.Row; +import com.mysql.cj.util.StringUtils; +import com.mysql.cj.util.Util; + +/** + * A SQL Statement is pre-compiled and stored in a PreparedStatement object. This object can then be used to efficiently execute this statement multiple times. + * + *

+ * Note: The setXXX methods for setting IN parameter values must specify types that are compatible with the defined SQL type of the input parameter. For + * instance, if the IN parameter has SQL type Integer, then setInt should be used. + *

+ * + *

+ * If arbitrary parameter type conversions are required, then the setObject method should be used with a target SQL type. + *

+ */ +public class ClientPreparedStatement extends com.mysql.cj.jdbc.StatementImpl implements JdbcPreparedStatement { + + /** + * Does the batch (if any) contain "plain" statements added by + * Statement.addBatch(String)? + * + * If so, we can't re-write it to use multi-value or multi-queries. + */ + protected boolean batchHasPlainStatements = false; + + protected MysqlParameterMetadata parameterMetaData; + + private java.sql.ResultSetMetaData pstmtResultMetaData; + + protected String batchedValuesClause; + + private boolean doPingInstead; + + private boolean compensateForOnDuplicateKeyUpdate = false; + + protected ReadableProperty useStreamLengthsInPrepStmts; + protected ReadableProperty autoClosePStmtStreams; + + protected int rewrittenBatchSize = 0; + + /** + * Creates a prepared statement instance + */ + + protected static ClientPreparedStatement getInstance(JdbcConnection conn, String sql, String catalog) throws SQLException { + return new ClientPreparedStatement(conn, sql, catalog); + } + + /** + * Creates a prepared statement instance + */ + protected static ClientPreparedStatement getInstance(JdbcConnection conn, String sql, String catalog, ParseInfo cachedParseInfo) throws SQLException { + return new ClientPreparedStatement(conn, sql, catalog, cachedParseInfo); + } + + @Override + protected void initQuery() { + this.query = new ClientPreparedQuery(this.session); + } + + /** + * Constructor used by server-side prepared statements + * + * @param conn + * the connection that created us + * @param catalog + * the catalog in use when we were created + * + * @throws SQLException + * if an error occurs + */ + protected ClientPreparedStatement(JdbcConnection conn, String catalog) throws SQLException { + super(conn, catalog); + + this.compensateForOnDuplicateKeyUpdate = this.session.getPropertySet() + .getBooleanReadableProperty(PropertyDefinitions.PNAME_compensateOnDuplicateKeyUpdateCounts).getValue(); + this.useStreamLengthsInPrepStmts = this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useStreamLengthsInPrepStmts); + this.autoClosePStmtStreams = this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_autoClosePStmtStreams); + } + + /** + * Constructor for the PreparedStatement class. + * + * @param conn + * the connection creating this statement + * @param sql + * the SQL for this statement + * @param catalog + * the catalog/database this statement should be issued against + * + * @throws SQLException + * if a database error occurs. + */ + public ClientPreparedStatement(JdbcConnection conn, String sql, String catalog) throws SQLException { + this(conn, sql, catalog, null); + } + + /** + * Creates a new PreparedStatement object. + * + * @param conn + * the connection creating this statement + * @param sql + * the SQL for this statement + * @param catalog + * the catalog/database this statement should be issued against + * @param cachedParseInfo + * already created parseInfo or null. + * + * @throws SQLException + */ + public ClientPreparedStatement(JdbcConnection conn, String sql, String catalog, ParseInfo cachedParseInfo) throws SQLException { + this(conn, catalog); + + try { + ((PreparedQuery) this.query).checkNullOrEmptyQuery(sql); + ((PreparedQuery) this.query).setOriginalSql(sql); + ((PreparedQuery) this.query).setParseInfo(cachedParseInfo != null ? cachedParseInfo : new ParseInfo(sql, this.session, this.charEncoding)); + } catch (CJException e) { + throw SQLExceptionsMapping.translateException(e, this.exceptionInterceptor); + } + + this.doPingInstead = sql.startsWith(PING_MARKER); + + initializeFromParseInfo(); + + } + + public QueryBindings getQueryBindings() { + return ((PreparedQuery) this.query).getQueryBindings(); + } + + /** + * Returns this PreparedStatement represented as a string. + * + * @return this PreparedStatement represented as a string. + */ + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(this.getClass().getName()); + buf.append(": "); + + try { + buf.append(asSql()); + } catch (SQLException sqlEx) { + buf.append("EXCEPTION: " + sqlEx.toString()); + } + + return buf.toString(); + } + + public void addBatch() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + QueryBindings queryBindings = ((PreparedQuery) this.query).getQueryBindings(); + queryBindings.checkAllParametersSet(); + this.query.addBatch(queryBindings.clone()); + } + } + + @Override + public void addBatch(String sql) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.batchHasPlainStatements = true; + + super.addBatch(sql); + } + } + + public String asSql() throws SQLException { + return ((PreparedQuery) this.query).asSql(false); + } + + public String asSql(boolean quoteStreamsAndUnknowns) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return ((PreparedQuery) this.query).asSql(quoteStreamsAndUnknowns); + } + } + + @Override + public void clearBatch() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.batchHasPlainStatements = false; + + super.clearBatch(); + } + } + + /** + * In general, parameter values remain in force for repeated used of a + * Statement. Setting a parameter value automatically clears its previous + * value. However, in some cases, it is useful to immediately release the + * resources used by the current parameter values; this can be done by + * calling clearParameters + * + * @exception SQLException + * if a database access error occurs + */ + public void clearParameters() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + for (BindValue bv : ((PreparedQuery) this.query).getQueryBindings().getBindValues()) { + bv.setNull(false); + bv.setIsStream(false); + bv.setMysqlType(MysqlType.NULL); + bv.setByteValue(null); + bv.setStreamValue(null, 0); + } + } + } + + /** + * Check to see if the statement is safe for read-only slaves after failover. + * + * @return true if safe for read-only. + * @throws SQLException + */ + protected boolean checkReadOnlySafeStatement() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return ((PreparedQuery) this.query).getParseInfo().getFirstStmtChar() == 'S' || !this.connection.isReadOnly(); + } + } + + /** + * Some prepared statements return multiple results; the execute method + * handles these complex statements as well as the simpler form of + * statements handled by executeQuery and executeUpdate + * + * @return true if the next result is a ResultSet; false if it is an update + * count or there are no more results + * + * @exception SQLException + * if a database access error occurs + */ + public boolean execute() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + JdbcConnection locallyScopedConn = this.connection; + + if (!this.doPingInstead && !checkReadOnlySafeStatement()) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") + Messages.getString("PreparedStatement.21"), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + ResultSetInternalMethods rs = null; + + this.lastQueryIsOnDupKeyUpdate = false; + + if (this.retrieveGeneratedKeys) { + this.lastQueryIsOnDupKeyUpdate = containsOnDuplicateKeyUpdateInSQL(); + } + + this.batchedGeneratedKeys = null; + + resetCancelledState(); + + implicitlyCloseAllOpenResults(); + + clearWarnings(); + + if (this.doPingInstead) { + doPingInstead(); + + return true; + } + + setupStreamingTimeout(locallyScopedConn); + + Message sendPacket = ((PreparedQuery) this.query).fillSendPacket(); + + String oldCatalog = null; + + if (!locallyScopedConn.getCatalog().equals(this.getCurrentCatalog())) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(this.getCurrentCatalog()); + } + + // + // Check if we have cached metadata for this query... + // + CachedResultSetMetaData cachedMetadata = null; + + boolean cacheResultSetMetadata = locallyScopedConn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_cacheResultSetMetadata) + .getValue(); + if (cacheResultSetMetadata) { + cachedMetadata = locallyScopedConn.getCachedMetaData(((PreparedQuery) this.query).getOriginalSql()); + } + + // + // Only apply max_rows to selects + // + locallyScopedConn.setSessionMaxRows(((PreparedQuery) this.query).getParseInfo().getFirstStmtChar() == 'S' ? this.maxRows : -1); + + rs = executeInternal(this.maxRows, sendPacket, createStreamingResultSet(), + (((PreparedQuery) this.query).getParseInfo().getFirstStmtChar() == 'S'), cachedMetadata, false); + + if (cachedMetadata != null) { + locallyScopedConn.initializeResultsMetadataFromCache(((PreparedQuery) this.query).getOriginalSql(), cachedMetadata, rs); + } else { + if (rs.hasRows() && cacheResultSetMetadata) { + locallyScopedConn.initializeResultsMetadataFromCache(((PreparedQuery) this.query).getOriginalSql(), null /* will be created */, rs); + } + } + + if (this.retrieveGeneratedKeys) { + rs.setFirstCharOfQuery(((PreparedQuery) this.query).getParseInfo().getFirstStmtChar()); + } + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + + if (rs != null) { + this.lastInsertId = rs.getUpdateID(); + + this.results = rs; + } + + return ((rs != null) && rs.hasRows()); + } + } + + @Override + protected long[] executeBatchInternal() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (this.connection.isReadOnly()) { + throw new SQLException(Messages.getString("PreparedStatement.25") + Messages.getString("PreparedStatement.26"), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT); + } + + if (this.query.getBatchedArgs() == null || this.query.getBatchedArgs().size() == 0) { + return new long[0]; + } + + // we timeout the entire batch, not individual statements + int batchTimeout = getTimeoutInMillis(); + setTimeoutInMillis(0); + + resetCancelledState(); + + try { + statementBegins(); + + clearWarnings(); + + if (!this.batchHasPlainStatements && this.rewriteBatchedStatements.getValue()) { + + if (((PreparedQuery) this.query).getParseInfo().canRewriteAsMultiValueInsertAtSqlLevel()) { + return executeBatchedInserts(batchTimeout); + } + + if (!this.batchHasPlainStatements && this.query.getBatchedArgs() != null + && this.query.getBatchedArgs().size() > 3 /* cost of option setting rt-wise */) { + return executePreparedBatchAsMultiStatement(batchTimeout); + } + } + + return executeBatchSerially(batchTimeout); + } finally { + this.query.getStatementExecuting().set(false); + + clearBatch(); + } + } + } + + /** + * Rewrites the already prepared statement into a multi-statement + * query of 'statementsPerBatch' values and executes the entire batch + * using this new statement. + * + * @return update counts in the same fashion as executeBatch() + * + * @throws SQLException + */ + + protected long[] executePreparedBatchAsMultiStatement(int batchTimeout) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + // This is kind of an abuse, but it gets the job done + if (this.batchedValuesClause == null) { + this.batchedValuesClause = ((PreparedQuery) this.query).getOriginalSql() + ";"; + } + + JdbcConnection locallyScopedConn = this.connection; + + boolean multiQueriesEnabled = locallyScopedConn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_allowMultiQueries).getValue(); + CancelQueryTask timeoutTask = null; + + try { + clearWarnings(); + + int numBatchedArgs = this.query.getBatchedArgs().size(); + + if (this.retrieveGeneratedKeys) { + this.batchedGeneratedKeys = new ArrayList<>(numBatchedArgs); + } + + int numValuesPerBatch = ((PreparedQuery) this.query).computeBatchSize(numBatchedArgs); + + if (numBatchedArgs < numValuesPerBatch) { + numValuesPerBatch = numBatchedArgs; + } + + java.sql.PreparedStatement batchedStatement = null; + + int batchedParamIndex = 1; + int numberToExecuteAsMultiValue = 0; + int batchCounter = 0; + int updateCountCounter = 0; + long[] updateCounts = new long[numBatchedArgs]; + SQLException sqlEx = null; + + try { + if (!multiQueriesEnabled) { + ((NativeSession) locallyScopedConn.getSession()).enableMultiQueries(); + } + + batchedStatement = this.retrieveGeneratedKeys + ? ((Wrapper) locallyScopedConn.prepareStatement(generateMultiStatementForBatch(numValuesPerBatch), RETURN_GENERATED_KEYS)) + .unwrap(java.sql.PreparedStatement.class) + : ((Wrapper) locallyScopedConn.prepareStatement(generateMultiStatementForBatch(numValuesPerBatch))) + .unwrap(java.sql.PreparedStatement.class); + + timeoutTask = startQueryTimer((StatementImpl) batchedStatement, batchTimeout); + + numberToExecuteAsMultiValue = numBatchedArgs < numValuesPerBatch ? numBatchedArgs : numBatchedArgs / numValuesPerBatch; + + int numberArgsToExecute = numberToExecuteAsMultiValue * numValuesPerBatch; + + for (int i = 0; i < numberArgsToExecute; i++) { + if (i != 0 && i % numValuesPerBatch == 0) { + try { + batchedStatement.execute(); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(batchCounter, numValuesPerBatch, updateCounts, ex); + } + + updateCountCounter = processMultiCountsAndKeys((StatementImpl) batchedStatement, updateCountCounter, updateCounts); + + batchedStatement.clearParameters(); + batchedParamIndex = 1; + } + + batchedParamIndex = setOneBatchedParameterSet(batchedStatement, batchedParamIndex, this.query.getBatchedArgs().get(batchCounter++)); + } + + try { + batchedStatement.execute(); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, updateCounts, ex); + } + + updateCountCounter = processMultiCountsAndKeys((StatementImpl) batchedStatement, updateCountCounter, updateCounts); + + batchedStatement.clearParameters(); + + numValuesPerBatch = numBatchedArgs - batchCounter; + + if (timeoutTask != null) { + // we need to check the cancel state now because we loose if after the following batchedStatement.close() + ((JdbcPreparedStatement) batchedStatement).checkCancelTimeout(); + } + + } finally { + if (batchedStatement != null) { + batchedStatement.close(); + batchedStatement = null; + } + } + + try { + if (numValuesPerBatch > 0) { + + batchedStatement = this.retrieveGeneratedKeys + ? locallyScopedConn.prepareStatement(generateMultiStatementForBatch(numValuesPerBatch), RETURN_GENERATED_KEYS) + : locallyScopedConn.prepareStatement(generateMultiStatementForBatch(numValuesPerBatch)); + + if (timeoutTask != null) { + timeoutTask.setQueryToCancel((Query) batchedStatement); + } + + batchedParamIndex = 1; + + while (batchCounter < numBatchedArgs) { + batchedParamIndex = setOneBatchedParameterSet(batchedStatement, batchedParamIndex, this.query.getBatchedArgs().get(batchCounter++)); + } + + try { + batchedStatement.execute(); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, updateCounts, ex); + } + + updateCountCounter = processMultiCountsAndKeys((StatementImpl) batchedStatement, updateCountCounter, updateCounts); + + batchedStatement.clearParameters(); + } + + if (timeoutTask != null) { + stopQueryTimer(timeoutTask, true, true); + timeoutTask = null; + } + + if (sqlEx != null) { + throw SQLError.createBatchUpdateException(sqlEx, updateCounts, this.exceptionInterceptor); + } + + return updateCounts; + } finally { + if (batchedStatement != null) { + batchedStatement.close(); + } + } + } finally { + stopQueryTimer(timeoutTask, false, false); + resetCancelledState(); + + if (!multiQueriesEnabled) { + ((NativeSession) locallyScopedConn.getSession()).disableMultiQueries(); + } + + clearBatch(); + } + } + } + + protected int setOneBatchedParameterSet(java.sql.PreparedStatement batchedStatement, int batchedParamIndex, Object paramSet) throws SQLException { + QueryBindings paramArg = (QueryBindings) paramSet; + + BindValue[] bindValues = paramArg.getBindValues(); + + for (int j = 0; j < bindValues.length; j++) { + if (bindValues[j].isNull()) { + batchedStatement.setNull(batchedParamIndex++, MysqlType.NULL.getJdbcType()); + } else { + if (bindValues[j].isStream()) { + batchedStatement.setBinaryStream(batchedParamIndex++, bindValues[j].getStreamValue(), bindValues[j].getStreamLength()); + } else { + ((JdbcPreparedStatement) batchedStatement).setBytesNoEscapeNoQuotes(batchedParamIndex++, bindValues[j].getByteValue()); + } + } + } + + return batchedParamIndex; + } + + private String generateMultiStatementForBatch(int numBatches) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + String origSql = ((PreparedQuery) this.query).getOriginalSql(); + StringBuilder newStatementSql = new StringBuilder((origSql.length() + 1) * numBatches); + + newStatementSql.append(origSql); + + for (int i = 0; i < numBatches - 1; i++) { + newStatementSql.append(';'); + newStatementSql.append(origSql); + } + + return newStatementSql.toString(); + } + } + + /** + * Rewrites the already prepared statement into a multi-value insert + * statement of 'statementsPerBatch' values and executes the entire batch + * using this new statement. + * + * @return update counts in the same fashion as executeBatch() + * + * @throws SQLException + */ + protected long[] executeBatchedInserts(int batchTimeout) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + String valuesClause = ((PreparedQuery) this.query).getParseInfo().getValuesClause(); + + JdbcConnection locallyScopedConn = this.connection; + + if (valuesClause == null) { + return executeBatchSerially(batchTimeout); + } + + int numBatchedArgs = this.query.getBatchedArgs().size(); + + if (this.retrieveGeneratedKeys) { + this.batchedGeneratedKeys = new ArrayList<>(numBatchedArgs); + } + + int numValuesPerBatch = ((PreparedQuery) this.query).computeBatchSize(numBatchedArgs); + + if (numBatchedArgs < numValuesPerBatch) { + numValuesPerBatch = numBatchedArgs; + } + + JdbcPreparedStatement batchedStatement = null; + + int batchedParamIndex = 1; + long updateCountRunningTotal = 0; + int numberToExecuteAsMultiValue = 0; + int batchCounter = 0; + CancelQueryTask timeoutTask = null; + SQLException sqlEx = null; + + long[] updateCounts = new long[numBatchedArgs]; + + try { + try { + batchedStatement = /* FIXME -if we ever care about folks proxying our JdbcConnection */ + prepareBatchedInsertSQL(locallyScopedConn, numValuesPerBatch); + + timeoutTask = startQueryTimer(batchedStatement, batchTimeout); + + numberToExecuteAsMultiValue = numBatchedArgs < numValuesPerBatch ? numBatchedArgs : numBatchedArgs / numValuesPerBatch; + + int numberArgsToExecute = numberToExecuteAsMultiValue * numValuesPerBatch; + + for (int i = 0; i < numberArgsToExecute; i++) { + if (i != 0 && i % numValuesPerBatch == 0) { + try { + updateCountRunningTotal += batchedStatement.executeLargeUpdate(); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, updateCounts, ex); + } + + getBatchedGeneratedKeys(batchedStatement); + batchedStatement.clearParameters(); + batchedParamIndex = 1; + + } + + batchedParamIndex = setOneBatchedParameterSet(batchedStatement, batchedParamIndex, this.query.getBatchedArgs().get(batchCounter++)); + } + + try { + updateCountRunningTotal += batchedStatement.executeLargeUpdate(); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, updateCounts, ex); + } + + getBatchedGeneratedKeys(batchedStatement); + + numValuesPerBatch = numBatchedArgs - batchCounter; + } finally { + if (batchedStatement != null) { + batchedStatement.close(); + batchedStatement = null; + } + } + + try { + if (numValuesPerBatch > 0) { + batchedStatement = prepareBatchedInsertSQL(locallyScopedConn, numValuesPerBatch); + + if (timeoutTask != null) { + timeoutTask.setQueryToCancel(batchedStatement); + } + + batchedParamIndex = 1; + + while (batchCounter < numBatchedArgs) { + batchedParamIndex = setOneBatchedParameterSet(batchedStatement, batchedParamIndex, this.query.getBatchedArgs().get(batchCounter++)); + } + + try { + updateCountRunningTotal += batchedStatement.executeLargeUpdate(); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, updateCounts, ex); + } + + getBatchedGeneratedKeys(batchedStatement); + } + + if (sqlEx != null) { + throw SQLError.createBatchUpdateException(sqlEx, updateCounts, this.exceptionInterceptor); + } + + if (numBatchedArgs > 1) { + long updCount = updateCountRunningTotal > 0 ? java.sql.Statement.SUCCESS_NO_INFO : 0; + for (int j = 0; j < numBatchedArgs; j++) { + updateCounts[j] = updCount; + } + } else { + updateCounts[0] = updateCountRunningTotal; + } + return updateCounts; + } finally { + if (batchedStatement != null) { + batchedStatement.close(); + } + } + } finally { + stopQueryTimer(timeoutTask, false, false); + resetCancelledState(); + } + } + } + + /** + * Executes the current batch of statements by executing them one-by-one. + * + * @return a list of update counts + * @throws SQLException + * if an error occurs + */ + protected long[] executeBatchSerially(int batchTimeout) throws SQLException { + + synchronized (checkClosed().getConnectionMutex()) { + if (this.connection == null) { + checkClosed(); + } + + long[] updateCounts = null; + + if (this.query.getBatchedArgs() != null) { + int nbrCommands = this.query.getBatchedArgs().size(); + updateCounts = new long[nbrCommands]; + + for (int i = 0; i < nbrCommands; i++) { + updateCounts[i] = -3; + } + + SQLException sqlEx = null; + + CancelQueryTask timeoutTask = null; + + try { + timeoutTask = startQueryTimer(this, batchTimeout); + + if (this.retrieveGeneratedKeys) { + this.batchedGeneratedKeys = new ArrayList<>(nbrCommands); + } + + int batchCommandIndex = ((PreparedQuery) this.query).getBatchCommandIndex(); + + for (batchCommandIndex = 0; batchCommandIndex < nbrCommands; batchCommandIndex++) { + + ((PreparedQuery) this.query).setBatchCommandIndex(batchCommandIndex); + + Object arg = this.query.getBatchedArgs().get(batchCommandIndex); + + try { + if (arg instanceof String) { + updateCounts[batchCommandIndex] = executeUpdateInternal((String) arg, true, this.retrieveGeneratedKeys); + + // limit one generated key per OnDuplicateKey statement + getBatchedGeneratedKeys(this.results.getFirstCharOfQuery() == 'I' && containsOnDuplicateKeyInString((String) arg) ? 1 : 0); + } else { + QueryBindings queryBindings = (QueryBindings) arg; + updateCounts[batchCommandIndex] = executeUpdateInternal(queryBindings, true); + + // limit one generated key per OnDuplicateKey statement + getBatchedGeneratedKeys(containsOnDuplicateKeyUpdateInSQL() ? 1 : 0); + } + } catch (SQLException ex) { + updateCounts[batchCommandIndex] = EXECUTE_FAILED; + + if (this.continueBatchOnError && !(ex instanceof MySQLTimeoutException) && !(ex instanceof MySQLStatementCancelledException) + && !hasDeadlockOrTimeoutRolledBackTx(ex)) { + sqlEx = ex; + } else { + long[] newUpdateCounts = new long[batchCommandIndex]; + System.arraycopy(updateCounts, 0, newUpdateCounts, 0, batchCommandIndex); + + throw SQLError.createBatchUpdateException(ex, newUpdateCounts, this.exceptionInterceptor); + } + } + } + + if (sqlEx != null) { + throw SQLError.createBatchUpdateException(sqlEx, updateCounts, this.exceptionInterceptor); + } + } catch (NullPointerException npe) { + try { + checkClosed(); + } catch (StatementIsClosedException connectionClosedEx) { + int batchCommandIndex = ((PreparedQuery) this.query).getBatchCommandIndex(); + updateCounts[batchCommandIndex] = EXECUTE_FAILED; + + long[] newUpdateCounts = new long[batchCommandIndex]; + + System.arraycopy(updateCounts, 0, newUpdateCounts, 0, batchCommandIndex); + + throw SQLError.createBatchUpdateException(SQLExceptionsMapping.translateException(connectionClosedEx), newUpdateCounts, + this.exceptionInterceptor); + } + + throw npe; // we don't know why this happened, punt + } finally { + ((PreparedQuery) this.query).setBatchCommandIndex(-1); + + stopQueryTimer(timeoutTask, false, false); + resetCancelledState(); + } + } + + return (updateCounts != null) ? updateCounts : new long[0]; + } + + } + + /** + * Actually execute the prepared statement. This is here so server-side + * PreparedStatements can re-use most of the code from this class. + * + * @param maxRowsToRetrieve + * the max number of rows to return + * @param sendPacket + * the packet to send + * @param createStreamingResultSet + * should a 'streaming' result set be created? + * @param queryIsSelectOnly + * is this query doing a SELECT? + * @param metadata + * use this metadata instead of the one provided on wire + * @param isBatch + * + * @return the results as a ResultSet + * + * @throws SQLException + * if an error occurs. + */ + protected ResultSetInternalMethods executeInternal(int maxRowsToRetrieve, M sendPacket, boolean createStreamingResultSet, + boolean queryIsSelectOnly, ColumnDefinition metadata, boolean isBatch) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + try { + + JdbcConnection locallyScopedConnection = this.connection; + + ((PreparedQuery) this.query).getQueryBindings() + .setNumberOfExecutions(((PreparedQuery) this.query).getQueryBindings().getNumberOfExecutions() + 1); + + ResultSetInternalMethods rs; + + CancelQueryTask timeoutTask = null; + + try { + timeoutTask = startQueryTimer(this, getTimeoutInMillis()); + + if (!isBatch) { + statementBegins(); + } + + rs = ((NativeSession) locallyScopedConnection.getSession()).execSQL(this, null, maxRowsToRetrieve, (NativePacketPayload) sendPacket, + createStreamingResultSet, getResultSetFactory(), this.getCurrentCatalog(), metadata, isBatch); + + if (timeoutTask != null) { + stopQueryTimer(timeoutTask, true, true); + timeoutTask = null; + } + + } finally { + if (!isBatch) { + this.query.getStatementExecuting().set(false); + } + + stopQueryTimer(timeoutTask, false, false); + } + + return rs; + } catch (NullPointerException npe) { + checkClosed(); // we can't synchronize ourselves against async connection-close due to deadlock issues, so this is the next best thing for + // this particular corner case. + + throw npe; + } + } + } + + /** + * A Prepared SQL query is executed and its ResultSet is returned + * + * @return a ResultSet that contains the data produced by the query - never + * null + * + * @exception SQLException + * if a database access error occurs + */ + public java.sql.ResultSet executeQuery() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + JdbcConnection locallyScopedConn = this.connection; + + checkForDml(((PreparedQuery) this.query).getOriginalSql(), ((PreparedQuery) this.query).getParseInfo().getFirstStmtChar()); + + this.batchedGeneratedKeys = null; + + resetCancelledState(); + + implicitlyCloseAllOpenResults(); + + clearWarnings(); + + if (this.doPingInstead) { + doPingInstead(); + + return this.results; + } + + setupStreamingTimeout(locallyScopedConn); + + Message sendPacket = ((PreparedQuery) this.query).fillSendPacket(); + + String oldCatalog = null; + + if (!locallyScopedConn.getCatalog().equals(this.getCurrentCatalog())) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(this.getCurrentCatalog()); + } + + // + // Check if we have cached metadata for this query... + // + CachedResultSetMetaData cachedMetadata = null; + boolean cacheResultSetMetadata = locallyScopedConn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_cacheResultSetMetadata) + .getValue(); + + String origSql = ((PreparedQuery) this.query).getOriginalSql(); + + if (cacheResultSetMetadata) { + cachedMetadata = locallyScopedConn.getCachedMetaData(origSql); + } + + locallyScopedConn.setSessionMaxRows(this.maxRows); + + this.results = executeInternal(this.maxRows, sendPacket, createStreamingResultSet(), true, cachedMetadata, false); + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + + if (cachedMetadata != null) { + locallyScopedConn.initializeResultsMetadataFromCache(origSql, cachedMetadata, this.results); + } else { + if (cacheResultSetMetadata) { + locallyScopedConn.initializeResultsMetadataFromCache(origSql, null /* will be created */, this.results); + } + } + + this.lastInsertId = this.results.getUpdateID(); + + return this.results; + } + } + + /** + * Execute a SQL INSERT, UPDATE or DELETE statement. In addition, SQL + * statements that return nothing such as SQL DDL statements can be + * executed. + * + * @return either the row count for INSERT, UPDATE or DELETE; or 0 for SQL + * statements that return nothing. + * + * @exception SQLException + * if a database access error occurs + */ + public int executeUpdate() throws SQLException { + return Util.truncateAndConvertToInt(executeLargeUpdate()); + } + + /* + * We need this variant, because ServerPreparedStatement calls this for + * batched updates, which will end up clobbering the warnings and generated + * keys we need to gather for the batch. + */ + protected long executeUpdateInternal(boolean clearBatchedGeneratedKeysAndWarnings, boolean isBatch) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (clearBatchedGeneratedKeysAndWarnings) { + clearWarnings(); + this.batchedGeneratedKeys = null; + } + + return executeUpdateInternal(((PreparedQuery) this.query).getQueryBindings(), isBatch); + } + } + + /** + * Added to allow batch-updates + * + * @param bindings + * @param isReallyBatch + * + * @return the update count + * + * @throws SQLException + * if a database error occurs + */ + protected long executeUpdateInternal(QueryBindings bindings, boolean isReallyBatch) throws SQLException { + + synchronized (checkClosed().getConnectionMutex()) { + + JdbcConnection locallyScopedConn = this.connection; + + if (locallyScopedConn.isReadOnly(false)) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.34") + Messages.getString("PreparedStatement.35"), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + if ((((PreparedQuery) this.query).getParseInfo().getFirstStmtChar() == 'S') && isSelectQuery()) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.37"), "01S03", this.exceptionInterceptor); + } + + resetCancelledState(); + + implicitlyCloseAllOpenResults(); + + ResultSetInternalMethods rs = null; + + Message sendPacket = ((PreparedQuery) this.query).fillSendPacket(bindings); + + String oldCatalog = null; + + if (!locallyScopedConn.getCatalog().equals(this.getCurrentCatalog())) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(this.getCurrentCatalog()); + } + + // + // Only apply max_rows to selects + // + locallyScopedConn.setSessionMaxRows(-1); + + rs = executeInternal(-1, sendPacket, false, false, null, isReallyBatch); + + if (this.retrieveGeneratedKeys) { + rs.setFirstCharOfQuery(((PreparedQuery) this.query).getParseInfo().getFirstStmtChar()); + } + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + + this.results = rs; + + this.updateCount = rs.getUpdateCount(); + + if (containsOnDuplicateKeyUpdateInSQL() && this.compensateForOnDuplicateKeyUpdate) { + if (this.updateCount == 2 || this.updateCount == 0) { + this.updateCount = 1; + } + } + + this.lastInsertId = rs.getUpdateID(); + + return this.updateCount; + } + } + + protected boolean containsOnDuplicateKeyUpdateInSQL() { + return ((PreparedQuery) this.query).getParseInfo().containsOnDuplicateKeyUpdateInSQL(); + } + + /** + * Returns a prepared statement for the number of batched parameters, used when re-writing batch INSERTs. + */ + protected ClientPreparedStatement prepareBatchedInsertSQL(JdbcConnection localConn, int numBatches) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ClientPreparedStatement pstmt = new ClientPreparedStatement(localConn, "Rewritten batch of: " + ((PreparedQuery) this.query).getOriginalSql(), + this.getCurrentCatalog(), ((PreparedQuery) this.query).getParseInfo().getParseInfoForBatch(numBatches)); + pstmt.setRetrieveGeneratedKeys(this.retrieveGeneratedKeys); + pstmt.rewrittenBatchSize = numBatches; + + return pstmt; + } + } + + protected void setRetrieveGeneratedKeys(boolean flag) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.retrieveGeneratedKeys = flag; + } + } + + /** + * @param parameterIndex + * + * @throws SQLException + */ + public byte[] getBytesRepresentation(int parameterIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return ((ClientPreparedQuery) this.query).getBytesRepresentation(parameterIndex); + } + } + + /** + * Get bytes representation for a parameter in a statement batch. + * + * @param parameterIndex + * @param commandIndex + * @throws SQLException + */ + protected byte[] getBytesRepresentationForBatch(int parameterIndex, int commandIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return ((ClientPreparedQuery) this.query).getBytesRepresentationForBatch(parameterIndex, commandIndex); + } + } + + /** + * The number, types, and properties of a ResultSet's columns are provided by + * the getMetaData method. + * + * @return the description of a ResultSet's columns + * + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.ResultSetMetaData getMetaData() throws SQLException { + + synchronized (checkClosed().getConnectionMutex()) { + // + // We could just tack on a LIMIT 0 here no matter what the statement, and check if a result set was returned or not, but I'm not comfortable with + // that, myself, so we take the "safer" road, and only allow metadata for _actual_ SELECTS (but not SHOWs). + // + // CALL's are trapped further up and you end up with a CallableStatement anyway. + // + + if (!isSelectQuery()) { + return null; + } + + JdbcPreparedStatement mdStmt = null; + java.sql.ResultSet mdRs = null; + + if (this.pstmtResultMetaData == null) { + try { + mdStmt = new ClientPreparedStatement(this.connection, ((PreparedQuery) this.query).getOriginalSql(), this.getCurrentCatalog(), + ((PreparedQuery) this.query).getParseInfo()); + + mdStmt.setMaxRows(1); + + int paramCount = ((PreparedQuery) this.query).getParameterCount(); + + for (int i = 1; i <= paramCount; i++) { + mdStmt.setString(i, ""); + } + + boolean hadResults = mdStmt.execute(); + + if (hadResults) { + mdRs = mdStmt.getResultSet(); + + this.pstmtResultMetaData = mdRs.getMetaData(); + } else { + this.pstmtResultMetaData = new ResultSetMetaData(this.session, new Field[0], + this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useOldAliasMetadataBehavior).getValue(), + this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_yearIsDateType).getValue(), + this.exceptionInterceptor); + } + } finally { + SQLException sqlExRethrow = null; + + if (mdRs != null) { + try { + mdRs.close(); + } catch (SQLException sqlEx) { + sqlExRethrow = sqlEx; + } + + mdRs = null; + } + + if (mdStmt != null) { + try { + mdStmt.close(); + } catch (SQLException sqlEx) { + sqlExRethrow = sqlEx; + } + + mdStmt = null; + } + + if (sqlExRethrow != null) { + throw sqlExRethrow; + } + } + } + + return this.pstmtResultMetaData; + } + } + + protected boolean isSelectQuery() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return StringUtils.startsWithIgnoreCaseAndWs( + StringUtils.stripComments(((PreparedQuery) this.query).getOriginalSql(), "'\"", "'\"", true, false, true, true), "SELECT"); + } + } + + /** + * @see PreparedStatement#getParameterMetaData() + */ + public ParameterMetaData getParameterMetaData() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.parameterMetaData == null) { + if (this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_generateSimpleParameterMetadata).getValue()) { + this.parameterMetaData = new MysqlParameterMetadata(((PreparedQuery) this.query).getParameterCount()); + } else { + this.parameterMetaData = new MysqlParameterMetadata(this.session, null, ((PreparedQuery) this.query).getParameterCount(), + this.exceptionInterceptor); + } + } + + return this.parameterMetaData; + } + } + + public ParseInfo getParseInfo() { + return ((PreparedQuery) this.query).getParseInfo(); + } + + @SuppressWarnings("unchecked") + private void initializeFromParseInfo() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + int parameterCount = ((PreparedQuery) this.query).getParseInfo().getStaticSql().length - 1; + ((PreparedQuery) this.query).setParameterCount(parameterCount); + ((PreparedQuery) this.query).setQueryBindings(new ClientPreparedQueryBindings(parameterCount, this.session)); + ((ClientPreparedQuery) this.query).getQueryBindings().setLoadDataQuery(((PreparedQuery) this.query).getParseInfo().isFoundLoadData()); + + clearParameters(); + } + } + + public boolean isNull(int paramIndex) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return ((PreparedQuery) this.query).getQueryBindings().getBindValues()[paramIndex].isNull(); + } + } + + /** + * Closes this statement, releasing all resources + * + * @param calledExplicitly + * was this called by close()? + * + * @throws SQLException + * if an error occurs + */ + @Override + public void realClose(boolean calledExplicitly, boolean closeOpenResults) throws SQLException { + JdbcConnection locallyScopedConn = this.connection; + + if (locallyScopedConn == null) { + return; // already closed + } + + synchronized (locallyScopedConn.getConnectionMutex()) { + + // additional check in case Statement was closed + // while current thread was waiting for lock + if (this.isClosed) { + return; + } + + if (this.useUsageAdvisor) { + if (((PreparedQuery) this.query).getQueryBindings().getNumberOfExecutions() <= 1) { + String message = Messages.getString("PreparedStatement.43"); + + this.query.getEventSink() + .consumeEvent(new ProfilerEventImpl(ProfilerEvent.TYPE_WARN, "", this.getCurrentCatalog(), this.session.getThreadId(), this.getId(), + -1, System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, message)); + } + } + + super.realClose(calledExplicitly, closeOpenResults); + + ((PreparedQuery) this.query).setOriginalSql(null); + ((PreparedQuery) this.query).setQueryBindings(null); + } + } + + public String getPreparedSql() { + synchronized (checkClosed().getConnectionMutex()) { + if (this.rewrittenBatchSize == 0) { + return ((PreparedQuery) this.query).getOriginalSql(); + } + + try { + return ((PreparedQuery) this.query).getParseInfo().getSqlForBatch(); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public int getUpdateCount() throws SQLException { + int count = super.getUpdateCount(); + + if (containsOnDuplicateKeyUpdateInSQL() && this.compensateForOnDuplicateKeyUpdate) { + if (count == 2 || count == 0) { + count = 1; + } + } + + return count; + } + + /** + * JDBC 4.2 + * Same as PreparedStatement.executeUpdate() but returns long instead of int. + */ + public long executeLargeUpdate() throws SQLException { + return executeUpdateInternal(true, false); + } + + public ParameterBindings getParameterBindings() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return new EmulatedPreparedStatementBindings(); + } + } + + /** + * For calling stored functions, this will be -1 as Connector/J does not count + * the first '?' parameter marker, but JDBC counts it * as 1, otherwise it will return 0 + */ + protected int getParameterIndexOffset() { + return 0; + } + + protected void checkBounds(int paramIndex, int parameterIndexOffset) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if ((paramIndex < 1)) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.49") + paramIndex + Messages.getString("PreparedStatement.50"), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } else if (paramIndex > ((PreparedQuery) this.query).getParameterCount()) { + throw SQLError.createSQLException( + Messages.getString("PreparedStatement.51") + paramIndex + Messages.getString("PreparedStatement.52") + + ((PreparedQuery) this.query).getParameterCount() + Messages.getString("PreparedStatement.53"), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } else if (parameterIndexOffset == -1 && paramIndex == 1) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.63"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + } + } + + protected final int getCoreParameterIndex(int paramIndex) throws SQLException { + int parameterIndexOffset = getParameterIndexOffset(); + checkBounds(paramIndex, parameterIndexOffset); + return paramIndex - 1 + parameterIndexOffset; + } + + public void setArray(int i, Array x) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setAsciiStream(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setAsciiStream(getCoreParameterIndex(parameterIndex), x, length); + } + } + + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setAsciiStream(getCoreParameterIndex(parameterIndex), x, length); + } + } + + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setBigDecimal(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setBinaryStream(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setBinaryStream(getCoreParameterIndex(parameterIndex), x, length); + } + } + + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setBinaryStream(getCoreParameterIndex(parameterIndex), x, length); + } + } + + public void setBlob(int i, java.sql.Blob x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setBlob(getCoreParameterIndex(i), x); + } + } + + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setBlob(getCoreParameterIndex(parameterIndex), inputStream); + } + } + + public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setBlob(getCoreParameterIndex(parameterIndex), inputStream, length); + } + } + + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setBoolean(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setByte(int parameterIndex, byte x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setByte(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setBytes(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setBytes(int parameterIndex, byte[] x, boolean checkForIntroducer, boolean escapeForMBChars) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setBytes(getCoreParameterIndex(parameterIndex), x, checkForIntroducer, escapeForMBChars); + } + } + + /** + * Used by updatable result sets for refreshRow() because the parameter has + * already been escaped for updater or inserter prepared statements. + * + * @param parameterIndex + * the parameter to set. + * @param parameterAsBytes + * the parameter as a string. + * + * @throws SQLException + * if an error occurs + */ + public void setBytesNoEscape(int parameterIndex, byte[] parameterAsBytes) throws SQLException { + ((PreparedQuery) this.query).getQueryBindings().setBytesNoEscape(getCoreParameterIndex(parameterIndex), parameterAsBytes); + } + + public void setBytesNoEscapeNoQuotes(int parameterIndex, byte[] parameterAsBytes) throws SQLException { + ((PreparedQuery) this.query).getQueryBindings().setBytesNoEscapeNoQuotes(getCoreParameterIndex(parameterIndex), parameterAsBytes); + } + + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setCharacterStream(getCoreParameterIndex(parameterIndex), reader); + } + } + + public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setCharacterStream(getCoreParameterIndex(parameterIndex), reader, length); + } + } + + public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setCharacterStream(getCoreParameterIndex(parameterIndex), reader, length); + } + } + + public void setClob(int parameterIndex, Reader reader) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setCharacterStream(getCoreParameterIndex(parameterIndex), reader); + } + } + + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setCharacterStream(getCoreParameterIndex(parameterIndex), reader, length); + } + } + + public void setClob(int i, Clob x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setClob(getCoreParameterIndex(i), x); + } + } + + public void setDate(int parameterIndex, Date x) throws java.sql.SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setDate(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setDate(getCoreParameterIndex(parameterIndex), x, cal); + } + } + + public void setDouble(int parameterIndex, double x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setDouble(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setFloat(int parameterIndex, float x) throws SQLException { + ((PreparedQuery) this.query).getQueryBindings().setFloat(getCoreParameterIndex(parameterIndex), x); + } + + public void setInt(int parameterIndex, int x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setInt(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setLong(int parameterIndex, long x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setLong(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setBigInteger(int parameterIndex, BigInteger x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setBigInteger(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setNCharacterStream(getCoreParameterIndex(parameterIndex), value); + } + } + + public void setNCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setNCharacterStream(getCoreParameterIndex(parameterIndex), reader, length); + } + } + + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setNClob(getCoreParameterIndex(parameterIndex), reader); + } + } + + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setNClob(getCoreParameterIndex(parameterIndex), reader, length); + } + } + + public void setNClob(int parameterIndex, NClob value) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setNClob(getCoreParameterIndex(parameterIndex), value); + } + } + + /** + * Set a parameter to a Java String value. The driver converts this to a SQL + * VARCHAR or LONGVARCHAR value with introducer _utf8 (depending on the + * arguments size relative to the driver's limits on VARCHARs) when it sends + * it to the database. If charset is set as utf8, this method just call setString. + * + * @param parameterIndex + * the first parameter is 1... + * @param x + * the parameter value + * + * @exception SQLException + * if a database access error occurs + */ + public void setNString(int parameterIndex, String x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setNString(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setNull(int parameterIndex, int sqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setNull(getCoreParameterIndex(parameterIndex)); // MySQL ignores sqlType + } + } + + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setNull(getCoreParameterIndex(parameterIndex)); + } + } + + public void setNull(int parameterIndex, MysqlType mysqlType) throws SQLException { + setNull(parameterIndex, mysqlType.getJdbcType()); + } + + public void setObject(int parameterIndex, Object parameterObj) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setObject(getCoreParameterIndex(parameterIndex), parameterObj); + } + } + + public void setObject(int parameterIndex, Object parameterObj, int targetSqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + try { + ((PreparedQuery) this.query).getQueryBindings().setObject(getCoreParameterIndex(parameterIndex), parameterObj, + MysqlType.getByJdbcType(targetSqlType)); + } catch (FeatureNotAvailableException nae) { + throw SQLError.createSQLFeatureNotSupportedException(Messages.getString("Statement.UnsupportedSQLType") + JDBCType.valueOf(targetSqlType), + MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, this.exceptionInterceptor); + } + } + } + + public void setObject(int parameterIndex, Object parameterObj, SQLType targetSqlType) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (targetSqlType instanceof MysqlType) { + ((PreparedQuery) this.query).getQueryBindings().setObject(getCoreParameterIndex(parameterIndex), parameterObj, (MysqlType) targetSqlType); + } else { + setObject(parameterIndex, parameterObj, targetSqlType.getVendorTypeNumber()); + } + } + } + + public void setObject(int parameterIndex, Object parameterObj, int targetSqlType, int scale) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + try { + ((PreparedQuery) this.query).getQueryBindings().setObject(getCoreParameterIndex(parameterIndex), parameterObj, + MysqlType.getByJdbcType(targetSqlType), scale); + } catch (FeatureNotAvailableException nae) { + throw SQLError.createSQLFeatureNotSupportedException(Messages.getString("Statement.UnsupportedSQLType") + JDBCType.valueOf(targetSqlType), + MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, this.exceptionInterceptor); + } + } + } + + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (targetSqlType instanceof MysqlType) { + ((PreparedQuery) this.query).getQueryBindings().setObject(getCoreParameterIndex(parameterIndex), x, (MysqlType) targetSqlType, + scaleOrLength); + } else { + setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber(), scaleOrLength); + } + } + } + + public void setRef(int i, Ref x) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + public void setRowId(int parameterIndex, RowId x) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + public void setShort(int parameterIndex, short x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setShort(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + if (xmlObject == null) { + setNull(parameterIndex, MysqlType.VARCHAR); + } else { + // FIXME: Won't work for Non-MYSQL SQLXMLs + setCharacterStream(parameterIndex, ((MysqlSQLXML) xmlObject).serializeAsCharacterStream()); + } + } + + public void setString(int parameterIndex, String x) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setString(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setTime(int parameterIndex, Time x) throws java.sql.SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setTime(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setTime(int parameterIndex, java.sql.Time x, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setTime(getCoreParameterIndex(parameterIndex), x, cal); + } + } + + public void setTimestamp(int parameterIndex, Timestamp x) throws java.sql.SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setTimestamp(getCoreParameterIndex(parameterIndex), x); + } + } + + public void setTimestamp(int parameterIndex, java.sql.Timestamp x, Calendar cal) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings().setTimestamp(getCoreParameterIndex(parameterIndex), x, cal); + } + } + + @Deprecated + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + setBinaryStream(parameterIndex, x, length); + ((PreparedQuery) this.query).getQueryBindings().getBindValues()[getCoreParameterIndex(parameterIndex)].setMysqlType(MysqlType.TEXT); // TODO was Types.CLOB + } + + public void setURL(int parameterIndex, URL arg) throws SQLException { + if (arg == null) { + setNull(parameterIndex, MysqlType.VARCHAR); + } else { + setString(parameterIndex, arg.toString()); + ((PreparedQuery) this.query).getQueryBindings().getBindValues()[getCoreParameterIndex(parameterIndex)].setMysqlType(MysqlType.VARCHAR); // TODO was Types.DATALINK + } + } + + class EmulatedPreparedStatementBindings implements ParameterBindings { + + private ResultSetImpl bindingsAsRs; + private ClientPreparedQueryBindValue[] bindValues; + + EmulatedPreparedStatementBindings() throws SQLException { + List rows = new ArrayList<>(); + int paramCount = ((PreparedQuery) ClientPreparedStatement.this.query).getParameterCount(); + this.bindValues = new ClientPreparedQueryBindValue[paramCount]; + for (int i = 0; i < paramCount; i++) { + this.bindValues[i] = ((ClientPreparedQueryBindings) ((PreparedQuery) ClientPreparedStatement.this.query).getQueryBindings()) + .getBindValues()[i].clone(); + + } + byte[][] rowData = new byte[paramCount][]; + Field[] typeMetadata = new Field[paramCount]; + + for (int i = 0; i < paramCount; i++) { + int batchCommandIndex = ((PreparedQuery) ClientPreparedStatement.this.query).getBatchCommandIndex(); + rowData[i] = batchCommandIndex == -1 ? getBytesRepresentation(i) : getBytesRepresentationForBatch(i, batchCommandIndex); + + int charsetIndex = 0; + + switch (((PreparedQuery) ClientPreparedStatement.this.query).getQueryBindings().getBindValues()[i].getMysqlType()) { + case BINARY: + case BLOB: + case GEOMETRY: + case LONGBLOB: + case MEDIUMBLOB: + case TINYBLOB: + case UNKNOWN: + case VARBINARY: + charsetIndex = CharsetMapping.MYSQL_COLLATION_INDEX_binary; + break; + default: + try { + charsetIndex = CharsetMapping + .getCollationIndexForJavaEncoding( + ClientPreparedStatement.this.session.getPropertySet() + .getStringReadableProperty(PropertyDefinitions.PNAME_characterEncoding).getValue(), + ClientPreparedStatement.this.session.getServerSession().getServerVersion()); + } catch (RuntimeException ex) { + throw SQLError.createSQLException(ex.toString(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, ex, null); + } + break; + } + + Field parameterMetadata = new Field(null, "parameter_" + (i + 1), charsetIndex, ClientPreparedStatement.this.charEncoding, + ((PreparedQuery) ClientPreparedStatement.this.query).getQueryBindings().getBindValues()[i].getMysqlType(), rowData[i].length); + typeMetadata[i] = parameterMetadata; + } + + rows.add(new ByteArrayRow(rowData, ClientPreparedStatement.this.exceptionInterceptor)); + + this.bindingsAsRs = ClientPreparedStatement.this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, + ResultSet.TYPE_SCROLL_INSENSITIVE, new ResultsetRowsStatic(rows, new DefaultColumnDefinition(typeMetadata))); + this.bindingsAsRs.next(); + } + + public Array getArray(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getArray(parameterIndex); + } + + public InputStream getAsciiStream(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getAsciiStream(parameterIndex); + } + + public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getBigDecimal(parameterIndex); + } + + public InputStream getBinaryStream(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getBinaryStream(parameterIndex); + } + + public java.sql.Blob getBlob(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getBlob(parameterIndex); + } + + public boolean getBoolean(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getBoolean(parameterIndex); + } + + public byte getByte(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getByte(parameterIndex); + } + + public byte[] getBytes(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getBytes(parameterIndex); + } + + public Reader getCharacterStream(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getCharacterStream(parameterIndex); + } + + public java.sql.Clob getClob(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getClob(parameterIndex); + } + + public Date getDate(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getDate(parameterIndex); + } + + public double getDouble(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getDouble(parameterIndex); + } + + public float getFloat(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getFloat(parameterIndex); + } + + public int getInt(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getInt(parameterIndex); + } + + public BigInteger getBigInteger(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getBigInteger(parameterIndex); + } + + public long getLong(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getLong(parameterIndex); + } + + public Reader getNCharacterStream(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getCharacterStream(parameterIndex); + } + + public Reader getNClob(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getCharacterStream(parameterIndex); + } + + public Object getObject(int parameterIndex) throws SQLException { + checkBounds(parameterIndex, 0); + + if (this.bindValues[parameterIndex - 1].isNull()) { + return null; + } + + // we can't rely on the default mapping for JDBC's ResultSet.getObject() for numerics, they're not one-to-one with PreparedStatement.setObject + + switch (((PreparedQuery) ClientPreparedStatement.this.query).getQueryBindings().getBindValues()[parameterIndex - 1].getMysqlType()) { + case TINYINT: + case TINYINT_UNSIGNED: + return Byte.valueOf(getByte(parameterIndex)); + case SMALLINT: + case SMALLINT_UNSIGNED: + return Short.valueOf(getShort(parameterIndex)); + case INT: + case INT_UNSIGNED: + return Integer.valueOf(getInt(parameterIndex)); + case BIGINT: + return Long.valueOf(getLong(parameterIndex)); + case BIGINT_UNSIGNED: + return getBigInteger(parameterIndex); + case FLOAT: + case FLOAT_UNSIGNED: + return Float.valueOf(getFloat(parameterIndex)); + case DOUBLE: + case DOUBLE_UNSIGNED: + return Double.valueOf(getDouble(parameterIndex)); + default: + return this.bindingsAsRs.getObject(parameterIndex); + } + } + + public Ref getRef(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getRef(parameterIndex); + } + + public short getShort(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getShort(parameterIndex); + } + + public String getString(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getString(parameterIndex); + } + + public Time getTime(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getTime(parameterIndex); + } + + public Timestamp getTimestamp(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getTimestamp(parameterIndex); + } + + public URL getURL(int parameterIndex) throws SQLException { + return this.bindingsAsRs.getURL(parameterIndex); + } + + public boolean isNull(int parameterIndex) throws SQLException { + checkBounds(parameterIndex, 0); + + return this.bindValues[parameterIndex - 1].isNull(); + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/Clob.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/Clob.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/Clob.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.sql.SQLException; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.protocol.OutputStreamWatcher; +import com.mysql.cj.protocol.WatchableOutputStream; +import com.mysql.cj.protocol.WatchableStream; +import com.mysql.cj.protocol.WatchableWriter; +import com.mysql.cj.protocol.WriterWatcher; +import com.mysql.cj.util.StringUtils; + +/** + * Simplistic implementation of java.sql.Clob for MySQL Connector/J + */ +public class Clob implements java.sql.Clob, OutputStreamWatcher, WriterWatcher { + private String charData; + private ExceptionInterceptor exceptionInterceptor; + + Clob(ExceptionInterceptor exceptionInterceptor) { + this.charData = ""; + this.exceptionInterceptor = exceptionInterceptor; + } + + public Clob(String charDataInit, ExceptionInterceptor exceptionInterceptor) { + this.charData = charDataInit; + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * @see java.sql.Clob#getAsciiStream() + */ + public InputStream getAsciiStream() throws SQLException { + if (this.charData != null) { + return new ByteArrayInputStream(StringUtils.getBytes(this.charData)); + } + + return null; + } + + /** + * @see java.sql.Clob#getCharacterStream() + */ + public Reader getCharacterStream() throws SQLException { + if (this.charData != null) { + return new StringReader(this.charData); + } + + return null; + } + + /** + * @see java.sql.Clob#getSubString(long, int) + */ + public String getSubString(long startPos, int length) throws SQLException { + if (startPos < 1) { + throw SQLError.createSQLException(Messages.getString("Clob.6"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + int adjustedStartPos = (int) startPos - 1; + int adjustedEndIndex = adjustedStartPos + length; + + if (this.charData != null) { + if (adjustedEndIndex > this.charData.length()) { + throw SQLError.createSQLException(Messages.getString("Clob.7"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + return this.charData.substring(adjustedStartPos, adjustedEndIndex); + } + + return null; + } + + /** + * @see java.sql.Clob#length() + */ + public long length() throws SQLException { + if (this.charData != null) { + return this.charData.length(); + } + + return 0; + } + + /** + * @see java.sql.Clob#position(Clob, long) + */ + public long position(java.sql.Clob arg0, long arg1) throws SQLException { + return position(arg0.getSubString(1L, (int) arg0.length()), arg1); + } + + /** + * @see java.sql.Clob#position(String, long) + */ + public long position(String stringToFind, long startPos) throws SQLException { + if (startPos < 1) { + throw SQLError.createSQLException(Messages.getString("Clob.8", new Object[] { startPos }), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + + if (this.charData != null) { + if ((startPos - 1) > this.charData.length()) { + throw SQLError.createSQLException(Messages.getString("Clob.10"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + int pos = this.charData.indexOf(stringToFind, (int) (startPos - 1)); + + return (pos == -1) ? (-1) : (pos + 1); + } + + return -1; + } + + /** + * @see java.sql.Clob#setAsciiStream(long) + */ + public OutputStream setAsciiStream(long indexToWriteAt) throws SQLException { + if (indexToWriteAt < 1) { + throw SQLError.createSQLException(Messages.getString("Clob.0"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + WatchableOutputStream bytesOut = new WatchableOutputStream(); + bytesOut.setWatcher(this); + + if (indexToWriteAt > 0) { + bytesOut.write(StringUtils.getBytes(this.charData), 0, (int) (indexToWriteAt - 1)); + } + + return bytesOut; + } + + /** + * @see java.sql.Clob#setCharacterStream(long) + */ + public Writer setCharacterStream(long indexToWriteAt) throws SQLException { + if (indexToWriteAt < 1) { + throw SQLError.createSQLException(Messages.getString("Clob.1"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + WatchableWriter writer = new WatchableWriter(); + writer.setWatcher(this); + + // + // Don't call write() if nothing to write... + // + if (indexToWriteAt > 1) { + writer.write(this.charData, 0, (int) (indexToWriteAt - 1)); + } + + return writer; + } + + /** + * @see java.sql.Clob#setString(long, String) + */ + public int setString(long pos, String str) throws SQLException { + if (pos < 1) { + throw SQLError.createSQLException(Messages.getString("Clob.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + if (str == null) { + throw SQLError.createSQLException(Messages.getString("Clob.3"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + StringBuilder charBuf = new StringBuilder(this.charData); + + pos--; + + int strLength = str.length(); + + charBuf.replace((int) pos, (int) (pos + strLength), str); + + this.charData = charBuf.toString(); + + return strLength; + } + + /** + * @see java.sql.Clob#setString(long, String, int, int) + */ + public int setString(long pos, String str, int offset, int len) throws SQLException { + if (pos < 1) { + throw SQLError.createSQLException(Messages.getString("Clob.4"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + if (str == null) { + throw SQLError.createSQLException(Messages.getString("Clob.5"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + StringBuilder charBuf = new StringBuilder(this.charData); + + pos--; + + try { + String replaceString = str.substring(offset, offset + len); + + charBuf.replace((int) pos, (int) (pos + replaceString.length()), replaceString); + } catch (StringIndexOutOfBoundsException e) { + throw SQLError.createSQLException(e.getMessage(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, e, this.exceptionInterceptor); + } + + this.charData = charBuf.toString(); + + return len; + } + + public void streamClosed(WatchableStream out) { + int streamSize = out.size(); + + if (streamSize < this.charData.length()) { + out.write(StringUtils.getBytes(this.charData), streamSize, this.charData.length() - streamSize); + } + + this.charData = StringUtils.toAsciiString(out.toByteArray()); + } + + /** + * @see java.sql.Clob#truncate(long) + */ + public void truncate(long length) throws SQLException { + if (length > this.charData.length()) { + throw SQLError.createSQLException( + Messages.getString("Clob.11") + this.charData.length() + Messages.getString("Clob.12") + length + Messages.getString("Clob.13"), + this.exceptionInterceptor); + } + + this.charData = this.charData.substring(0, (int) length); + } + + public void writerClosed(char[] charDataBeingWritten) { + this.charData = new String(charDataBeingWritten); + } + + public void writerClosed(WatchableWriter out) { + int dataLength = out.size(); + + if (dataLength < this.charData.length()) { + out.write(this.charData, dataLength, this.charData.length() - dataLength); + } + + this.charData = out.toString(); + } + + public void free() throws SQLException { + this.charData = null; + } + + public Reader getCharacterStream(long pos, long length) throws SQLException { + return new StringReader(getSubString(pos, (int) length)); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/CommentClientInfoProvider.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/CommentClientInfoProvider.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/CommentClientInfoProvider.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + +/** + * An implementation of ClientInfoProvider that exposes the client info as a comment prepended to all statements issued by the driver. + * + * Client information is never read from the server with this implementation, it is always cached locally. + */ + +public class CommentClientInfoProvider implements ClientInfoProvider { + private Properties clientInfo; + + public synchronized void initialize(java.sql.Connection conn, Properties configurationProps) throws SQLException { + this.clientInfo = new Properties(); + } + + public synchronized void destroy() throws SQLException { + this.clientInfo = null; + } + + public synchronized Properties getClientInfo(java.sql.Connection conn) throws SQLException { + return this.clientInfo; + } + + public synchronized String getClientInfo(java.sql.Connection conn, String name) throws SQLException { + return this.clientInfo.getProperty(name); + } + + public synchronized void setClientInfo(java.sql.Connection conn, Properties properties) throws SQLClientInfoException { + this.clientInfo = new Properties(); + + Enumeration propNames = properties.propertyNames(); + + while (propNames.hasMoreElements()) { + String name = (String) propNames.nextElement(); + + this.clientInfo.put(name, properties.getProperty(name)); + } + + setComment(conn); + } + + public synchronized void setClientInfo(java.sql.Connection conn, String name, String value) throws SQLClientInfoException { + this.clientInfo.setProperty(name, value); + setComment(conn); + } + + private synchronized void setComment(java.sql.Connection conn) { + StringBuilder commentBuf = new StringBuilder(); + Iterator> elements = this.clientInfo.entrySet().iterator(); + + while (elements.hasNext()) { + if (commentBuf.length() > 0) { + commentBuf.append(", "); + } + + Map.Entry entry = elements.next(); + commentBuf.append("" + entry.getKey()); + commentBuf.append("="); + commentBuf.append("" + entry.getValue()); + } + + ((com.mysql.cj.jdbc.JdbcConnection) conn).setStatementComment(commentBuf.toString()); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ConnectionGroup.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ConnectionGroup.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ConnectionGroup.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.mysql.cj.Messages; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.ha.LoadBalancedConnectionProxy; + +public class ConnectionGroup { + private String groupName; + private long connections = 0; + private long activeConnections = 0; + private HashMap connectionProxies = new HashMap<>(); + private Set hostList = new HashSet<>(); + private boolean isInitialized = false; + private long closedProxyTotalPhysicalConnections = 0; + private long closedProxyTotalTransactions = 0; + private int activeHosts = 0; + private Set closedHosts = new HashSet<>(); + + ConnectionGroup(String groupName) { + this.groupName = groupName; + } + + public long registerConnectionProxy(LoadBalancedConnectionProxy proxy, List localHostList) { + long currentConnectionId; + + synchronized (this) { + if (!this.isInitialized) { + this.hostList.addAll(localHostList); + this.isInitialized = true; + this.activeHosts = localHostList.size(); + } + currentConnectionId = ++this.connections; + this.connectionProxies.put(Long.valueOf(currentConnectionId), proxy); + } + this.activeConnections++; + + return currentConnectionId; + + } + + public String getGroupName() { + return this.groupName; + } + + public Collection getInitialHosts() { + return this.hostList; + } + + public int getActiveHostCount() { + return this.activeHosts; + } + + public Collection getClosedHosts() { + return this.closedHosts; + } + + public long getTotalLogicalConnectionCount() { + return this.connections; + } + + public long getActiveLogicalConnectionCount() { + return this.activeConnections; + } + + public long getActivePhysicalConnectionCount() { + long result = 0; + Map proxyMap = new HashMap<>(); + synchronized (this.connectionProxies) { + proxyMap.putAll(this.connectionProxies); + } + for (LoadBalancedConnectionProxy proxy : proxyMap.values()) { + result += proxy.getActivePhysicalConnectionCount(); + } + return result; + } + + public long getTotalPhysicalConnectionCount() { + long allConnections = this.closedProxyTotalPhysicalConnections; + Map proxyMap = new HashMap<>(); + synchronized (this.connectionProxies) { + proxyMap.putAll(this.connectionProxies); + } + for (LoadBalancedConnectionProxy proxy : proxyMap.values()) { + allConnections += proxy.getTotalPhysicalConnectionCount(); + } + return allConnections; + } + + public long getTotalTransactionCount() { + // need to account for closed connection proxies + long transactions = this.closedProxyTotalTransactions; + Map proxyMap = new HashMap<>(); + synchronized (this.connectionProxies) { + proxyMap.putAll(this.connectionProxies); + } + for (LoadBalancedConnectionProxy proxy : proxyMap.values()) { + transactions += proxy.getTransactionCount(); + } + return transactions; + } + + public void closeConnectionProxy(LoadBalancedConnectionProxy proxy) { + this.activeConnections--; + this.connectionProxies.remove(Long.valueOf(proxy.getConnectionGroupProxyID())); + this.closedProxyTotalPhysicalConnections += proxy.getTotalPhysicalConnectionCount(); + this.closedProxyTotalTransactions += proxy.getTransactionCount(); + + } + + /** + * Remove the given host (host:port pair) from this Connection Group. + * + * @param hostPortPair + * The host:port pair to remove. + * @throws SQLException + */ + public void removeHost(String hostPortPair) throws SQLException { + removeHost(hostPortPair, false); + } + + /** + * Remove the given host (host:port pair) from this Connection Group. + * + * @param hostPortPair + * The host:port pair to remove. + * @param removeExisting + * Whether affects existing load-balanced connections or only new ones. + * @throws SQLException + */ + public void removeHost(String hostPortPair, boolean removeExisting) throws SQLException { + this.removeHost(hostPortPair, removeExisting, true); + } + + /** + * Remove the given host (host:port pair) from this Connection Group and, consequently, from all the load-balanced connections it holds. + * + * @param hostPortPair + * The host:port pair to remove. + * @param removeExisting + * Whether affects existing load-balanced connections or only new ones. + * @param waitForGracefulFailover + * If true instructs the load-balanced connections to fail-over the underlying active connection before removing this host, otherwise remove + * immediately. + * @throws SQLException + */ + public synchronized void removeHost(String hostPortPair, boolean removeExisting, boolean waitForGracefulFailover) throws SQLException { + if (this.activeHosts == 1) { + throw SQLError.createSQLException(Messages.getString("ConnectionGroup.0"), null); + } + + if (this.hostList.remove(hostPortPair)) { + this.activeHosts--; + } else { + throw SQLError.createSQLException(Messages.getString("ConnectionGroup.1", new Object[] { hostPortPair }), null); + } + + if (removeExisting) { + // make a local copy to keep synchronization overhead to minimum + Map proxyMap = new HashMap<>(); + synchronized (this.connectionProxies) { + proxyMap.putAll(this.connectionProxies); + } + + for (LoadBalancedConnectionProxy proxy : proxyMap.values()) { + if (waitForGracefulFailover) { + proxy.removeHostWhenNotInUse(hostPortPair); + } else { + proxy.removeHost(hostPortPair); + } + } + } + this.closedHosts.add(hostPortPair); + } + + /** + * Add the given host (host:port pair) to this Connection Group. + * + * @param hostPortPair + * The host:port pair to add. + * @throws SQLException + */ + public void addHost(String hostPortPair) { + addHost(hostPortPair, false); + } + + /** + * Add the given host (host:port pair) to this Connection Group and, consequently, to all the load-balanced connections it holds. + * + * @param hostPortPair + * The host:port pair to add. + * @param forExisting + * Whether affects existing load-balanced connections or only new ones. + */ + public void addHost(String hostPortPair, boolean forExisting) { + synchronized (this) { + if (this.hostList.add(hostPortPair)) { + this.activeHosts++; + } + } + // all new connections will have this host + if (!forExisting) { + return; + } + + // make a local copy to keep synchronization overhead to minimum + Map proxyMap = new HashMap<>(); + synchronized (this.connectionProxies) { + proxyMap.putAll(this.connectionProxies); + } + + for (LoadBalancedConnectionProxy proxy : proxyMap.values()) { + proxy.addHost(hostPortPair); + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ConnectionGroupManager.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ConnectionGroupManager.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ConnectionGroupManager.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.mysql.cj.jdbc.jmx.LoadBalanceConnectionGroupManager; + +public class ConnectionGroupManager { + + private static HashMap GROUP_MAP = new HashMap<>(); + + private static LoadBalanceConnectionGroupManager mbean = new LoadBalanceConnectionGroupManager(); + + private static boolean hasRegisteredJmx = false; + + public static synchronized ConnectionGroup getConnectionGroupInstance(String groupName) { + if (GROUP_MAP.containsKey(groupName)) { + return GROUP_MAP.get(groupName); + } + ConnectionGroup group = new ConnectionGroup(groupName); + GROUP_MAP.put(groupName, group); + return group; + } + + public static void registerJmx() throws SQLException { + if (hasRegisteredJmx) { + return; + } + + mbean.registerJmx(); + hasRegisteredJmx = true; + } + + public static ConnectionGroup getConnectionGroup(String groupName) { + return GROUP_MAP.get(groupName); + } + + private static Collection getGroupsMatching(String group) { + if (group == null || group.equals("")) { + Set s = new HashSet<>(); + + s.addAll(GROUP_MAP.values()); + return s; + } + Set s = new HashSet<>(); + ConnectionGroup o = GROUP_MAP.get(group); + if (o != null) { + s.add(o); + } + return s; + + } + + public static void addHost(String group, String hostPortPair, boolean forExisting) { + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + cg.addHost(hostPortPair, forExisting); + } + } + + public static int getActiveHostCount(String group) { + + Set active = new HashSet<>(); + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + active.addAll(cg.getInitialHosts()); + } + return active.size(); + } + + public static long getActiveLogicalConnectionCount(String group) { + int count = 0; + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + count += cg.getActiveLogicalConnectionCount(); + } + return count; + } + + public static long getActivePhysicalConnectionCount(String group) { + int count = 0; + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + count += cg.getActivePhysicalConnectionCount(); + } + return count; + } + + public static int getTotalHostCount(String group) { + Collection s = getGroupsMatching(group); + Set hosts = new HashSet<>(); + for (ConnectionGroup cg : s) { + hosts.addAll(cg.getInitialHosts()); + hosts.addAll(cg.getClosedHosts()); + } + return hosts.size(); + } + + public static long getTotalLogicalConnectionCount(String group) { + long count = 0; + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + count += cg.getTotalLogicalConnectionCount(); + } + return count; + } + + public static long getTotalPhysicalConnectionCount(String group) { + long count = 0; + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + count += cg.getTotalPhysicalConnectionCount(); + } + return count; + } + + public static long getTotalTransactionCount(String group) { + long count = 0; + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + count += cg.getTotalTransactionCount(); + } + return count; + } + + public static void removeHost(String group, String hostPortPair) throws SQLException { + removeHost(group, hostPortPair, false); + } + + public static void removeHost(String group, String host, boolean removeExisting) throws SQLException { + Collection s = getGroupsMatching(group); + for (ConnectionGroup cg : s) { + cg.removeHost(host, removeExisting); + } + } + + public static String getActiveHostLists(String group) { + Collection s = getGroupsMatching(group); + Map hosts = new HashMap<>(); + for (ConnectionGroup cg : s) { + + Collection l = cg.getInitialHosts(); + for (String host : l) { + Integer o = hosts.get(host); + if (o == null) { + o = Integer.valueOf(1); + } else { + o = Integer.valueOf(o.intValue() + 1); + } + hosts.put(host, o); + + } + } + + StringBuilder sb = new StringBuilder(); + String sep = ""; + for (String host : hosts.keySet()) { + sb.append(sep); + sb.append(host); + sb.append('('); + sb.append(hosts.get(host)); + sb.append(')'); + sep = ","; + } + return sb.toString(); + } + + public static String getRegisteredConnectionGroups() { + Collection s = getGroupsMatching(null); + StringBuilder sb = new StringBuilder(); + String sep = ""; + for (ConnectionGroup cg : s) { + String group = cg.getGroupName(); + sb.append(sep); + sb.append(group); + sep = ","; + } + return sb.toString(); + + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ConnectionImpl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ConnectionImpl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ConnectionImpl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,3045 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.Serializable; +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationHandler; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.NClob; +import java.sql.ResultSet; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLPermission; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Struct; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Random; +import java.util.Stack; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; + +import com.mysql.cj.CacheAdapter; +import com.mysql.cj.CacheAdapterFactory; +import com.mysql.cj.Constants; +import com.mysql.cj.LicenseConfiguration; +import com.mysql.cj.Messages; +import com.mysql.cj.NativeSession; +import com.mysql.cj.NoSubInterceptorWrapper; +import com.mysql.cj.ParseInfo; +import com.mysql.cj.PreparedQuery; +import com.mysql.cj.ServerVersion; +import com.mysql.cj.Session.SessionEventListener; +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.conf.ModifiableProperty; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.ReadableProperty; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.ExceptionInterceptorChain; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.exceptions.PasswordExpiredException; +import com.mysql.cj.exceptions.UnableToConnectException; +import com.mysql.cj.interceptors.QueryInterceptor; +import com.mysql.cj.jdbc.exceptions.CommunicationsException; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping; +import com.mysql.cj.jdbc.ha.MultiHostMySQLConnection; +import com.mysql.cj.jdbc.interceptors.ConnectionLifecycleInterceptor; +import com.mysql.cj.jdbc.result.CachedResultSetMetaData; +import com.mysql.cj.jdbc.result.CachedResultSetMetaDataImpl; +import com.mysql.cj.jdbc.result.ResultSetFactory; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; +import com.mysql.cj.jdbc.result.UpdatableResultSet; +import com.mysql.cj.log.ProfilerEvent; +import com.mysql.cj.log.ProfilerEventHandlerFactory; +import com.mysql.cj.log.ProfilerEventImpl; +import com.mysql.cj.log.StandardLogger; +import com.mysql.cj.protocol.SocksProxySocketFactory; +import com.mysql.cj.util.LRUCache; +import com.mysql.cj.util.LogUtils; +import com.mysql.cj.util.StringUtils; +import com.mysql.cj.util.Util; + +/** + * A Connection represents a session with a specific database. Within the context of a Connection, SQL statements are executed and results are returned. + * + *

+ * A Connection's database is able to provide information describing its tables, its supported SQL grammar, its stored procedures, the capabilities of this + * connection, etc. This information is obtained with the getMetaData method. + *

+ */ +public class ConnectionImpl implements JdbcConnection, SessionEventListener, Serializable { + + private static final long serialVersionUID = 4009476458425101761L; + + private static final SQLPermission SET_NETWORK_TIMEOUT_PERM = new SQLPermission("setNetworkTimeout"); + + private static final SQLPermission ABORT_PERM = new SQLPermission("abort"); + + public String getHost() { + return this.session.getHostInfo().getHost(); + } + + private JdbcConnection proxy = null; + private InvocationHandler realProxy = null; + + public boolean isProxySet() { + return this.proxy != null; + } + + public void setProxy(JdbcConnection proxy) { + this.proxy = proxy; + this.realProxy = this.proxy instanceof MultiHostMySQLConnection ? ((MultiHostMySQLConnection) proxy).getThisAsProxy() : null; + } + + // this connection has to be proxied when using multi-host settings so that statements get routed to the right physical connection + // (works as "logical" connection) + private JdbcConnection getProxy() { + return (this.proxy != null) ? this.proxy : (JdbcConnection) this; + } + + public JdbcConnection getMultiHostSafeProxy() { + return this.getProxy(); + } + + public JdbcConnection getActiveMySQLConnection() { + return this; + } + + public Object getConnectionMutex() { + return (this.realProxy != null) ? this.realProxy : getProxy(); + } + + /** + * Used as a key for caching callable statements which (may) depend on + * current catalog...In 5.0.x, they don't (currently), but stored procedure + * names soon will, so current catalog is a (hidden) component of the name. + */ + static class CompoundCacheKey { + final String componentOne; + + final String componentTwo; + + final int hashCode; + + CompoundCacheKey(String partOne, String partTwo) { + this.componentOne = partOne; + this.componentTwo = partTwo; + + int hc = 17; + hc = 31 * hc + (this.componentOne != null ? this.componentOne.hashCode() : 0); + hc = 31 * hc + (this.componentTwo != null ? this.componentTwo.hashCode() : 0); + this.hashCode = hc; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj != null && CompoundCacheKey.class.isAssignableFrom(obj.getClass())) { + CompoundCacheKey another = (CompoundCacheKey) obj; + if (this.componentOne == null ? another.componentOne == null : this.componentOne.equals(another.componentOne)) { + return this.componentTwo == null ? another.componentTwo == null : this.componentTwo.equals(another.componentTwo); + } + } + return false; + } + + @Override + public int hashCode() { + return this.hashCode; + } + } + + /** + * The mapping between MySQL charset names and Java charset names. + * Initialized by loadCharacterSetMapping() + */ + public static Map charsetMap; + + /** Default logger class name */ + protected static final String DEFAULT_LOGGER_CLASS = StandardLogger.class.getName(); + + /** + * Map mysql transaction isolation level name to + * java.sql.Connection.TRANSACTION_XXX + */ + private static Map mapTransIsolationNameToValue = null; + + protected static Map roundRobinStatsMap; + + private List connectionLifecycleInterceptors; + + private static final int DEFAULT_RESULT_SET_TYPE = ResultSet.TYPE_FORWARD_ONLY; + + private static final int DEFAULT_RESULT_SET_CONCURRENCY = ResultSet.CONCUR_READ_ONLY; + + static { + mapTransIsolationNameToValue = new HashMap<>(8); + mapTransIsolationNameToValue.put("READ-UNCOMMITED", TRANSACTION_READ_UNCOMMITTED); + mapTransIsolationNameToValue.put("READ-UNCOMMITTED", TRANSACTION_READ_UNCOMMITTED); + mapTransIsolationNameToValue.put("READ-COMMITTED", TRANSACTION_READ_COMMITTED); + mapTransIsolationNameToValue.put("REPEATABLE-READ", TRANSACTION_REPEATABLE_READ); + mapTransIsolationNameToValue.put("SERIALIZABLE", TRANSACTION_SERIALIZABLE); + } + + /** + * Creates a connection instance + */ + public static JdbcConnection getInstance(HostInfo hostInfo) throws SQLException { + return new ConnectionImpl(hostInfo); + } + + private static final Random random = new Random(); + + /** + * @param url + * @param hostList + */ + protected static synchronized int getNextRoundRobinHostIndex(String url, List hostList) { + // we really do "random" here, because you don't get even distribution when this is coupled with connection pools + + int indexRange = hostList.size(); + + int index = random.nextInt(indexRange); + + return index; + } + + private static boolean nullSafeCompare(String s1, String s2) { + if (s1 == null && s2 == null) { + return true; + } + + if (s1 == null && s2 != null) { + return false; + } + + return s1 != null && s1.equals(s2); + } + + /** A cache of SQL to parsed prepared statement parameters. */ + private CacheAdapter cachedPreparedStatementParams; + + /** The database we're currently using (called Catalog in JDBC terms). */ + private String database = null; + + /** Internal DBMD to use for various database-version specific features */ + private DatabaseMetaData dbmd = null; + + private NativeSession session = null; + + /** Is this connection associated with a global tx? */ + private boolean isInGlobalTx = false; + + /** isolation level */ + private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED; + + /** When did the master fail? */ + // private long masterFailTimeMillis = 0L; + + /** + * An array of currently open statements. + * Copy-on-write used here to avoid ConcurrentModificationException when statements unregister themselves while we iterate over the list. + */ + private final CopyOnWriteArrayList openStatements = new CopyOnWriteArrayList<>(); + + private LRUCache parsedCallableStatementCache; + + /** The password we used */ + private String password = null; + + /** Point of origin where this Connection was created */ + private String pointOfOrigin; + + /** Properties for this connection specified by user */ + protected Properties props = null; + + /** Are we in read-only mode? */ + private boolean readOnly = false; + + /** Cache of ResultSet metadata */ + protected LRUCache resultSetMetadataCache; + + /** + * The type map for UDTs (not implemented, but used by some third-party + * vendors, most notably IBM WebSphere) + */ + private Map> typeMap; + + /** The user we're connected as */ + private String user = null; + + private LRUCache serverSideStatementCheckCache; + private LRUCache serverSideStatementCache; + + private HostInfo origHostInfo; + + private String origHostToConnectTo; + + // we don't want to be able to publicly clone this... + + private int origPortToConnectTo; + + /* + * For testing failover scenarios + */ + private boolean hasTriedMasterFlag = false; + + private List queryInterceptors; + + protected JdbcPropertySet propertySet; + + private ReadableProperty autoReconnectForPools; + private ReadableProperty cachePrepStmts; + private ModifiableProperty autoReconnect; + private ReadableProperty useUsageAdvisor; + private ReadableProperty reconnectAtTxEnd; + private ReadableProperty emulateUnsupportedPstmts; + private ReadableProperty ignoreNonTxTables; + private ReadableProperty pedantic; + private ReadableProperty prepStmtCacheSqlLimit; + private ReadableProperty useLocalSessionState; + private ReadableProperty useServerPrepStmts; + private ReadableProperty processEscapeCodesForPrepStmts; + private ReadableProperty useLocalTransactionState; + private ReadableProperty disconnectOnExpiredPasswords; + private ReadableProperty readOnlyPropagatesToServer; + + protected ResultSetFactory nullStatementResultSetFactory; + + /** + * ' + * For the delegate only + */ + protected ConnectionImpl() { + } + + /** + * Creates a connection to a MySQL Server. + * + * @param hostInfo + * the {@link HostInfo} instance that contains the host, user and connections attributes for this connection + * @exception SQLException + * if a database access error occurs + */ + public ConnectionImpl(HostInfo hostInfo) throws SQLException { + + try { + // Stash away for later, used to clone this connection for Statement.cancel and Statement.setQueryTimeout(). + this.origHostInfo = hostInfo; + this.origHostToConnectTo = hostInfo.getHost(); + this.origPortToConnectTo = hostInfo.getPort(); + + this.database = hostInfo.getDatabase(); + this.user = StringUtils.isNullOrEmpty(hostInfo.getUser()) ? "" : hostInfo.getUser(); + this.password = StringUtils.isNullOrEmpty(hostInfo.getPassword()) ? "" : hostInfo.getPassword(); + + this.props = hostInfo.exposeAsProperties(); + + this.propertySet = new JdbcPropertySetImpl(); + + this.propertySet.initializeProperties(this.props); + + // We need Session ASAP to get access to central driver functionality + this.nullStatementResultSetFactory = new ResultSetFactory(this, null); + this.session = new NativeSession(hostInfo, this.propertySet); + this.session.addListener(this); // listen for session status changes + + // we can't cache fixed values here because properties are still not initialized with user provided values + this.autoReconnectForPools = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_autoReconnectForPools); + this.cachePrepStmts = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_cachePrepStmts); + this.autoReconnect = this.propertySet. getModifiableProperty(PropertyDefinitions.PNAME_autoReconnect); + this.useUsageAdvisor = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_useUsageAdvisor); + this.reconnectAtTxEnd = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_reconnectAtTxEnd); + this.emulateUnsupportedPstmts = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_emulateUnsupportedPstmts); + this.ignoreNonTxTables = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_ignoreNonTxTables); + this.pedantic = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_pedantic); + this.prepStmtCacheSqlLimit = this.propertySet.getIntegerReadableProperty(PropertyDefinitions.PNAME_prepStmtCacheSqlLimit); + this.useLocalSessionState = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_useLocalSessionState); + this.useServerPrepStmts = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_useServerPrepStmts); + this.processEscapeCodesForPrepStmts = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_processEscapeCodesForPrepStmts); + this.useLocalTransactionState = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_useLocalTransactionState); + this.disconnectOnExpiredPasswords = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_disconnectOnExpiredPasswords); + this.readOnlyPropagatesToServer = this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_readOnlyPropagatesToServer); + + String exceptionInterceptorClasses = this.propertySet.getStringReadableProperty(PropertyDefinitions.PNAME_exceptionInterceptors).getStringValue(); + if (exceptionInterceptorClasses != null && !"".equals(exceptionInterceptorClasses)) { + this.exceptionInterceptor = new ExceptionInterceptorChain(exceptionInterceptorClasses, this.props, this.session.getLog()); + } + + if (this.cachePrepStmts.getValue()) { + createPreparedStatementCaches(); + } + + if (this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_cacheCallableStmts).getValue()) { + this.parsedCallableStatementCache = new LRUCache<>( + this.propertySet.getIntegerReadableProperty(PropertyDefinitions.PNAME_callableStmtCacheSize).getValue()); + } + + if (this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_allowMultiQueries).getValue()) { + this.propertySet. getModifiableProperty(PropertyDefinitions.PNAME_cacheResultSetMetadata).setValue(false); // we don't handle this yet + } + + if (this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_cacheResultSetMetadata).getValue()) { + this.resultSetMetadataCache = new LRUCache<>( + this.propertySet.getIntegerReadableProperty(PropertyDefinitions.PNAME_metadataCacheSize).getValue()); + } + + if (this.propertySet.getStringReadableProperty(PropertyDefinitions.PNAME_socksProxyHost).getStringValue() != null) { + this.propertySet.getJdbcModifiableProperty(PropertyDefinitions.PNAME_socketFactory).setValue(SocksProxySocketFactory.class.getName()); + } + + this.pointOfOrigin = this.useUsageAdvisor.getValue() ? LogUtils.findCallingClassAndMethod(new Throwable()) : ""; + + this.dbmd = getMetaData(false, false); + + initializeSafeQueryInterceptors(); + + } catch (CJException e1) { + throw SQLExceptionsMapping.translateException(e1, getExceptionInterceptor()); + } + + try { + createNewIO(false); + + unSafeQueryInterceptors(); + + NonRegisteringDriver.trackConnection(this); + } catch (SQLException ex) { + cleanup(ex); + + // don't clobber SQL exceptions + throw ex; + } catch (Exception ex) { + cleanup(ex); + + throw SQLError + .createSQLException( + this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_paranoid).getValue() ? Messages.getString("Connection.0") + : Messages.getString("Connection.1", + new Object[] { this.session.getHostInfo().getHost(), this.session.getHostInfo().getPort() }), + MysqlErrorNumbers.SQL_STATE_COMMUNICATION_LINK_FAILURE, ex, getExceptionInterceptor()); + } + + } + + @Override + public JdbcPropertySet getPropertySet() { + return this.propertySet; + } + + public void unSafeQueryInterceptors() throws SQLException { + this.queryInterceptors = this.queryInterceptors.stream().map(u -> ((NoSubInterceptorWrapper) u).getUnderlyingInterceptor()) + .collect(Collectors.toList()); + + if (this.session != null) { + this.session.setQueryInterceptors(this.queryInterceptors); + } + } + + public void initializeSafeQueryInterceptors() throws SQLException { + this.queryInterceptors = Util + . loadClasses(this.propertySet.getStringReadableProperty(PropertyDefinitions.PNAME_queryInterceptors).getStringValue(), + "MysqlIo.BadQueryInterceptor", getExceptionInterceptor()) + .stream().map(o -> new NoSubInterceptorWrapper(o.init(this, this.props, this.session.getLog()))).collect(Collectors.toList()); + } + + public List getQueryInterceptorsInstances() { + return this.queryInterceptors; + } + + private boolean canHandleAsServerPreparedStatement(String sql) throws SQLException { + if (sql == null || sql.length() == 0) { + return true; + } + + if (!this.useServerPrepStmts.getValue()) { + return false; + } + + if (this.cachePrepStmts.getValue()) { + synchronized (this.serverSideStatementCheckCache) { + Boolean flag = this.serverSideStatementCheckCache.get(sql); + + if (flag != null) { + return flag.booleanValue(); + } + + boolean canHandle = StringUtils.canHandleAsServerPreparedStatementNoCache(sql, getServerVersion()); + + if (sql.length() < this.prepStmtCacheSqlLimit.getValue()) { + this.serverSideStatementCheckCache.put(sql, canHandle ? Boolean.TRUE : Boolean.FALSE); + } + + return canHandle; + } + } + + return StringUtils.canHandleAsServerPreparedStatementNoCache(sql, getServerVersion()); + } + + public void changeUser(String userName, String newPassword) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + if ((userName == null) || userName.equals("")) { + userName = ""; + } + + if (newPassword == null) { + newPassword = ""; + } + + try { + this.session.changeUser(userName, newPassword, this.database); + } catch (CJException ex) { + // After Bug#16241992 fix the server doesn't return to previous credentials if COM_CHANGE_USER attempt failed. + if ("28000".equals(ex.getSQLState())) { + cleanup(ex); + } + throw ex; + } + this.user = userName; + this.password = newPassword; + + this.session.configureClientCharacterSet(true); + + this.session.setSessionVariables(); + + setupServerForTruncationChecks(); + } + } + + public void checkClosed() { + this.session.checkClosed(); + } + + public void throwConnectionClosedException() throws SQLException { + SQLException ex = SQLError.createSQLException(Messages.getString("Connection.2"), MysqlErrorNumbers.SQL_STATE_CONNECTION_NOT_OPEN, + getExceptionInterceptor()); + + if (this.session.getForceClosedReason() != null) { + ex.initCause(this.session.getForceClosedReason()); + } + + throw ex; + } + + /** + * Set transaction isolation level to the value received from server if any. + * Is called by connectionInit(...) + */ + private void checkTransactionIsolationLevel() { + String s = this.session.getServerSession().getServerVariable("transaction_isolation"); + if (s == null) { + s = this.session.getServerSession().getServerVariable("tx_isolation"); + } + + if (s != null) { + Integer intTI = mapTransIsolationNameToValue.get(s); + + if (intTI != null) { + this.isolationLevel = intTI.intValue(); + } + } + } + + /** + * Clobbers the physical network connection and marks + * this connection as closed. + * + * @throws SQLException + */ + public void abortInternal() throws SQLException { + this.session.forceClose(); + } + + /** + * Destroys this connection and any underlying resources + * + * @param fromWhere + * @param whyCleanedUp + */ + @Override + public void cleanup(Throwable whyCleanedUp) { + try { + if (this.session != null) { + if (isClosed()) { + this.session.forceClose(); + } else { + realClose(false, false, false, whyCleanedUp); + } + } + } catch (SQLException | CJException sqlEx) { + // ignore, we're going away. + } + } + + @Deprecated + public void clearHasTriedMaster() { + this.hasTriedMasterFlag = false; + } + + /** + * After this call, getWarnings returns null until a new warning is reported + * for this connection. + * + * @exception SQLException + * if a database access error occurs + */ + public void clearWarnings() throws SQLException { + // firstWarning = null; + } + + /** + * @param sql + * @throws SQLException + */ + public java.sql.PreparedStatement clientPrepareStatement(String sql) throws SQLException { + return clientPrepareStatement(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY); + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + java.sql.PreparedStatement pStmt = clientPrepareStatement(sql); + + ((ClientPreparedStatement) pStmt).setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS); + + return pStmt; + } + + /** + * @param sql + * @param resultSetType + * @param resultSetConcurrency + * @throws SQLException + */ + public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true); + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, boolean processEscapeCodesIfNeeded) + throws SQLException { + + String nativeSql = processEscapeCodesIfNeeded && this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql; + + ClientPreparedStatement pStmt = null; + + if (this.cachePrepStmts.getValue()) { + ParseInfo pStmtInfo = this.cachedPreparedStatementParams.get(nativeSql); + + if (pStmtInfo == null) { + pStmt = ClientPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database); + + this.cachedPreparedStatementParams.put(nativeSql, pStmt.getParseInfo()); + } else { + pStmt = ClientPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, pStmtInfo); + } + } else { + pStmt = ClientPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database); + } + + pStmt.setResultSetType(resultSetType); + pStmt.setResultSetConcurrency(resultSetConcurrency); + + return pStmt; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + + ClientPreparedStatement pStmt = (ClientPreparedStatement) clientPrepareStatement(sql); + + pStmt.setRetrieveGeneratedKeys((autoGenKeyIndexes != null) && (autoGenKeyIndexes.length > 0)); + + return pStmt; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + ClientPreparedStatement pStmt = (ClientPreparedStatement) clientPrepareStatement(sql); + + pStmt.setRetrieveGeneratedKeys((autoGenKeyColNames != null) && (autoGenKeyColNames.length > 0)); + + return pStmt; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true); + } + + // --------------------------JDBC 2.0----------------------------- + + /** + * In some cases, it is desirable to immediately release a Connection's + * database and JDBC resources instead of waiting for them to be + * automatically released (cant think why off the top of my head) Note: + * A Connection is automatically closed when it is garbage collected. + * Certain fatal errors also result in a closed connection. + * + * @exception SQLException + * if a database access error occurs + */ + public void close() throws SQLException { + synchronized (getConnectionMutex()) { + if (this.connectionLifecycleInterceptors != null) { + for (ConnectionLifecycleInterceptor cli : this.connectionLifecycleInterceptors) { + cli.close(); + } + } + + realClose(true, true, false, null); + } + } + + @Override + public void normalClose() { + try { + close(); + } catch (SQLException e) { + ExceptionFactory.createException(e.getMessage(), e); + } + } + + /** + * Closes all currently open statements. + * + * @throws SQLException + */ + private void closeAllOpenStatements() throws SQLException { + SQLException postponedException = null; + + for (JdbcStatement stmt : this.openStatements) { + try { + ((StatementImpl) stmt).realClose(false, true); + } catch (SQLException sqlEx) { + postponedException = sqlEx; // throw it later, cleanup all statements first + } + } + + if (postponedException != null) { + throw postponedException; + } + } + + private void closeStatement(java.sql.Statement stmt) { + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { + // ignore + } + + stmt = null; + } + } + + /** + * The method commit() makes all changes made since the previous + * commit/rollback permanent and releases any database locks currently held + * by the Connection. This method should only be used when auto-commit has + * been disabled. + *

+ * Note: MySQL does not support transactions, so this method is a no-op. + *

+ * + * @exception SQLException + * if a database access error occurs + * @see setAutoCommit + */ + public void commit() throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + try { + if (this.connectionLifecycleInterceptors != null) { + IterateBlock iter = new IterateBlock( + this.connectionLifecycleInterceptors.iterator()) { + + @Override + void forEach(ConnectionLifecycleInterceptor each) throws SQLException { + if (!each.commit()) { + this.stopIterating = true; + } + } + }; + + iter.doForAll(); + + if (!iter.fullIteration()) { + return; + } + } + + if (this.session.getServerSession().isAutoCommit()) { + throw SQLError.createSQLException(Messages.getString("Connection.3"), getExceptionInterceptor()); + } + if (this.useLocalTransactionState.getValue()) { + if (!this.session.getServerSession().inTransactionOnServer()) { + return; // effectively a no-op + } + } + + this.session.execSQL(null, "commit", -1, null, false, this.nullStatementResultSetFactory, this.database, null, false); + } catch (SQLException sqlException) { + if (MysqlErrorNumbers.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlException.getSQLState())) { + throw SQLError.createSQLException(Messages.getString("Connection.4"), MysqlErrorNumbers.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, + getExceptionInterceptor()); + } + + throw sqlException; + } finally { + this.session.setNeedsPing(this.reconnectAtTxEnd.getValue()); + } + } + return; + } + + /** + * Creates an IO channel to the server + * + * @param isForReconnect + * is this request for a re-connect + * @throws CommunicationsException + */ + public void createNewIO(boolean isForReconnect) { + synchronized (getConnectionMutex()) { + // Synchronization Not needed for *new* connections, but defintely for connections going through fail-over, since we might get the new connection up + // and running *enough* to start sending cached or still-open server-side prepared statements over to the backend before we get a chance to + // re-prepare them... + + try { + if (!this.autoReconnect.getValue()) { + connectOneTryOnly(isForReconnect); + + return; + } + + connectWithRetries(isForReconnect); + } catch (SQLException ex) { + throw ExceptionFactory.createException(UnableToConnectException.class, ex.getMessage(), ex); + } + } + } + + private void connectWithRetries(boolean isForReconnect) throws SQLException { + double timeout = this.propertySet.getIntegerReadableProperty(PropertyDefinitions.PNAME_initialTimeout).getValue(); + boolean connectionGood = false; + + Exception connectionException = null; + + for (int attemptCount = 0; (attemptCount < this.propertySet.getIntegerReadableProperty(PropertyDefinitions.PNAME_maxReconnects).getValue()) + && !connectionGood; attemptCount++) { + try { + this.session.forceClose(); + + JdbcConnection c = getProxy(); + this.session.connect(this.origHostInfo, this.user, this.password, this.database, DriverManager.getLoginTimeout() * 1000, c); + pingInternal(false, 0); + + boolean oldAutoCommit; + int oldIsolationLevel; + boolean oldReadOnly; + String oldCatalog; + + synchronized (getConnectionMutex()) { + // save state from old connection + oldAutoCommit = getAutoCommit(); + oldIsolationLevel = this.isolationLevel; + oldReadOnly = isReadOnly(false); + oldCatalog = getCatalog(); + + this.session.setQueryInterceptors(this.queryInterceptors); + } + + // Server properties might be different from previous connection, so initialize again... + initializePropsFromServer(); + + if (isForReconnect) { + // Restore state from old connection + setAutoCommit(oldAutoCommit); + setTransactionIsolation(oldIsolationLevel); + setCatalog(oldCatalog); + setReadOnly(oldReadOnly); + } + + connectionGood = true; + + break; + } catch (UnableToConnectException rejEx) { + close(); + this.session.getProtocol().getSocketConnection().forceClose(); + + } catch (Exception EEE) { + connectionException = EEE; + connectionGood = false; + } + + if (connectionGood) { + break; + } + + if (attemptCount > 0) { + try { + Thread.sleep((long) timeout * 1000); + } catch (InterruptedException IE) { + // ignore + } + } + } // end attempts for a single host + + if (!connectionGood) { + // We've really failed! + SQLException chainedEx = SQLError.createSQLException( + Messages.getString("Connection.UnableToConnectWithRetries", + new Object[] { this.propertySet.getIntegerReadableProperty(PropertyDefinitions.PNAME_maxReconnects).getValue() }), + MysqlErrorNumbers.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, connectionException, getExceptionInterceptor()); + throw chainedEx; + } + + if (this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_paranoid).getValue() && !this.autoReconnect.getValue()) { + this.password = null; + this.user = null; + } + + if (isForReconnect) { + // + // Retrieve any 'lost' prepared statements if re-connecting + // + Iterator statementIter = this.openStatements.iterator(); + + // + // We build a list of these outside the map of open statements, because in the process of re-preparing, we might end up having to close a prepared + // statement, thus removing it from the map, and generating a ConcurrentModificationException + // + Stack serverPreparedStatements = null; + + while (statementIter.hasNext()) { + JdbcStatement statementObj = statementIter.next(); + + if (statementObj instanceof ServerPreparedStatement) { + if (serverPreparedStatements == null) { + serverPreparedStatements = new Stack<>(); + } + + serverPreparedStatements.add(statementObj); + } + } + + if (serverPreparedStatements != null) { + while (!serverPreparedStatements.isEmpty()) { + ((ServerPreparedStatement) serverPreparedStatements.pop()).rePrepare(); + } + } + } + } + + private void connectOneTryOnly(boolean isForReconnect) throws SQLException { + Exception connectionNotEstablishedBecause = null; + + try { + + JdbcConnection c = getProxy(); + this.session.connect(this.origHostInfo, this.user, this.password, this.database, DriverManager.getLoginTimeout() * 1000, c); + + // save state from old connection + boolean oldAutoCommit = getAutoCommit(); + int oldIsolationLevel = this.isolationLevel; + boolean oldReadOnly = isReadOnly(false); + String oldCatalog = getCatalog(); + + this.session.setQueryInterceptors(this.queryInterceptors); + + // Server properties might be different from previous connection, so initialize again... + initializePropsFromServer(); + + if (isForReconnect) { + // Restore state from old connection + setAutoCommit(oldAutoCommit); + setTransactionIsolation(oldIsolationLevel); + setCatalog(oldCatalog); + setReadOnly(oldReadOnly); + } + return; + + } catch (UnableToConnectException rejEx) { + close(); + this.session.getProtocol().getSocketConnection().forceClose(); + throw rejEx; + + } catch (Exception EEE) { + + if ((EEE instanceof PasswordExpiredException + || EEE instanceof SQLException && ((SQLException) EEE).getErrorCode() == MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD) + && !this.disconnectOnExpiredPasswords.getValue()) { + return; + } + + if (this.session != null) { + this.session.forceClose(); + } + + connectionNotEstablishedBecause = EEE; + + if (EEE instanceof SQLException) { + throw (SQLException) EEE; + } + + if (EEE.getCause() != null && EEE.getCause() instanceof SQLException) { + throw (SQLException) EEE.getCause(); + } + + if (EEE instanceof CJException) { + throw (CJException) EEE; + } + + SQLException chainedEx = SQLError.createSQLException(Messages.getString("Connection.UnableToConnect"), + MysqlErrorNumbers.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor()); + chainedEx.initCause(connectionNotEstablishedBecause); + + throw chainedEx; + } + } + + private void createPreparedStatementCaches() throws SQLException { + synchronized (getConnectionMutex()) { + int cacheSize = this.propertySet.getIntegerReadableProperty(PropertyDefinitions.PNAME_prepStmtCacheSize).getValue(); + String parseInfoCacheFactory = this.propertySet.getStringReadableProperty(PropertyDefinitions.PNAME_parseInfoCacheFactory).getValue(); + + try { + Class factoryClass; + + factoryClass = Class.forName(parseInfoCacheFactory); + + @SuppressWarnings("unchecked") + CacheAdapterFactory cacheFactory = ((CacheAdapterFactory) factoryClass.newInstance()); + + this.cachedPreparedStatementParams = cacheFactory.getInstance(this, this.origHostInfo.getDatabaseUrl(), cacheSize, + this.prepStmtCacheSqlLimit.getValue()); + + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("Connection.CantFindCacheFactory", + new Object[] { parseInfoCacheFactory, PropertyDefinitions.PNAME_parseInfoCacheFactory }), getExceptionInterceptor()); + sqlEx.initCause(e); + + throw sqlEx; + } catch (Exception e) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("Connection.CantLoadCacheFactory", + new Object[] { parseInfoCacheFactory, PropertyDefinitions.PNAME_parseInfoCacheFactory }), getExceptionInterceptor()); + sqlEx.initCause(e); + + throw sqlEx; + } + + if (this.useServerPrepStmts.getValue()) { + this.serverSideStatementCheckCache = new LRUCache<>(cacheSize); + + this.serverSideStatementCache = new LRUCache(cacheSize) { + + private static final long serialVersionUID = 7692318650375988114L; + + @Override + protected boolean removeEldestEntry(java.util.Map.Entry eldest) { + if (this.maxElements <= 1) { + return false; + } + + boolean removeIt = super.removeEldestEntry(eldest); + + if (removeIt) { + ServerPreparedStatement ps = eldest.getValue(); + ps.isCached = false; + ps.setClosed(false); + + try { + ps.close(); + } catch (SQLException sqlEx) { + // punt + } + } + + return removeIt; + } + }; + } + } + } + + /** + * SQL statements without parameters are normally executed using Statement + * objects. If the same SQL statement is executed many times, it is more + * efficient to use a PreparedStatement + * + * @return a new Statement object + * @throws SQLException + * passed through from the constructor + */ + public java.sql.Statement createStatement() throws SQLException { + return createStatement(DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY); + } + + /** + * JDBC 2.0 Same as createStatement() above, but allows the default result + * set type and result set concurrency type to be overridden. + * + * @param resultSetType + * a result set type, see ResultSet.TYPE_XXX + * @param resultSetConcurrency + * a concurrency type, see ResultSet.CONCUR_XXX + * @return a new Statement object + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + + StatementImpl stmt = new StatementImpl(getMultiHostSafeProxy(), this.database); + stmt.setResultSetType(resultSetType); + stmt.setResultSetConcurrency(resultSetConcurrency); + + return stmt; + } + + public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + if (this.pedantic.getValue()) { + if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) { + throw SQLError.createSQLException("HOLD_CUSRORS_OVER_COMMIT is only supported holdability level", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + return createStatement(resultSetType, resultSetConcurrency); + } + + public int getActiveStatementCount() { + return this.openStatements.size(); + } + + /** + * Gets the current auto-commit state + * + * @return Current state of auto-commit + * @exception SQLException + * if an error occurs + * @see setAutoCommit + */ + public boolean getAutoCommit() throws SQLException { + synchronized (getConnectionMutex()) { + return this.session.getServerSession().isAutoCommit(); + } + } + + /** + * Return the connections current catalog name, or null if no catalog name + * is set, or we dont support catalogs. + *

+ * Note: MySQL's notion of catalogs are individual databases. + *

+ * + * @return the current catalog name or null + * @exception SQLException + * if a database access error occurs + */ + public String getCatalog() throws SQLException { + synchronized (getConnectionMutex()) { + return this.database; + } + } + + /** + * @return Returns the characterSetMetadata. + */ + public String getCharacterSetMetadata() { + synchronized (getConnectionMutex()) { + return this.session.getServerSession().getCharacterSetMetadata(); + } + } + + public int getHoldability() throws SQLException { + return java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT; + } + + public long getId() { + return this.session.getThreadId(); + } + + /** + * NOT JDBC-Compliant, but clients can use this method to determine how long + * this connection has been idle. This time (reported in milliseconds) is + * updated once a query has completed. + * + * @return number of ms that this connection has been idle, 0 if the driver + * is busy retrieving results. + */ + public long getIdleFor() { + synchronized (getConnectionMutex()) { + return this.session.getIdleFor(); + } + } + + /** + * A connection's database is able to provide information describing its + * tables, its supported SQL grammar, its stored procedures, the + * capabilities of this connection, etc. This information is made available + * through a DatabaseMetaData object. + * + * @return a DatabaseMetaData object for this connection + * @exception SQLException + * if a database access error occurs + */ + public java.sql.DatabaseMetaData getMetaData() throws SQLException { + return getMetaData(true, true); + } + + private java.sql.DatabaseMetaData getMetaData(boolean checkClosed, boolean checkForInfoSchema) throws SQLException { + if (checkClosed) { + checkClosed(); + } + + com.mysql.cj.jdbc.DatabaseMetaData dbmeta = com.mysql.cj.jdbc.DatabaseMetaData.getInstance(getMultiHostSafeProxy(), this.database, checkForInfoSchema, + this.nullStatementResultSetFactory); + + if (getSession() != null && getSession().getProtocol() != null) { + dbmeta.setMetadataEncoding(getSession().getServerSession().getCharacterSetMetadata()); + dbmeta.setMetadataCollationIndex(getSession().getServerSession().getMetadataCollationIndex()); + } + + return dbmeta; + } + + public java.sql.Statement getMetadataSafeStatement() throws SQLException { + return getMetadataSafeStatement(0); + } + + public java.sql.Statement getMetadataSafeStatement(int maxRows) throws SQLException { + java.sql.Statement stmt = createStatement(); + + stmt.setMaxRows(maxRows == -1 ? 0 : maxRows); + + stmt.setEscapeProcessing(false); + + if (stmt.getFetchSize() != 0) { + stmt.setFetchSize(0); + } + + return stmt; + } + + public ServerVersion getServerVersion() { + return this.session.getServerSession().getServerVersion(); + } + + /** + * Get this Connection's current transaction isolation mode. + * + * @return the current TRANSACTION_ mode value + * @exception SQLException + * if a database access error occurs + */ + public int getTransactionIsolation() throws SQLException { + + synchronized (getConnectionMutex()) { + if (!this.useLocalSessionState.getValue()) { + String s = this.session.queryServerVariable(versionMeetsMinimum(8, 0, 3) || (versionMeetsMinimum(5, 7, 20) && !versionMeetsMinimum(8, 0, 0)) + ? "@@session.transaction_isolation" : "@@session.tx_isolation"); + + if (s != null) { + Integer intTI = mapTransIsolationNameToValue.get(s); + if (intTI != null) { + this.isolationLevel = intTI.intValue(); + return this.isolationLevel; + } + throw SQLError.createSQLException(Messages.getString("Connection.12", new Object[] { s }), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + throw SQLError.createSQLException(Messages.getString("Connection.13"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + return this.isolationLevel; + } + } + + /** + * JDBC 2.0 Get the type-map object associated with this connection. By + * default, the map returned is empty. + * + * @return the type map + * @throws SQLException + * if a database error occurs + */ + public java.util.Map> getTypeMap() throws SQLException { + synchronized (getConnectionMutex()) { + if (this.typeMap == null) { + this.typeMap = new HashMap<>(); + } + + return this.typeMap; + } + } + + public String getURL() { + return this.origHostInfo.getDatabaseUrl(); + } + + public String getUser() { + return this.user; + } + + /** + * The first warning reported by calls on this Connection is returned. + * Note: Subsequent warnings will be changed to this + * java.sql.SQLWarning + * + * @return the first java.sql.SQLWarning or null + * @exception SQLException + * if a database access error occurs + */ + public SQLWarning getWarnings() throws SQLException { + return null; + } + + public boolean hasSameProperties(JdbcConnection c) { + return this.props.equals(c.getProperties()); + } + + public Properties getProperties() { + return this.props; + } + + @Deprecated + public boolean hasTriedMaster() { + return this.hasTriedMasterFlag; + } + + /** + * Sets varying properties that depend on server information. Called once we + * have connected to the server. + * + * @param info + * @throws SQLException + */ + private void initializePropsFromServer() throws SQLException { + String connectionInterceptorClasses = this.propertySet.getStringReadableProperty(PropertyDefinitions.PNAME_connectionLifecycleInterceptors) + .getStringValue(); + + this.connectionLifecycleInterceptors = null; + + if (connectionInterceptorClasses != null) { + try { + this.connectionLifecycleInterceptors = Util + . loadClasses( + this.propertySet.getStringReadableProperty(PropertyDefinitions.PNAME_connectionLifecycleInterceptors).getStringValue(), + "Connection.badLifecycleInterceptor", getExceptionInterceptor()) + .stream().map(o -> o.init(this, this.props, this.session.getLog())).collect(Collectors.toList()); + } catch (CJException e) { + throw SQLExceptionsMapping.translateException(e, getExceptionInterceptor()); + } + } + + this.session.setSessionVariables(); + + this.session.loadServerVariables(this.getConnectionMutex(), this.dbmd.getDriverVersion()); + + this.autoIncrementIncrement = this.session.getServerSession().getServerVariable("auto_increment_increment", 1); + + this.session.buildCollationMapping(); + + try { + LicenseConfiguration.checkLicenseType(this.session.getServerSession().getServerVariables()); + } catch (CJException e) { + throw SQLError.createSQLException(e.getMessage(), MysqlErrorNumbers.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, getExceptionInterceptor()); + } + + this.session.getProtocol().initServerSession(); + + checkTransactionIsolationLevel(); + + this.session.checkForCharsetMismatch(); + + this.session.configureClientCharacterSet(false); + + handleAutoCommitDefaults(); + + // + // We need to figure out what character set metadata and error messages will be returned in, and then map them to Java encoding names + // + // We've already set it, and it might be different than what was originally on the server, which is why we use the "special" key to retrieve it + this.session.getServerSession().configureCharacterSets(); + + ((com.mysql.cj.jdbc.DatabaseMetaData) this.dbmd).setMetadataEncoding(getSession().getServerSession().getCharacterSetMetadata()); + ((com.mysql.cj.jdbc.DatabaseMetaData) this.dbmd).setMetadataCollationIndex(getSession().getServerSession().getMetadataCollationIndex()); + + // + // Server can do this more efficiently for us + // + + setupServerForTruncationChecks(); + } + + /** + * Resets a default auto-commit value of 0 to 1, as required by JDBC specification. + * Takes into account that the default auto-commit value of 0 may have been changed on the server via init_connect. + */ + private void handleAutoCommitDefaults() throws SQLException { + boolean resetAutoCommitDefault = false; + + // Server Bug#66884 (SERVER_STATUS is always initiated with SERVER_STATUS_AUTOCOMMIT=1) invalidates "elideSetAutoCommits" feature. + // TODO Turn this feature back on as soon as the server bug is fixed. Consider making it version specific. + // if (!getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_elideSetAutoCommits).getValue()) { + String initConnectValue = this.session.getServerSession().getServerVariable("init_connect"); + if (initConnectValue != null && initConnectValue.length() > 0) { + // auto-commit might have changed + + String s = this.session.queryServerVariable("@@session.autocommit"); + if (s != null) { + this.session.getServerSession().setAutoCommit(Boolean.parseBoolean(s)); + if (!this.session.getServerSession().isAutoCommit()) { + resetAutoCommitDefault = true; + } + } + + } else { + // reset it anyway, the server may have been initialized with --autocommit=0 + resetAutoCommitDefault = true; + } + //} else if (getSession().isSetNeededForAutoCommitMode(true)) { + // // we're not in standard autocommit=true mode + // this.session.setAutoCommit(false); + // resetAutoCommitDefault = true; + //} + + if (resetAutoCommitDefault) { + try { + setAutoCommit(true); // required by JDBC spec + } catch (SQLException ex) { + if (ex.getErrorCode() != MysqlErrorNumbers.ER_MUST_CHANGE_PASSWORD || this.disconnectOnExpiredPasswords.getValue()) { + throw ex; + } + } + } + } + + public boolean isClosed() { + return this.session.isClosed(); + } + + public boolean isInGlobalTx() { + return this.isInGlobalTx; + } + + /** + * Is this connection connected to the first host in the list if + * there is a list of servers in the URL? + * + * @return true if this connection is connected to the first in + * the list. + */ + public boolean isMasterConnection() { + return false; // handled higher up + } + + /** + * Tests to see if the connection is in Read Only Mode. + * + * @return true if the connection is read only + * @exception SQLException + * if a database access error occurs + */ + public boolean isReadOnly() throws SQLException { + return isReadOnly(true); + } + + /** + * Tests to see if the connection is in Read Only Mode. + * + * @param useSessionStatus + * in some cases, for example when restoring connection with autoReconnect=true, + * we can rely only on saved readOnly state, so use useSessionStatus=false in that case + * + * @return true if the connection is read only + * @exception SQLException + * if a database access error occurs + */ + public boolean isReadOnly(boolean useSessionStatus) throws SQLException { + if (useSessionStatus && !this.session.isClosed() && versionMeetsMinimum(5, 6, 5) && !this.useLocalSessionState.getValue() + && this.readOnlyPropagatesToServer.getValue()) { + try { + String s = this.session.queryServerVariable(versionMeetsMinimum(8, 0, 3) || (versionMeetsMinimum(5, 7, 20) && !versionMeetsMinimum(8, 0, 0)) + ? "@@session.transaction_read_only" : "@@session.tx_read_only"); + if (s != null) { + return Integer.parseInt(s) != 0; // mysql has a habit of tri+ state booleans + } + } catch (PasswordExpiredException ex) { + if (this.disconnectOnExpiredPasswords.getValue()) { + throw SQLError.createSQLException(Messages.getString("Connection.16"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, ex, + getExceptionInterceptor()); + } + } + } + + return this.readOnly; + } + + public boolean isSameResource(JdbcConnection otherConnection) { + synchronized (getConnectionMutex()) { + if (otherConnection == null) { + return false; + } + + boolean directCompare = true; + + String otherHost = ((ConnectionImpl) otherConnection).origHostToConnectTo; + String otherOrigDatabase = ((ConnectionImpl) otherConnection).origHostInfo.getDatabase(); + String otherCurrentCatalog = ((ConnectionImpl) otherConnection).database; + + if (!nullSafeCompare(otherHost, this.origHostToConnectTo)) { + directCompare = false; + } else if (otherHost != null && otherHost.indexOf(',') == -1 && otherHost.indexOf(':') == -1) { + // need to check port numbers + directCompare = (((ConnectionImpl) otherConnection).origPortToConnectTo == this.origPortToConnectTo); + } + + if (directCompare) { + if (!nullSafeCompare(otherOrigDatabase, this.origHostInfo.getDatabase()) || !nullSafeCompare(otherCurrentCatalog, this.database)) { + directCompare = false; + } + } + + if (directCompare) { + return true; + } + + // Has the user explicitly set a resourceId? + String otherResourceId = ((ConnectionImpl) otherConnection).getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_resourceId) + .getValue(); + String myResourceId = this.propertySet.getStringReadableProperty(PropertyDefinitions.PNAME_resourceId).getValue(); + + if (otherResourceId != null || myResourceId != null) { + directCompare = nullSafeCompare(otherResourceId, myResourceId); + + if (directCompare) { + return true; + } + } + + return false; + } + } + + private int autoIncrementIncrement = 0; + + public int getAutoIncrementIncrement() { + return this.autoIncrementIncrement; + } + + /** + * Is the server configured to use lower-case table names only? + * + * @return true if lower_case_table_names is 'on' + */ + public boolean lowerCaseTableNames() { + return this.session.getServerSession().isLowerCaseTableNames(); + } + + /** + * A driver may convert the JDBC sql grammar into its system's native SQL + * grammar prior to sending it; nativeSQL returns the native form of the + * statement that the driver would have sent. + * + * @param sql + * a SQL statement that may contain one or more '?' parameter + * placeholders + * @return the native form of this statement + * @exception SQLException + * if a database access error occurs + */ + public String nativeSQL(String sql) throws SQLException { + if (sql == null) { + return null; + } + + Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, getMultiHostSafeProxy().getSession().getServerSession().getDefaultTimeZone(), + getMultiHostSafeProxy().getSession().getServerSession().getCapabilities().serverSupportsFracSecs(), getExceptionInterceptor()); + + if (escapedSqlResult instanceof String) { + return (String) escapedSqlResult; + } + + return ((EscapeProcessorResult) escapedSqlResult).escapedSql; + } + + private CallableStatement parseCallableStatement(String sql) throws SQLException { + Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, getMultiHostSafeProxy().getSession().getServerSession().getDefaultTimeZone(), + getMultiHostSafeProxy().getSession().getServerSession().getCapabilities().serverSupportsFracSecs(), getExceptionInterceptor()); + + boolean isFunctionCall = false; + String parsedSql = null; + + if (escapedSqlResult instanceof EscapeProcessorResult) { + parsedSql = ((EscapeProcessorResult) escapedSqlResult).escapedSql; + isFunctionCall = ((EscapeProcessorResult) escapedSqlResult).callingStoredFunction; + } else { + parsedSql = (String) escapedSqlResult; + isFunctionCall = false; + } + + return CallableStatement.getInstance(getMultiHostSafeProxy(), parsedSql, this.database, isFunctionCall); + } + + /** + * Detect if the connection is still good + * + * @throws SQLException + * if the ping fails + */ + public void ping() throws SQLException { + pingInternal(true, 0); + } + + @Override + public void pingInternal(boolean checkForClosedConnection, int timeoutMillis) throws SQLException { + this.session.ping(checkForClosedConnection, timeoutMillis); + } + + /** + * @param sql + * @throws SQLException + */ + public java.sql.CallableStatement prepareCall(String sql) throws SQLException { + + return prepareCall(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY); + } + + /** + * JDBC 2.0 Same as prepareCall() above, but allows the default result set + * type and result set concurrency type to be overridden. + * + * @param sql + * the SQL representing the callable statement + * @param resultSetType + * a result set type, see ResultSet.TYPE_XXX + * @param resultSetConcurrency + * a concurrency type, see ResultSet.CONCUR_XXX + * @return a new CallableStatement object containing the pre-compiled SQL + * statement + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + CallableStatement cStmt = null; + + if (!this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_cacheCallableStmts).getValue()) { + + cStmt = parseCallableStatement(sql); + } else { + synchronized (this.parsedCallableStatementCache) { + CompoundCacheKey key = new CompoundCacheKey(getCatalog(), sql); + + CallableStatement.CallableStatementParamInfo cachedParamInfo = this.parsedCallableStatementCache.get(key); + + if (cachedParamInfo != null) { + cStmt = CallableStatement.getInstance(getMultiHostSafeProxy(), cachedParamInfo); + } else { + cStmt = parseCallableStatement(sql); + + synchronized (cStmt) { + cachedParamInfo = cStmt.paramInfo; + } + + this.parsedCallableStatementCache.put(key, cachedParamInfo); + } + } + } + + cStmt.setResultSetType(resultSetType); + cStmt.setResultSetConcurrency(resultSetConcurrency); + + return cStmt; + } + + public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + if (this.pedantic.getValue()) { + if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) { + throw SQLError.createSQLException(Messages.getString("Connection.17"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + CallableStatement cStmt = (com.mysql.cj.jdbc.CallableStatement) prepareCall(sql, resultSetType, resultSetConcurrency); + + return cStmt; + } + + /** + * A SQL statement with or without IN parameters can be pre-compiled and + * stored in a PreparedStatement object. This object can then be used to + * efficiently execute this statement multiple times. + *

+ * Note: This method is optimized for handling parametric SQL statements that benefit from precompilation if the driver supports precompilation. In + * this case, the statement is not sent to the database until the PreparedStatement is executed. This has no direct effect on users; however it does affect + * which method throws certain java.sql.SQLExceptions + *

+ *

+ * MySQL does not support precompilation of statements, so they are handled by the driver. + *

+ * + * @param sql + * a SQL statement that may contain one or more '?' IN parameter + * placeholders + * @return a new PreparedStatement object containing the pre-compiled + * statement. + * @exception SQLException + * if a database access error occurs. + */ + public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException { + return prepareStatement(sql, DEFAULT_RESULT_SET_TYPE, DEFAULT_RESULT_SET_CONCURRENCY); + } + + public java.sql.PreparedStatement prepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + java.sql.PreparedStatement pStmt = prepareStatement(sql); + + ((ClientPreparedStatement) pStmt).setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS); + + return pStmt; + } + + /** + * JDBC 2.0 Same as prepareStatement() above, but allows the default result + * set type and result set concurrency type to be overridden. + * + * @param sql + * the SQL query containing place holders + * @param resultSetType + * a result set type, see ResultSet.TYPE_XXX + * @param resultSetConcurrency + * a concurrency type, see ResultSet.CONCUR_XXX + * @return a new PreparedStatement object containing the pre-compiled SQL + * statement + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + // + // FIXME: Create warnings if can't create results of the given type or concurrency + // + ClientPreparedStatement pStmt = null; + + boolean canServerPrepare = true; + + String nativeSql = this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql; + + if (this.useServerPrepStmts.getValue() && this.emulateUnsupportedPstmts.getValue()) { + canServerPrepare = canHandleAsServerPreparedStatement(nativeSql); + } + + if (this.useServerPrepStmts.getValue() && canServerPrepare) { + if (this.cachePrepStmts.getValue()) { + synchronized (this.serverSideStatementCache) { + pStmt = this.serverSideStatementCache.remove(new CompoundCacheKey(this.database, sql)); + + if (pStmt != null) { + ((com.mysql.cj.jdbc.ServerPreparedStatement) pStmt).setClosed(false); + pStmt.clearParameters(); + } + + if (pStmt == null) { + try { + pStmt = ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, resultSetType, + resultSetConcurrency); + if (sql.length() < this.prepStmtCacheSqlLimit.getValue()) { + ((com.mysql.cj.jdbc.ServerPreparedStatement) pStmt).isCached = true; + } + + pStmt.setResultSetType(resultSetType); + pStmt.setResultSetConcurrency(resultSetConcurrency); + } catch (SQLException sqlEx) { + // Punt, if necessary + if (this.emulateUnsupportedPstmts.getValue()) { + pStmt = (ClientPreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false); + + if (sql.length() < this.prepStmtCacheSqlLimit.getValue()) { + this.serverSideStatementCheckCache.put(sql, Boolean.FALSE); + } + } else { + throw sqlEx; + } + } + } + } + } else { + try { + pStmt = ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.database, resultSetType, resultSetConcurrency); + + pStmt.setResultSetType(resultSetType); + pStmt.setResultSetConcurrency(resultSetConcurrency); + } catch (SQLException sqlEx) { + // Punt, if necessary + if (this.emulateUnsupportedPstmts.getValue()) { + pStmt = (ClientPreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false); + } else { + throw sqlEx; + } + } + } + } else { + pStmt = (ClientPreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false); + } + + return pStmt; + } + } + + public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + if (this.pedantic.getValue()) { + if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) { + throw SQLError.createSQLException(Messages.getString("Connection.17"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + return prepareStatement(sql, resultSetType, resultSetConcurrency); + } + + public java.sql.PreparedStatement prepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + java.sql.PreparedStatement pStmt = prepareStatement(sql); + + ((ClientPreparedStatement) pStmt).setRetrieveGeneratedKeys((autoGenKeyIndexes != null) && (autoGenKeyIndexes.length > 0)); + + return pStmt; + } + + public java.sql.PreparedStatement prepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + java.sql.PreparedStatement pStmt = prepareStatement(sql); + + ((ClientPreparedStatement) pStmt).setRetrieveGeneratedKeys((autoGenKeyColNames != null) && (autoGenKeyColNames.length > 0)); + + return pStmt; + } + + /** + * Closes connection and frees resources. + * + * @param calledExplicitly + * is this being called from close() + * @param issueRollback + * should a rollback() be issued? + * @throws SQLException + * if an error occurs + */ + public void realClose(boolean calledExplicitly, boolean issueRollback, boolean skipLocalTeardown, Throwable reason) throws SQLException { + SQLException sqlEx = null; + + if (this.isClosed()) { + return; + } + + this.session.setForceClosedReason(reason); + + try { + if (!skipLocalTeardown) { + if (!getAutoCommit() && issueRollback) { + try { + rollback(); + } catch (SQLException ex) { + sqlEx = ex; + } + } + + this.session.reportMetrics(); + + if (this.useUsageAdvisor.getValue()) { + if (!calledExplicitly) { + this.session.getProfilerEventHandler() + .consumeEvent(new ProfilerEventImpl(ProfilerEvent.TYPE_WARN, "", this.getCatalog(), this.session.getThreadId(), -1, -1, + System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, Messages.getString("Connection.18"))); + } + + long connectionLifeTime = System.currentTimeMillis() - this.session.getConnectionCreationTimeMillis(); + + if (connectionLifeTime < 500) { + this.session.getProfilerEventHandler() + .consumeEvent(new ProfilerEventImpl(ProfilerEvent.TYPE_WARN, "", this.getCatalog(), this.session.getThreadId(), -1, -1, + System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, Messages.getString("Connection.19"))); + } + } + + try { + closeAllOpenStatements(); + } catch (SQLException ex) { + sqlEx = ex; + } + + this.session.quit(); + } else { + this.session.forceClose(); + } + + if (this.queryInterceptors != null) { + for (int i = 0; i < this.queryInterceptors.size(); i++) { + this.queryInterceptors.get(i).destroy(); + } + } + + if (this.exceptionInterceptor != null) { + this.exceptionInterceptor.destroy(); + } + } finally { + ProfilerEventHandlerFactory.removeInstance(this.session); + + this.openStatements.clear(); + this.queryInterceptors = null; + this.exceptionInterceptor = null; + this.nullStatementResultSetFactory = null; + } + + if (sqlEx != null) { + throw sqlEx; + } + + } + + public void recachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException { + synchronized (getConnectionMutex()) { + if (this.cachePrepStmts.getValue() && pstmt.isPoolable()) { + synchronized (this.serverSideStatementCache) { + Object oldServerPrepStmt = this.serverSideStatementCache.put( + new CompoundCacheKey(pstmt.getCurrentCatalog(), ((PreparedQuery) pstmt.getQuery()).getOriginalSql()), + (ServerPreparedStatement) pstmt); + if (oldServerPrepStmt != null && oldServerPrepStmt != pstmt) { + ((ServerPreparedStatement) oldServerPrepStmt).isCached = false; + ((ServerPreparedStatement) oldServerPrepStmt).setClosed(false); + ((ServerPreparedStatement) oldServerPrepStmt).realClose(true, true); + } + } + } + } + } + + public void decachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException { + synchronized (getConnectionMutex()) { + if (this.cachePrepStmts.getValue() && pstmt.isPoolable()) { + synchronized (this.serverSideStatementCache) { + this.serverSideStatementCache + .remove(new CompoundCacheKey(pstmt.getCurrentCatalog(), ((PreparedQuery) pstmt.getQuery()).getOriginalSql())); + } + } + } + } + + /** + * Register a Statement instance as open. + * + * @param stmt + * the Statement instance to remove + */ + public void registerStatement(JdbcStatement stmt) { + this.openStatements.addIfAbsent(stmt); + } + + public void releaseSavepoint(Savepoint arg0) throws SQLException { + // this is a no-op + } + + /** + * Resets the server-side state of this connection. Doesn't work if isParanoid() is set (it will become a + * no-op in this case). Usually only used from connection pooling code. + * + * @throws SQLException + * if the operation fails while resetting server state. + */ + public void resetServerState() throws SQLException { + if (!this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_paranoid).getValue() && (this.session != null)) { + changeUser(this.user, this.password); + } + } + + /** + * The method rollback() drops all changes made since the previous + * commit/rollback and releases any database locks currently held by the + * Connection. + * + * @exception SQLException + * if a database access error occurs + * @see commit + */ + public void rollback() throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + try { + if (this.connectionLifecycleInterceptors != null) { + IterateBlock iter = new IterateBlock( + this.connectionLifecycleInterceptors.iterator()) { + + @Override + void forEach(ConnectionLifecycleInterceptor each) throws SQLException { + if (!each.rollback()) { + this.stopIterating = true; + } + } + }; + + iter.doForAll(); + + if (!iter.fullIteration()) { + return; + } + } + if (this.session.getServerSession().isAutoCommit()) { + throw SQLError.createSQLException(Messages.getString("Connection.20"), MysqlErrorNumbers.SQL_STATE_CONNECTION_NOT_OPEN, + getExceptionInterceptor()); + } + try { + rollbackNoChecks(); + } catch (SQLException sqlEx) { + // We ignore non-transactional tables if told to do so + if (this.ignoreNonTxTables.getInitialValue() && (sqlEx.getErrorCode() == MysqlErrorNumbers.ER_WARNING_NOT_COMPLETE_ROLLBACK)) { + return; + } + throw sqlEx; + + } + } catch (SQLException sqlException) { + if (MysqlErrorNumbers.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlException.getSQLState())) { + throw SQLError.createSQLException(Messages.getString("Connection.21"), MysqlErrorNumbers.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, + getExceptionInterceptor()); + } + + throw sqlException; + } finally { + this.session.setNeedsPing(this.reconnectAtTxEnd.getValue()); + } + } + } + + public void rollback(final Savepoint savepoint) throws SQLException { + + synchronized (getConnectionMutex()) { + checkClosed(); + + try { + if (this.connectionLifecycleInterceptors != null) { + IterateBlock iter = new IterateBlock( + this.connectionLifecycleInterceptors.iterator()) { + + @Override + void forEach(ConnectionLifecycleInterceptor each) throws SQLException { + if (!each.rollback(savepoint)) { + this.stopIterating = true; + } + } + }; + + iter.doForAll(); + + if (!iter.fullIteration()) { + return; + } + } + + StringBuilder rollbackQuery = new StringBuilder("ROLLBACK TO SAVEPOINT "); + rollbackQuery.append('`'); + rollbackQuery.append(savepoint.getSavepointName()); + rollbackQuery.append('`'); + + java.sql.Statement stmt = null; + + try { + stmt = getMetadataSafeStatement(); + + stmt.executeUpdate(rollbackQuery.toString()); + } catch (SQLException sqlEx) { + int errno = sqlEx.getErrorCode(); + + if (errno == 1181) { + String msg = sqlEx.getMessage(); + + if (msg != null) { + int indexOfError153 = msg.indexOf("153"); + + if (indexOfError153 != -1) { + throw SQLError.createSQLException(Messages.getString("Connection.22", new Object[] { savepoint.getSavepointName() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, errno, getExceptionInterceptor()); + } + } + } + + // We ignore non-transactional tables if told to do so + if (this.ignoreNonTxTables.getValue() && (sqlEx.getErrorCode() != MysqlErrorNumbers.ER_WARNING_NOT_COMPLETE_ROLLBACK)) { + throw sqlEx; + } + + if (MysqlErrorNumbers.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlEx.getSQLState())) { + throw SQLError.createSQLException(Messages.getString("Connection.23"), MysqlErrorNumbers.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN, + getExceptionInterceptor()); + } + + throw sqlEx; + } finally { + closeStatement(stmt); + } + } finally { + this.session.setNeedsPing(this.reconnectAtTxEnd.getValue()); + } + } + } + + private void rollbackNoChecks() throws SQLException { + synchronized (getConnectionMutex()) { + if (this.useLocalTransactionState.getValue()) { + if (!this.session.getServerSession().inTransactionOnServer()) { + return; // effectively a no-op + } + } + + this.session.execSQL(null, "rollback", -1, null, false, this.nullStatementResultSetFactory, this.database, null, false); + + } + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql) throws SQLException { + String nativeSql = this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql; + + return ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.getCatalog(), DEFAULT_RESULT_SET_TYPE, + DEFAULT_RESULT_SET_CONCURRENCY); + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + String nativeSql = this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql; + + ClientPreparedStatement pStmt = ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.getCatalog(), DEFAULT_RESULT_SET_TYPE, + DEFAULT_RESULT_SET_CONCURRENCY); + + pStmt.setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS); + + return pStmt; + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + String nativeSql = this.processEscapeCodesForPrepStmts.getValue() ? nativeSQL(sql) : sql; + + return ServerPreparedStatement.getInstance(getMultiHostSafeProxy(), nativeSql, this.getCatalog(), resultSetType, resultSetConcurrency); + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + if (this.pedantic.getValue()) { + if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) { + throw SQLError.createSQLException(Messages.getString("Connection.17"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + return serverPrepareStatement(sql, resultSetType, resultSetConcurrency); + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + + ClientPreparedStatement pStmt = (ClientPreparedStatement) serverPrepareStatement(sql); + + pStmt.setRetrieveGeneratedKeys((autoGenKeyIndexes != null) && (autoGenKeyIndexes.length > 0)); + + return pStmt; + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + ClientPreparedStatement pStmt = (ClientPreparedStatement) serverPrepareStatement(sql); + + pStmt.setRetrieveGeneratedKeys((autoGenKeyColNames != null) && (autoGenKeyColNames.length > 0)); + + return pStmt; + } + + /** + * If a connection is in auto-commit mode, than all its SQL statements will + * be executed and committed as individual transactions. Otherwise, its SQL + * statements are grouped into transactions that are terminated by either + * commit() or rollback(). By default, new connections are in auto-commit + * mode. The commit occurs when the statement completes or the next execute + * occurs, whichever comes first. In the case of statements returning a + * ResultSet, the statement completes when the last row of the ResultSet has + * been retrieved or the ResultSet has been closed. In advanced cases, a + * single statement may return multiple results as well as output parameter + * values. Here the commit occurs when all results and output param values + * have been retrieved. + * + * @param autoCommitFlag + * true enables auto-commit; false disables it + * @exception SQLException + * if a database access error occurs + */ + public void setAutoCommit(final boolean autoCommitFlag) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + if (this.connectionLifecycleInterceptors != null) { + IterateBlock iter = new IterateBlock( + this.connectionLifecycleInterceptors.iterator()) { + + @Override + void forEach(ConnectionLifecycleInterceptor each) throws SQLException { + if (!each.setAutoCommit(autoCommitFlag)) { + this.stopIterating = true; + } + } + }; + + iter.doForAll(); + + if (!iter.fullIteration()) { + return; + } + } + + if (this.autoReconnectForPools.getValue()) { + this.autoReconnect.setValue(true); + } + + try { + boolean needsSetOnServer = true; + + if (this.useLocalSessionState.getValue() && this.session.getServerSession().isAutoCommit() == autoCommitFlag) { + needsSetOnServer = false; + } else if (!this.autoReconnect.getValue()) { + needsSetOnServer = getSession().isSetNeededForAutoCommitMode(autoCommitFlag); + } + + // this internal value must be set first as failover depends on it being set to true to fail over (which is done by most app servers and + // connection pools at the end of a transaction), and the driver issues an implicit set based on this value when it (re)-connects to a + // server so the value holds across connections + this.session.getServerSession().setAutoCommit(autoCommitFlag); + + if (needsSetOnServer) { + this.session.execSQL(null, autoCommitFlag ? "SET autocommit=1" : "SET autocommit=0", -1, null, false, this.nullStatementResultSetFactory, + this.database, null, false); + } + } finally { + if (this.autoReconnectForPools.getValue()) { + this.autoReconnect.setValue(false); + } + } + + return; + } + } + + /** + * A sub-space of this Connection's database may be selected by setting a + * catalog name. If the driver does not support catalogs, it will silently + * ignore this request + *

+ * Note: MySQL's notion of catalogs are individual databases. + *

+ * + * @param catalog + * the database for this connection to use + * @throws SQLException + * if a database access error occurs + */ + public void setCatalog(final String catalog) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + if (catalog == null) { + throw SQLError.createSQLException("Catalog can not be null", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (this.connectionLifecycleInterceptors != null) { + IterateBlock iter = new IterateBlock( + this.connectionLifecycleInterceptors.iterator()) { + + @Override + void forEach(ConnectionLifecycleInterceptor each) throws SQLException { + if (!each.setCatalog(catalog)) { + this.stopIterating = true; + } + } + }; + + iter.doForAll(); + + if (!iter.fullIteration()) { + return; + } + } + + if (this.useLocalSessionState.getValue()) { + if (this.session.getServerSession().isLowerCaseTableNames()) { + if (this.database.equalsIgnoreCase(catalog)) { + return; + } + } else { + if (this.database.equals(catalog)) { + return; + } + } + } + + String quotedId = this.session.getIdentifierQuoteString(); + + if ((quotedId == null) || quotedId.equals(" ")) { + quotedId = ""; + } + + StringBuilder query = new StringBuilder("USE "); + query.append(StringUtils.quoteIdentifier(catalog, quotedId, this.pedantic.getValue())); + + this.session.execSQL(null, query.toString(), -1, null, false, this.nullStatementResultSetFactory, this.database, null, false); + + this.database = catalog; + } + } + + /** + * @param failedOver + * The failedOver to set. + */ + public void setFailedOver(boolean flag) { + // handled higher up + } + + public void setHoldability(int arg0) throws SQLException { + // do nothing + } + + public void setInGlobalTx(boolean flag) { + this.isInGlobalTx = flag; + } + + /** + * You can put a connection in read-only mode as a hint to enable database + * optimizations Note: setReadOnly cannot be called while in the + * middle of a transaction + * + * @param readOnlyFlag + * - + * true enables read-only mode; false disables it + * @exception SQLException + * if a database access error occurs + */ + public void setReadOnly(boolean readOnlyFlag) throws SQLException { + + setReadOnlyInternal(readOnlyFlag); + } + + public void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException { + synchronized (getConnectionMutex()) { + // note this this is safe even inside a transaction + if (this.readOnlyPropagatesToServer.getValue() && versionMeetsMinimum(5, 6, 5)) { + if (!this.useLocalSessionState.getValue() || (readOnlyFlag != this.readOnly)) { + this.session.execSQL(null, "set session transaction " + (readOnlyFlag ? "read only" : "read write"), -1, null, false, + this.nullStatementResultSetFactory, this.database, null, false); + } + } + + this.readOnly = readOnlyFlag; + } + } + + public java.sql.Savepoint setSavepoint() throws SQLException { + MysqlSavepoint savepoint = new MysqlSavepoint(getExceptionInterceptor()); + + setSavepoint(savepoint); + + return savepoint; + } + + private void setSavepoint(MysqlSavepoint savepoint) throws SQLException { + + synchronized (getConnectionMutex()) { + checkClosed(); + + StringBuilder savePointQuery = new StringBuilder("SAVEPOINT "); + savePointQuery.append('`'); + savePointQuery.append(savepoint.getSavepointName()); + savePointQuery.append('`'); + + java.sql.Statement stmt = null; + + try { + stmt = getMetadataSafeStatement(); + + stmt.executeUpdate(savePointQuery.toString()); + } finally { + closeStatement(stmt); + } + } + } + + public java.sql.Savepoint setSavepoint(String name) throws SQLException { + synchronized (getConnectionMutex()) { + MysqlSavepoint savepoint = new MysqlSavepoint(name, getExceptionInterceptor()); + + setSavepoint(savepoint); + + return savepoint; + } + } + + /** + * @param level + * @throws SQLException + */ + public void setTransactionIsolation(int level) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + String sql = null; + + boolean shouldSendSet = false; + + if (this.propertySet.getBooleanReadableProperty(PropertyDefinitions.PNAME_alwaysSendSetIsolation).getValue()) { + shouldSendSet = true; + } else { + if (level != this.isolationLevel) { + shouldSendSet = true; + } + } + + if (this.useLocalSessionState.getValue()) { + shouldSendSet = this.isolationLevel != level; + } + + if (shouldSendSet) { + switch (level) { + case java.sql.Connection.TRANSACTION_NONE: + throw SQLError.createSQLException(Messages.getString("Connection.24"), getExceptionInterceptor()); + + case java.sql.Connection.TRANSACTION_READ_COMMITTED: + sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED"; + + break; + + case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED: + sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"; + + break; + + case java.sql.Connection.TRANSACTION_REPEATABLE_READ: + sql = "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ"; + + break; + + case java.sql.Connection.TRANSACTION_SERIALIZABLE: + sql = "SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE"; + + break; + + default: + throw SQLError.createSQLException(Messages.getString("Connection.25", new Object[] { level }), + MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor()); + } + + this.session.execSQL(null, sql, -1, null, false, this.nullStatementResultSetFactory, this.database, null, false); + + this.isolationLevel = level; + } + } + } + + /** + * JDBC 2.0 Install a type-map object as the default type-map for this + * connection + * + * @param map + * the type mapping + * @throws SQLException + * if a database error occurs. + */ + public void setTypeMap(java.util.Map> map) throws SQLException { + synchronized (getConnectionMutex()) { + this.typeMap = map; + } + } + + private void setupServerForTruncationChecks() throws SQLException { + synchronized (getConnectionMutex()) { + ModifiableProperty jdbcCompliantTruncation = this.propertySet + . getModifiableProperty(PropertyDefinitions.PNAME_jdbcCompliantTruncation); + if (jdbcCompliantTruncation.getValue()) { + String currentSqlMode = this.session.getServerSession().getServerVariable("sql_mode"); + + boolean strictTransTablesIsSet = StringUtils.indexOfIgnoreCase(currentSqlMode, "STRICT_TRANS_TABLES") != -1; + + if (currentSqlMode == null || currentSqlMode.length() == 0 || !strictTransTablesIsSet) { + StringBuilder commandBuf = new StringBuilder("SET sql_mode='"); + + if (currentSqlMode != null && currentSqlMode.length() > 0) { + commandBuf.append(currentSqlMode); + commandBuf.append(","); + } + + commandBuf.append("STRICT_TRANS_TABLES'"); + + this.session.execSQL(null, commandBuf.toString(), -1, null, false, this.nullStatementResultSetFactory, this.database, null, false); + + jdbcCompliantTruncation.setValue(false); // server's handling this for us now + } else if (strictTransTablesIsSet) { + // We didn't set it, but someone did, so we piggy back on it + jdbcCompliantTruncation.setValue(false); // server's handling this for us now + } + } + } + } + + /** + * Used by MiniAdmin to shutdown a MySQL server + * + * @throws SQLException + * if the command can not be issued. + */ + public void shutdownServer() throws SQLException { + try { + this.session.shutdownServer(); + } catch (CJException ex) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("Connection.UnhandledExceptionDuringShutdown"), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + + sqlEx.initCause(ex); + + throw sqlEx; + } + } + + /** + * Remove the given statement from the list of open statements + * + * @param stmt + * the Statement instance to remove + */ + public void unregisterStatement(JdbcStatement stmt) { + this.openStatements.remove(stmt); + } + + public boolean versionMeetsMinimum(int major, int minor, int subminor) { + return this.session.versionMeetsMinimum(major, minor, subminor); + } + + /** + * Returns cached metadata (or null if not cached) for the given query, + * which must match _exactly_. + * + * This method is synchronized by the caller on getMutex(), so if + * calling this method from internal code in the driver, make sure it's + * synchronized on the mutex that guards communication with the server. + * + * @param sql + * the query that is the key to the cache + * + * @return metadata cached for the given SQL, or none if it doesn't + * exist. + */ + public CachedResultSetMetaData getCachedMetaData(String sql) { + if (this.resultSetMetadataCache != null) { + synchronized (this.resultSetMetadataCache) { + return this.resultSetMetadataCache.get(sql); + } + } + + return null; // no cache exists + } + + /** + * Caches CachedResultSetMetaData that has been placed in the cache using + * the given SQL as a key. + * + * This method is synchronized by the caller on getMutex(), so if + * calling this method from internal code in the driver, make sure it's + * synchronized on the mutex that guards communication with the server. + * + * @param sql + * the query that the metadata pertains too. + * @param cachedMetaData + * metadata (if it exists) to populate the cache. + * @param resultSet + * the result set to retreive metadata from, or apply to. + * + * @throws SQLException + */ + public void initializeResultsMetadataFromCache(String sql, CachedResultSetMetaData cachedMetaData, ResultSetInternalMethods resultSet) throws SQLException { + + if (cachedMetaData == null) { + + // read from results + cachedMetaData = new CachedResultSetMetaDataImpl(); + + // assume that users will use named-based lookups + resultSet.getColumnDefinition().buildIndexMapping(); + resultSet.initializeWithMetadata(); + + if (resultSet instanceof UpdatableResultSet) { + ((UpdatableResultSet) resultSet).checkUpdatability(); + } + + resultSet.populateCachedMetaData(cachedMetaData); + + this.resultSetMetadataCache.put(sql, cachedMetaData); + } else { + resultSet.getColumnDefinition().initializeFrom(cachedMetaData); + resultSet.initializeWithMetadata(); + + if (resultSet instanceof UpdatableResultSet) { + ((UpdatableResultSet) resultSet).checkUpdatability(); + } + } + } + + /** + * Returns the comment that will be prepended to all statements + * sent to the server. + * + * @return the comment that will be prepended to all statements + * sent to the server. + */ + public String getStatementComment() { + return this.session.getProtocol().getQueryComment(); + } + + /** + * Sets the comment that will be prepended to all statements + * sent to the server. Do not use slash-star or star-slash tokens + * in the comment as these will be added by the driver itself. + * + * @param comment + * the comment that will be prepended to all statements + * sent to the server. + */ + public void setStatementComment(String comment) { + this.session.getProtocol().setQueryComment(comment); + } + + public void transactionBegun() { + synchronized (getConnectionMutex()) { + if (this.connectionLifecycleInterceptors != null) { + this.connectionLifecycleInterceptors.stream().forEach(ConnectionLifecycleInterceptor::transactionBegun); + } + } + } + + public void transactionCompleted() { + synchronized (getConnectionMutex()) { + if (this.connectionLifecycleInterceptors != null) { + this.connectionLifecycleInterceptors.stream().forEach(ConnectionLifecycleInterceptor::transactionCompleted); + } + } + } + + public boolean storesLowerCaseTableName() { + return this.session.getServerSession().storesLowerCaseTableNames(); + } + + private ExceptionInterceptor exceptionInterceptor; + + @Override + public ExceptionInterceptor getExceptionInterceptor() { + return this.exceptionInterceptor; + } + + public boolean isServerLocal() throws SQLException { + synchronized (getConnectionMutex()) { + try { + return this.session.isServerLocal(this.getSession()); + } catch (CJException ex) { + SQLException sqlEx = SQLExceptionsMapping.translateException(ex, getExceptionInterceptor()); + throw sqlEx; + } + } + } + + /** + * Returns the sql select limit max-rows for this session. + */ + public int getSessionMaxRows() { + synchronized (getConnectionMutex()) { + return this.session.getSessionMaxRows(); + } + } + + /** + * Sets the sql select limit max-rows for this session if different from current. + * + * @param max + * the new max-rows value to set. + * @throws SQLException + * if a database error occurs issuing the statement that sets the limit. + */ + public void setSessionMaxRows(int max) throws SQLException { + synchronized (getConnectionMutex()) { + if (this.session.getSessionMaxRows() != max) { + this.session.setSessionMaxRows(max); + this.session.execSQL(null, "SET SQL_SELECT_LIMIT=" + (this.session.getSessionMaxRows() == -1 ? "DEFAULT" : this.session.getSessionMaxRows()), + -1, null, false, this.nullStatementResultSetFactory, this.database, null, false); + } + } + } + + // until we flip catalog/schema, this is a no-op + public void setSchema(String schema) throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + } + } + + public String getSchema() throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + + return null; + } + } + + /** + * Terminates an open connection. Calling abort results in: + *
    + *
  • The connection marked as closed + *
  • Closes any physical connection to the database + *
  • Releases resources used by the connection + *
  • Insures that any thread that is currently accessing the connection will either progress to completion or throw an SQLException. + *
+ *

+ * Calling abort marks the connection closed and releases any resources. Calling abort on a closed connection is a no-op. + *

+ * It is possible that the aborting and releasing of the resources that are held by the connection can take an extended period of time. When the + * abort method returns, the connection will have been marked as closed and the Executor that was passed as a parameter to abort + * may still be executing tasks to release resources. + *

+ * This method checks to see that there is an SQLPermission object before allowing the method to proceed. If a SecurityManager + * exists and its checkPermission method denies calling abort, this method throws a java.lang.SecurityException. + * + * @param executor + * The Executor implementation which will + * be used by abort. + * @throws java.sql.SQLException + * if a database access error occurs or + * the {@code executor} is {@code null}, + * @throws java.lang.SecurityException + * if a security manager exists and its checkPermission method denies calling abort + * @see SecurityManager#checkPermission + * @see Executor + * @since 1.7 + */ + public void abort(Executor executor) throws SQLException { + SecurityManager sec = System.getSecurityManager(); + + if (sec != null) { + sec.checkPermission(ABORT_PERM); + } + + if (executor == null) { + throw SQLError.createSQLException(Messages.getString("Connection.26"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + executor.execute(new Runnable() { + + public void run() { + try { + abortInternal(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + }); + } + + public void setNetworkTimeout(Executor executor, final int milliseconds) throws SQLException { + synchronized (getConnectionMutex()) { + SecurityManager sec = System.getSecurityManager(); + + if (sec != null) { + sec.checkPermission(SET_NETWORK_TIMEOUT_PERM); + } + + if (executor == null) { + throw SQLError.createSQLException(Messages.getString("Connection.26"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + checkClosed(); + + executor.execute(new NetworkTimeoutSetter(this, milliseconds)); + } + } + + private static class NetworkTimeoutSetter implements Runnable { + private final WeakReference connRef; + private final int milliseconds; + + public NetworkTimeoutSetter(JdbcConnection conn, int milliseconds) { + this.connRef = new WeakReference<>(conn); + this.milliseconds = milliseconds; + } + + public void run() { + JdbcConnection conn = this.connRef.get(); + if (conn != null) { + synchronized (conn.getConnectionMutex()) { + ((NativeSession) conn.getSession()).setSocketTimeout(this.milliseconds); + } + } + } + } + + public int getNetworkTimeout() throws SQLException { + synchronized (getConnectionMutex()) { + checkClosed(); + return this.session.getSocketTimeout(); + } + } + + public Clob createClob() { + return new com.mysql.cj.jdbc.Clob(getExceptionInterceptor()); + } + + public Blob createBlob() { + return new com.mysql.cj.jdbc.Blob(getExceptionInterceptor()); + } + + public NClob createNClob() { + return new com.mysql.cj.jdbc.NClob(getExceptionInterceptor()); + } + + public SQLXML createSQLXML() throws SQLException { + return new MysqlSQLXML(getExceptionInterceptor()); + } + + /** + * Returns true if the connection has not been closed and is still valid. + * The driver shall submit a query on the connection or use some other + * mechanism that positively verifies the connection is still valid when + * this method is called. + *

+ * The query submitted by the driver to validate the connection shall be executed in the context of the current transaction. + * + * @param timeout + * - The time in seconds to wait for the database operation + * used to validate the connection to complete. If + * the timeout period expires before the operation + * completes, this method returns false. A value of + * 0 indicates a timeout is not applied to the + * database operation. + *

+ * @return true if the connection is valid, false otherwise + * @exception SQLException + * if the value supplied for timeout is less then 0 + * @since 1.6 + */ + public boolean isValid(int timeout) throws SQLException { + synchronized (getConnectionMutex()) { + if (isClosed()) { + return false; + } + + try { + try { + pingInternal(false, timeout * 1000); + } catch (Throwable t) { + try { + abortInternal(); + } catch (Throwable ignoreThrown) { + // we're dead now anyway + } + + return false; + } + + } catch (Throwable t) { + return false; + } + + return true; + } + } + + private ClientInfoProvider infoProvider; + + public ClientInfoProvider getClientInfoProviderImpl() throws SQLException { + synchronized (getConnectionMutex()) { + if (this.infoProvider == null) { + String clientInfoProvider = this.propertySet.getStringReadableProperty(PropertyDefinitions.PNAME_clientInfoProvider).getStringValue(); + try { + try { + this.infoProvider = (ClientInfoProvider) Util.getInstance(clientInfoProvider, new Class[0], new Object[0], + getExceptionInterceptor()); + } catch (CJException ex) { + if (ex.getCause() instanceof ClassCastException) { + // try with package name prepended + try { + this.infoProvider = (ClientInfoProvider) Util.getInstance("com.mysql.cj.jdbc." + clientInfoProvider, new Class[0], + new Object[0], getExceptionInterceptor()); + } catch (CJException e) { + throw SQLExceptionsMapping.translateException(e, getExceptionInterceptor()); + } + } + } + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("Connection.ClientInfoNotImplemented", new Object[] { clientInfoProvider }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + this.infoProvider.initialize(this, this.props); + } + + return this.infoProvider; + } + } + + public void setClientInfo(String name, String value) throws SQLClientInfoException { + try { + getClientInfoProviderImpl().setClientInfo(this, name, value); + } catch (SQLClientInfoException ciEx) { + throw ciEx; + } catch (SQLException | CJException sqlEx) { + SQLClientInfoException clientInfoEx = new SQLClientInfoException(); + clientInfoEx.initCause(sqlEx); + + throw clientInfoEx; + } + } + + public void setClientInfo(Properties properties) throws SQLClientInfoException { + try { + getClientInfoProviderImpl().setClientInfo(this, properties); + } catch (SQLClientInfoException ciEx) { + throw ciEx; + } catch (SQLException | CJException sqlEx) { + SQLClientInfoException clientInfoEx = new SQLClientInfoException(); + clientInfoEx.initCause(sqlEx); + + throw clientInfoEx; + } + } + + public String getClientInfo(String name) throws SQLException { + return getClientInfoProviderImpl().getClientInfo(this, name); + } + + public Properties getClientInfo() throws SQLException { + return getClientInfoProviderImpl().getClientInfo(this); + } + + public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + throw SQLError.createSQLFeatureNotSupportedException(); + } + + /** + * Returns an object that implements the given interface to allow access to non-standard methods, + * or standard methods not exposed by the proxy. + * The result may be either the object found to implement the interface or a proxy for that object. + * If the receiver implements the interface then that is the object. If the receiver is a wrapper + * and the wrapped object implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped object. If the receiver is not a + * wrapper and does not implement the interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping + // anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + } + + /** + * Returns true if this either implements the interface argument or is directly or indirectly a wrapper + * for an object that does. Returns false otherwise. If this implements the interface then return true, + * else if this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped + * object. If this does not implement the interface and is not a wrapper, return false. + * This method should be implemented as a low-cost operation compared to unwrap so that + * callers can use this method to avoid expensive unwrap calls that may fail. If this method + * returns true then calling unwrap with the same argument should succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a wrapper + * for an object with the given interface. + * @since 1.6 + */ + public boolean isWrapperFor(Class iface) throws SQLException { + + // This works for classes that aren't actually wrapping + // anything + return iface.isInstance(this); + } + + @Override + public NativeSession getSession() { + return this.session; + } + + @Override + public String getHostPortPair() { + return this.origHostInfo.getHostPortPair(); + } + + @Override + public void handleNormalClose() { + try { + close(); + } catch (SQLException e) { + ExceptionFactory.createException(e.getMessage(), e); + } + } + + @Override + public void handleReconnect() { + createNewIO(true); + } + + @Override + public void handleCleanup(Throwable whyCleanedUp) { + cleanup(whyCleanedUp); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ConnectionWrapper.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ConnectionWrapper.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ConnectionWrapper.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,1131 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.lang.reflect.Proxy; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.NClob; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.sql.Wrapper; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +import com.mysql.cj.Messages; +import com.mysql.cj.MysqlConnection; +import com.mysql.cj.ServerVersion; +import com.mysql.cj.Session; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.ConnectionIsClosedException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.interceptors.QueryInterceptor; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.result.CachedResultSetMetaData; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; + +/** + * This class serves as a wrapper for the connection object. It is returned to the application server which may wrap it again and then return it to the + * application client in response to dataSource.getConnection(). + * + * All method invocations are forwarded to underlying connection unless the close method was previously called, in which case a SQLException is thrown. The + * close method performs a 'logical close' on the connection. + * + * All SQL exceptions thrown by the physical connection are intercepted and sent to connectionEvent listeners before being thrown to client. + */ +public class ConnectionWrapper extends WrapperBase implements JdbcConnection { + protected JdbcConnection mc = null; + + private String invalidHandleStr = "Logical handle no longer valid"; + + private boolean closed; + + private boolean isForXa; + + protected static ConnectionWrapper getInstance(MysqlPooledConnection mysqlPooledConnection, JdbcConnection mysqlConnection, boolean forXa) + throws SQLException { + return new ConnectionWrapper(mysqlPooledConnection, mysqlConnection, forXa); + } + + /** + * Construct a new LogicalHandle and set instance variables + * + * @param mysqlPooledConnection + * reference to object that instantiated this object + * @param mysqlConnection + * physical connection to db + * + * @throws SQLException + * if an error occurs. + */ + public ConnectionWrapper(MysqlPooledConnection mysqlPooledConnection, JdbcConnection mysqlConnection, boolean forXa) throws SQLException { + super(mysqlPooledConnection); + + this.mc = mysqlConnection; + this.closed = false; + this.isForXa = forXa; + + if (this.isForXa) { + setInGlobalTx(false); + } + } + + public void setAutoCommit(boolean autoCommit) throws SQLException { + + if (autoCommit && isInGlobalTx()) { + throw SQLError.createSQLException(Messages.getString("ConnectionWrapper.0"), MysqlErrorNumbers.SQL_STATE_INVALID_TRANSACTION_TERMINATION, + MysqlErrorNumbers.ER_XA_RMERR, this.exceptionInterceptor); + } + + try { + this.mc.setAutoCommit(autoCommit); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public boolean getAutoCommit() throws SQLException { + + try { + return this.mc.getAutoCommit(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return false; // we don't reach this code, compiler can't tell + } + + public void setCatalog(String catalog) throws SQLException { + + try { + this.mc.setCatalog(catalog); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public String getCatalog() throws SQLException { + + try { + return this.mc.getCatalog(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public boolean isClosed() throws SQLException { + return (this.closed || this.mc.isClosed()); + } + + public boolean isMasterConnection() { + return this.mc.isMasterConnection(); + } + + public void setHoldability(int arg0) throws SQLException { + try { + this.mc.setHoldability(arg0); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public int getHoldability() throws SQLException { + try { + return this.mc.getHoldability(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return Statement.CLOSE_CURRENT_RESULT; // we don't reach this code, compiler can't tell + } + + /** + * Allows clients to determine how long this connection has been idle. + * + * @return how long the connection has been idle. + */ + public long getIdleFor() { + return this.mc.getIdleFor(); + } + + public java.sql.DatabaseMetaData getMetaData() throws SQLException { + try { + return this.mc.getMetaData(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public void setReadOnly(boolean readOnly) throws SQLException { + try { + this.mc.setReadOnly(readOnly); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public boolean isReadOnly() throws SQLException { + try { + return this.mc.isReadOnly(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return false; // we don't reach this code, compiler can't tell + } + + public java.sql.Savepoint setSavepoint() throws SQLException { + if (isInGlobalTx()) { + throw SQLError.createSQLException(Messages.getString("ConnectionWrapper.0"), MysqlErrorNumbers.SQL_STATE_INVALID_TRANSACTION_TERMINATION, + MysqlErrorNumbers.ER_XA_RMERR, this.exceptionInterceptor); + } + + try { + return this.mc.setSavepoint(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.Savepoint setSavepoint(String arg0) throws SQLException { + if (isInGlobalTx()) { + throw SQLError.createSQLException(Messages.getString("ConnectionWrapper.0"), MysqlErrorNumbers.SQL_STATE_INVALID_TRANSACTION_TERMINATION, + MysqlErrorNumbers.ER_XA_RMERR, this.exceptionInterceptor); + } + + try { + return this.mc.setSavepoint(arg0); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public void setTransactionIsolation(int level) throws SQLException { + try { + this.mc.setTransactionIsolation(level); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public int getTransactionIsolation() throws SQLException { + try { + return this.mc.getTransactionIsolation(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return TRANSACTION_REPEATABLE_READ; // we don't reach this code, compiler can't tell + } + + public java.util.Map> getTypeMap() throws SQLException { + try { + return this.mc.getTypeMap(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.SQLWarning getWarnings() throws SQLException { + try { + return this.mc.getWarnings(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public void clearWarnings() throws SQLException { + try { + this.mc.clearWarnings(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + /** + * The physical connection is not actually closed. the physical connection is closed when the application server calls mysqlPooledConnection.close(). this + * object is de-referenced by the pooled connection each time mysqlPooledConnection.getConnection() is called by app server. + * + * @throws SQLException + * if an error occurs + */ + public void close() throws SQLException { + try { + close(true); + } finally { + this.unwrappedInterfaces = null; + } + } + + public void commit() throws SQLException { + if (isInGlobalTx()) { + throw SQLError.createSQLException(Messages.getString("ConnectionWrapper.1"), MysqlErrorNumbers.SQL_STATE_INVALID_TRANSACTION_TERMINATION, + MysqlErrorNumbers.ER_XA_RMERR, this.exceptionInterceptor); + } + + try { + this.mc.commit(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public java.sql.Statement createStatement() throws SQLException { + try { + return StatementWrapper.getInstance(this, this.pooledConnection, this.mc.createStatement()); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + try { + return StatementWrapper.getInstance(this, this.pooledConnection, this.mc.createStatement(resultSetType, resultSetConcurrency)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.Statement createStatement(int arg0, int arg1, int arg2) throws SQLException { + try { + return StatementWrapper.getInstance(this, this.pooledConnection, this.mc.createStatement(arg0, arg1, arg2)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public String nativeSQL(String sql) throws SQLException { + try { + return this.mc.nativeSQL(sql); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.CallableStatement prepareCall(String sql) throws SQLException { + try { + return CallableStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareCall(sql)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + try { + return CallableStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareCall(sql, resultSetType, resultSetConcurrency)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.CallableStatement prepareCall(String arg0, int arg1, int arg2, int arg3) throws SQLException { + try { + return CallableStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareCall(arg0, arg1, arg2, arg3)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.PreparedStatement clientPrepare(String sql) throws SQLException { + try { + return new PreparedStatementWrapper(this, this.pooledConnection, this.mc.clientPrepareStatement(sql)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement clientPrepare(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + try { + return new PreparedStatementWrapper(this, this.pooledConnection, this.mc.clientPrepareStatement(sql, resultSetType, resultSetConcurrency)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException { + java.sql.PreparedStatement res = null; + try { + res = PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareStatement(sql)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return res; + } + + public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareStatement(sql, resultSetType, resultSetConcurrency)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.PreparedStatement prepareStatement(String arg0, int arg1, int arg2, int arg3) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareStatement(arg0, arg1, arg2, arg3)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.PreparedStatement prepareStatement(String arg0, int arg1) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareStatement(arg0, arg1)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.PreparedStatement prepareStatement(String arg0, int[] arg1) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareStatement(arg0, arg1)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public java.sql.PreparedStatement prepareStatement(String arg0, String[] arg1) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.prepareStatement(arg0, arg1)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // we don't reach this code, compiler can't tell + } + + public void releaseSavepoint(Savepoint arg0) throws SQLException { + try { + this.mc.releaseSavepoint(arg0); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public void rollback() throws SQLException { + if (isInGlobalTx()) { + throw SQLError.createSQLException(Messages.getString("ConnectionWrapper.2"), MysqlErrorNumbers.SQL_STATE_INVALID_TRANSACTION_TERMINATION, + MysqlErrorNumbers.ER_XA_RMERR, this.exceptionInterceptor); + } + + try { + this.mc.rollback(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public void rollback(Savepoint arg0) throws SQLException { + if (isInGlobalTx()) { + throw SQLError.createSQLException(Messages.getString("ConnectionWrapper.2"), MysqlErrorNumbers.SQL_STATE_INVALID_TRANSACTION_TERMINATION, + MysqlErrorNumbers.ER_XA_RMERR, this.exceptionInterceptor); + } + + try { + this.mc.rollback(arg0); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public boolean isSameResource(com.mysql.cj.jdbc.JdbcConnection c) { + if (c instanceof ConnectionWrapper) { + return this.mc.isSameResource(((ConnectionWrapper) c).mc); + } + return this.mc.isSameResource(c); + } + + protected void close(boolean fireClosedEvent) throws SQLException { + synchronized (this.pooledConnection) { + if (this.closed) { + return; + } + + if (!isInGlobalTx() && this.mc.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_rollbackOnPooledClose).getValue() + && !this.getAutoCommit()) { + rollback(); + } + + if (fireClosedEvent) { + this.pooledConnection.callConnectionEventListeners(MysqlPooledConnection.CONNECTION_CLOSED_EVENT, null); + } + + // set closed status to true so that if application client tries to make additional calls a sqlException will be thrown. The physical connection is + // re-used by the pooled connection each time getConnection is called. + this.closed = true; + } + } + + public void checkClosed() { + if (this.closed) { + throw ExceptionFactory.createException(ConnectionIsClosedException.class, this.invalidHandleStr, this.exceptionInterceptor); + } + } + + public boolean isInGlobalTx() { + return this.mc.isInGlobalTx(); + } + + public void setInGlobalTx(boolean flag) { + this.mc.setInGlobalTx(flag); + } + + public void ping() throws SQLException { + if (this.mc != null) { + this.mc.ping(); + } + } + + public void changeUser(String userName, String newPassword) throws SQLException { + try { + this.mc.changeUser(userName, newPassword); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + @Deprecated + public void clearHasTriedMaster() { + this.mc.clearHasTriedMaster(); + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.clientPrepareStatement(sql)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.clientPrepareStatement(sql, autoGenKeyIndex)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.clientPrepareStatement(sql, resultSetType, resultSetConcurrency)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, + this.mc.clientPrepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.clientPrepareStatement(sql, autoGenKeyIndexes)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement clientPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.clientPrepareStatement(sql, autoGenKeyColNames)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public int getActiveStatementCount() { + return this.mc.getActiveStatementCount(); + } + + public String getStatementComment() { + return this.mc.getStatementComment(); + } + + @Deprecated + public boolean hasTriedMaster() { + return this.mc.hasTriedMaster(); + } + + public boolean lowerCaseTableNames() { + return this.mc.lowerCaseTableNames(); + } + + public void resetServerState() throws SQLException { + try { + this.mc.resetServerState(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.serverPrepareStatement(sql)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.serverPrepareStatement(sql, autoGenKeyIndex)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.serverPrepareStatement(sql, resultSetType, resultSetConcurrency)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, + this.mc.serverPrepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.serverPrepareStatement(sql, autoGenKeyIndexes)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public java.sql.PreparedStatement serverPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + try { + return PreparedStatementWrapper.getInstance(this, this.pooledConnection, this.mc.serverPrepareStatement(sql, autoGenKeyColNames)); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; + } + + public void setFailedOver(boolean flag) { + this.mc.setFailedOver(flag); + } + + public void setStatementComment(String comment) { + this.mc.setStatementComment(comment); + } + + public void shutdownServer() throws SQLException { + try { + this.mc.shutdownServer(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + } + + public int getAutoIncrementIncrement() { + return this.mc.getAutoIncrementIncrement(); + } + + public ExceptionInterceptor getExceptionInterceptor() { + return this.pooledConnection.getExceptionInterceptor(); + } + + public boolean hasSameProperties(JdbcConnection c) { + return this.mc.hasSameProperties(c); + } + + public Properties getProperties() { + return this.mc.getProperties(); + } + + public String getHost() { + return this.mc.getHost(); + } + + public void setProxy(JdbcConnection conn) { + this.mc.setProxy(conn); + } + + public void setTypeMap(Map> map) throws SQLException { + try { + this.mc.setTypeMap(map); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + } + + public boolean isServerLocal() throws SQLException { + return this.mc.isServerLocal(); + } + + public void setSchema(String schema) throws SQLException { + this.mc.setSchema(schema); + } + + public String getSchema() throws SQLException { + return this.mc.getSchema(); + } + + public void abort(Executor executor) throws SQLException { + this.mc.abort(executor); + } + + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + this.mc.setNetworkTimeout(executor, milliseconds); + } + + public int getNetworkTimeout() throws SQLException { + return this.mc.getNetworkTimeout(); + } + + public void abortInternal() throws SQLException { + this.mc.abortInternal(); + } + + public Object getConnectionMutex() { + return this.mc.getConnectionMutex(); + } + + public int getSessionMaxRows() { + return this.mc.getSessionMaxRows(); + } + + public void setSessionMaxRows(int max) throws SQLException { + this.mc.setSessionMaxRows(max); + } + + public Clob createClob() throws SQLException { + try { + return ((java.sql.Connection) this.mc).createClob(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + public Blob createBlob() throws SQLException { + try { + return ((java.sql.Connection) this.mc).createBlob(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + public NClob createNClob() throws SQLException { + try { + return ((java.sql.Connection) this.mc).createNClob(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + public SQLXML createSQLXML() throws SQLException { + try { + return ((java.sql.Connection) this.mc).createSQLXML(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + public synchronized boolean isValid(int timeout) throws SQLException { + try { + return ((java.sql.Connection) this.mc).isValid(timeout); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return false; // never reached, but compiler can't tell + } + + public void setClientInfo(String name, String value) throws SQLClientInfoException { + try { + checkClosed(); + + ((java.sql.Connection) this.mc).setClientInfo(name, value); + } catch (SQLException sqlException) { + try { + checkAndFireConnectionError(sqlException); + } catch (SQLException sqlEx2) { + SQLClientInfoException clientEx = new SQLClientInfoException(); + clientEx.initCause(sqlEx2); + + throw clientEx; + } + } + } + + public void setClientInfo(Properties properties) throws SQLClientInfoException { + try { + checkClosed(); + + ((java.sql.Connection) this.mc).setClientInfo(properties); + } catch (SQLException sqlException) { + try { + checkAndFireConnectionError(sqlException); + } catch (SQLException sqlEx2) { + SQLClientInfoException clientEx = new SQLClientInfoException(); + clientEx.initCause(sqlEx2); + + throw clientEx; + } + } + } + + public String getClientInfo(String name) throws SQLException { + try { + return ((java.sql.Connection) this.mc).getClientInfo(name); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + public Properties getClientInfo() throws SQLException { + try { + return ((java.sql.Connection) this.mc).getClientInfo(); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { + try { + return ((java.sql.Connection) this.mc).createArrayOf(typeName, elements); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + try { + return ((java.sql.Connection) this.mc).createStruct(typeName, attributes); + } catch (SQLException sqlException) { + checkAndFireConnectionError(sqlException); + } + + return null; // never reached, but compiler can't tell + } + + public synchronized T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + if ("java.sql.Connection".equals(iface.getName()) || "java.sql.Wrapper.class".equals(iface.getName())) { + return iface.cast(this); + } + + if (this.unwrappedInterfaces == null) { + this.unwrappedInterfaces = new HashMap<>(); + } + + Object cachedUnwrapped = this.unwrappedInterfaces.get(iface); + + if (cachedUnwrapped == null) { + cachedUnwrapped = Proxy.newProxyInstance(this.mc.getClass().getClassLoader(), new Class[] { iface }, + new ConnectionErrorFiringInvocationHandler(this.mc)); + this.unwrappedInterfaces.put(iface, cachedUnwrapped); + } + + return iface.cast(cachedUnwrapped); + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("Common.UnableToUnwrap", new Object[] { iface.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } + + public boolean isWrapperFor(Class iface) throws SQLException { + boolean isInstance = iface.isInstance(this); + + if (isInstance) { + return true; + } + + return (iface.getName().equals(JdbcConnection.class.getName()) || iface.getName().equals(MysqlConnection.class.getName()) + || iface.getName().equals(java.sql.Connection.class.getName()) || iface.getName().equals(Wrapper.class.getName()) + || iface.getName().equals(AutoCloseable.class.getName())); + } + + @Override + public Session getSession() { + return this.mc.getSession(); + } + + @Override + public long getId() { + return this.mc.getId(); + } + + @Override + public String getURL() { + return this.mc.getURL(); + } + + @Override + public String getUser() { + return this.mc.getUser(); + } + + @Override + public void createNewIO(boolean isForReconnect) { + this.mc.createNewIO(isForReconnect); + } + + @Override + public boolean isProxySet() { + return this.mc.isProxySet(); + } + + @Override + public JdbcPropertySet getPropertySet() { + return this.mc.getPropertySet(); + } + + @Override + public CachedResultSetMetaData getCachedMetaData(String sql) { + return this.mc.getCachedMetaData(sql); + } + + @Override + public String getCharacterSetMetadata() { + return this.mc.getCharacterSetMetadata(); + } + + @Override + public Statement getMetadataSafeStatement() throws SQLException { + return this.mc.getMetadataSafeStatement(); + } + + @Override + public ServerVersion getServerVersion() { + return this.mc.getServerVersion(); + } + + @Override + public List getQueryInterceptorsInstances() { + return this.mc.getQueryInterceptorsInstances(); + } + + @Override + public void initializeResultsMetadataFromCache(String sql, CachedResultSetMetaData cachedMetaData, ResultSetInternalMethods resultSet) throws SQLException { + this.mc.initializeResultsMetadataFromCache(sql, cachedMetaData, resultSet); + } + + @Override + public void initializeSafeQueryInterceptors() throws SQLException { + this.mc.initializeSafeQueryInterceptors(); + } + + @Override + public boolean isReadOnly(boolean useSessionStatus) throws SQLException { + return this.mc.isReadOnly(useSessionStatus); + } + + @Override + public void pingInternal(boolean checkForClosedConnection, int timeoutMillis) throws SQLException { + this.mc.pingInternal(checkForClosedConnection, timeoutMillis); + } + + @Override + public void realClose(boolean calledExplicitly, boolean issueRollback, boolean skipLocalTeardown, Throwable reason) throws SQLException { + this.mc.realClose(calledExplicitly, issueRollback, skipLocalTeardown, reason); + } + + @Override + public void recachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException { + this.mc.recachePreparedStatement(pstmt); + } + + @Override + public void decachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException { + this.mc.decachePreparedStatement(pstmt); + } + + @Override + public void registerStatement(com.mysql.cj.jdbc.JdbcStatement stmt) { + this.mc.registerStatement(stmt); + } + + @Override + public void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException { + this.mc.setReadOnlyInternal(readOnlyFlag); + } + + @Override + public boolean storesLowerCaseTableName() { + return this.mc.storesLowerCaseTableName(); + } + + @Override + public void throwConnectionClosedException() throws SQLException { + this.mc.throwConnectionClosedException(); + } + + @Override + public void transactionBegun() { + this.mc.transactionBegun(); + } + + @Override + public void transactionCompleted() { + this.mc.transactionCompleted(); + } + + @Override + public void unregisterStatement(com.mysql.cj.jdbc.JdbcStatement stmt) { + this.mc.unregisterStatement(stmt); + } + + @Override + public void unSafeQueryInterceptors() throws SQLException { + this.mc.unSafeQueryInterceptors(); + } + + @Override + public JdbcConnection getMultiHostSafeProxy() { + return this.mc.getMultiHostSafeProxy(); + } + + @Override + public JdbcConnection getActiveMySQLConnection() { + return this.mc.getActiveMySQLConnection(); + } + + @Override + public ClientInfoProvider getClientInfoProviderImpl() throws SQLException { + return this.mc.getClientInfoProviderImpl(); + } + + @Override + public String getHostPortPair() { + return this.mc.getHostPortPair(); + } + + @Override + public void normalClose() { + this.mc.normalClose(); + } + + @Override + public void cleanup(Throwable whyCleanedUp) { + this.mc.cleanup(whyCleanedUp); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/DatabaseMetaData.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/DatabaseMetaData.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/DatabaseMetaData.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,5592 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import static com.mysql.cj.jdbc.DatabaseMetaData.ProcedureType.FUNCTION; +import static com.mysql.cj.jdbc.DatabaseMetaData.ProcedureType.PROCEDURE; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.RowIdLifetime; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.StringTokenizer; +import java.util.TreeMap; +import java.util.TreeSet; + +import com.mysql.cj.Constants; +import com.mysql.cj.Messages; +import com.mysql.cj.MysqlType; +import com.mysql.cj.NativeSession; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.AssertionFailedException; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping; +import com.mysql.cj.jdbc.result.ResultSetFactory; +import com.mysql.cj.protocol.ColumnDefinition; +import com.mysql.cj.protocol.ResultsetRow; +import com.mysql.cj.protocol.a.result.ByteArrayRow; +import com.mysql.cj.protocol.a.result.ResultsetRowsStatic; +import com.mysql.cj.result.DefaultColumnDefinition; +import com.mysql.cj.result.Field; +import com.mysql.cj.result.Row; +import com.mysql.cj.util.StringUtils; + +/** + * JDBC Interface to Mysql functions + *

+ * This class provides information about the database as a whole. + *

+ *

+ * Many of the methods here return lists of information in ResultSets. You can use the normal ResultSet methods such as getString and getInt to retrieve the + * data from these ResultSets. If a given form of metadata is not available, these methods show throw a SQLException. + *

+ *

+ * Some of these methods take arguments that are String patterns. These methods all have names such as fooPattern. Within a pattern String "%" means match any + * substring of 0 or more characters and "_" means match any one character. + *

+ */ +public class DatabaseMetaData implements java.sql.DatabaseMetaData { + + /** + * Default max buffer size. See {@link PropertyDefinitions#PNAME_maxAllowedPacket}. + */ + protected static int maxBufferSize = 65535; // TODO find a way to use actual (not default) value + + protected abstract class IteratorWithCleanup { + abstract void close() throws SQLException; + + abstract boolean hasNext() throws SQLException; + + abstract T next() throws SQLException; + } + + class LocalAndReferencedColumns { + String constraintName; + + List localColumnsList; + + String referencedCatalog; + + List referencedColumnsList; + + String referencedTable; + + LocalAndReferencedColumns(List localColumns, List refColumns, String constName, String refCatalog, String refTable) { + this.localColumnsList = localColumns; + this.referencedColumnsList = refColumns; + this.constraintName = constName; + this.referencedTable = refTable; + this.referencedCatalog = refCatalog; + } + } + + protected class ResultSetIterator extends IteratorWithCleanup { + int colIndex; + + ResultSet resultSet; + + ResultSetIterator(ResultSet rs, int index) { + this.resultSet = rs; + this.colIndex = index; + } + + @Override + void close() throws SQLException { + this.resultSet.close(); + } + + @Override + boolean hasNext() throws SQLException { + return this.resultSet.next(); + } + + @Override + String next() throws SQLException { + return this.resultSet.getObject(this.colIndex).toString(); + } + } + + protected class SingleStringIterator extends IteratorWithCleanup { + boolean onFirst = true; + + String value; + + SingleStringIterator(String s) { + this.value = s; + } + + @Override + void close() throws SQLException { + // not needed + + } + + @Override + boolean hasNext() throws SQLException { + return this.onFirst; + } + + @Override + String next() throws SQLException { + this.onFirst = false; + return this.value; + } + } + + /** + * Parses and represents common data type information used by various + * column/parameter methods. + */ + class TypeDescriptor { + int bufferLength; + + int charOctetLength; + + Integer columnSize = null; + + Integer decimalDigits = null; + + String isNullable; + + int nullability; + + int numPrecRadix = 10; + + String mysqlTypeName; + MysqlType mysqlType; + + TypeDescriptor(String typeInfo, String nullabilityInfo) throws SQLException { + if (typeInfo == null) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.0"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + this.mysqlType = MysqlType.getByName(typeInfo); + + // Figure Out the Size + + String temp; + java.util.StringTokenizer tokenizer; + int maxLength = 0; + + switch (this.mysqlType) { + case ENUM: + temp = typeInfo.substring(typeInfo.indexOf("(") + 1, typeInfo.lastIndexOf(")")); + tokenizer = new java.util.StringTokenizer(temp, ","); + while (tokenizer.hasMoreTokens()) { + String nextToken = tokenizer.nextToken(); + maxLength = Math.max(maxLength, (nextToken.length() - 2)); + } + this.columnSize = Integer.valueOf(maxLength); + break; + + case SET: + temp = typeInfo.substring(typeInfo.indexOf("(") + 1, typeInfo.lastIndexOf(")")); + tokenizer = new java.util.StringTokenizer(temp, ","); + + int numElements = tokenizer.countTokens(); + if (numElements > 0) { + maxLength += (numElements - 1); + } + + while (tokenizer.hasMoreTokens()) { + String setMember = tokenizer.nextToken().trim(); + + if (setMember.startsWith("'") && setMember.endsWith("'")) { + maxLength += setMember.length() - 2; + } else { + maxLength += setMember.length(); + } + } + this.columnSize = Integer.valueOf(maxLength); + break; + + case DECIMAL: + case DECIMAL_UNSIGNED: + case FLOAT: + case FLOAT_UNSIGNED: + case DOUBLE: + case DOUBLE_UNSIGNED: + if (typeInfo.indexOf(",") != -1) { + // Numeric with decimals + this.columnSize = Integer.valueOf(typeInfo.substring((typeInfo.indexOf("(") + 1), (typeInfo.indexOf(","))).trim()); + this.decimalDigits = Integer.valueOf(typeInfo.substring((typeInfo.indexOf(",") + 1), (typeInfo.indexOf(")"))).trim()); + } else { + switch (this.mysqlType) { + case DECIMAL: + case DECIMAL_UNSIGNED: + this.columnSize = Integer.valueOf(65); + break; + case FLOAT: + case FLOAT_UNSIGNED: + this.columnSize = Integer.valueOf(12); + break; + case DOUBLE: + case DOUBLE_UNSIGNED: + this.columnSize = Integer.valueOf(22); + break; + default: + break; + } + this.decimalDigits = 0; + } + break; + + case CHAR: + case VARCHAR: + case TINYTEXT: + case MEDIUMTEXT: + case LONGTEXT: + case JSON: + case TEXT: + case TINYBLOB: + case MEDIUMBLOB: + case LONGBLOB: + case BLOB: + case BINARY: + case VARBINARY: + case BIT: + if (this.mysqlType == MysqlType.CHAR) { + this.columnSize = Integer.valueOf(1); + } + if (typeInfo.indexOf("(") != -1) { + int endParenIndex = typeInfo.indexOf(")"); + + if (endParenIndex == -1) { + endParenIndex = typeInfo.length(); + } + + this.columnSize = Integer.valueOf(typeInfo.substring((typeInfo.indexOf("(") + 1), endParenIndex).trim()); + + // Adjust for pseudo-boolean + if (DatabaseMetaData.this.tinyInt1isBit && this.columnSize.intValue() == 1 + && StringUtils.startsWithIgnoreCase(typeInfo, 0, "tinyint")) { + if (DatabaseMetaData.this.transformedBitIsBoolean) { + this.mysqlType = MysqlType.BOOLEAN; + } else { + this.mysqlType = MysqlType.BIT; + } + } + } + + break; + + case TINYINT: + case TINYINT_UNSIGNED: + if (DatabaseMetaData.this.tinyInt1isBit && typeInfo.indexOf("(1)") != -1) { + if (DatabaseMetaData.this.transformedBitIsBoolean) { + this.mysqlType = MysqlType.BOOLEAN; + } else { + this.mysqlType = MysqlType.BIT; + } + } else { + this.columnSize = Integer.valueOf(3); + } + break; + + case BOOLEAN: + case GEOMETRY: + case NULL: + case UNKNOWN: + case YEAR: + + default: + } + + // if not defined explicitly take the max precision + if (this.columnSize == null) { + // JDBC spec reserved only 'int' type for precision, thus we need to cut longer values + this.columnSize = this.mysqlType.getPrecision() > Integer.MAX_VALUE ? Integer.MAX_VALUE : this.mysqlType.getPrecision().intValue(); + } + + // BUFFER_LENGTH + this.bufferLength = maxBufferSize; + + // NUM_PREC_RADIX (is this right for char?) + this.numPrecRadix = 10; + + // Nullable? + if (nullabilityInfo != null) { + if (nullabilityInfo.equals("YES")) { + this.nullability = java.sql.DatabaseMetaData.columnNullable; + this.isNullable = "YES"; + + } else if (nullabilityInfo.equals("UNKNOWN")) { + this.nullability = java.sql.DatabaseMetaData.columnNullableUnknown; + this.isNullable = ""; + + // IS_NULLABLE + } else { + this.nullability = java.sql.DatabaseMetaData.columnNoNulls; + this.isNullable = "NO"; + } + } else { + this.nullability = java.sql.DatabaseMetaData.columnNoNulls; + this.isNullable = "NO"; + } + } + } + + /** + * Helper class to provide means of comparing indexes by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. + */ + protected class IndexMetaDataKey implements Comparable { + Boolean columnNonUnique; + Short columnType; + String columnIndexName; + Short columnOrdinalPosition; + + IndexMetaDataKey(boolean columnNonUnique, short columnType, String columnIndexName, short columnOrdinalPosition) { + this.columnNonUnique = columnNonUnique; + this.columnType = columnType; + this.columnIndexName = columnIndexName; + this.columnOrdinalPosition = columnOrdinalPosition; + } + + public int compareTo(IndexMetaDataKey indexInfoKey) { + int compareResult; + + if ((compareResult = this.columnNonUnique.compareTo(indexInfoKey.columnNonUnique)) != 0) { + return compareResult; + } + if ((compareResult = this.columnType.compareTo(indexInfoKey.columnType)) != 0) { + return compareResult; + } + if ((compareResult = this.columnIndexName.compareTo(indexInfoKey.columnIndexName)) != 0) { + return compareResult; + } + return this.columnOrdinalPosition.compareTo(indexInfoKey.columnOrdinalPosition); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (obj == this) { + return true; + } + + if (!(obj instanceof IndexMetaDataKey)) { + return false; + } + return compareTo((IndexMetaDataKey) obj) == 0; + } + + @Override + public int hashCode() { + assert false : "hashCode not designed"; + return 0; + } + } + + /** + * Helper class to provide means of comparing tables by TABLE_TYPE, TABLE_CAT, TABLE_SCHEM and TABLE_NAME. + */ + protected class TableMetaDataKey implements Comparable { + String tableType; + String tableCat; + String tableSchem; + String tableName; + + TableMetaDataKey(String tableType, String tableCat, String tableSchem, String tableName) { + this.tableType = tableType == null ? "" : tableType; + this.tableCat = tableCat == null ? "" : tableCat; + this.tableSchem = tableSchem == null ? "" : tableSchem; + this.tableName = tableName == null ? "" : tableName; + } + + public int compareTo(TableMetaDataKey tablesKey) { + int compareResult; + + if ((compareResult = this.tableType.compareTo(tablesKey.tableType)) != 0) { + return compareResult; + } + if ((compareResult = this.tableCat.compareTo(tablesKey.tableCat)) != 0) { + return compareResult; + } + if ((compareResult = this.tableSchem.compareTo(tablesKey.tableSchem)) != 0) { + return compareResult; + } + return this.tableName.compareTo(tablesKey.tableName); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (obj == this) { + return true; + } + + if (!(obj instanceof TableMetaDataKey)) { + return false; + } + return compareTo((TableMetaDataKey) obj) == 0; + } + + @Override + public int hashCode() { + assert false : "hashCode not designed"; + return 0; + } + } + + /** + * Helper/wrapper class to provide means of sorting objects by using a sorting key. + */ + protected class ComparableWrapper, V> implements Comparable> { + K key; + V value; + + public ComparableWrapper(K key, V value) { + this.key = key; + this.value = value; + } + + public K getKey() { + return this.key; + } + + public V getValue() { + return this.value; + } + + public int compareTo(ComparableWrapper other) { + return getKey().compareTo(other.getKey()); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (obj == this) { + return true; + } + + if (!(obj instanceof ComparableWrapper)) { + return false; + } + + Object otherKey = ((ComparableWrapper) obj).getKey(); + return this.key.equals(otherKey); + } + + @Override + public int hashCode() { + assert false : "hashCode not designed"; + return 0; + } + + @Override + public String toString() { + return "{KEY:" + this.key + "; VALUE:" + this.value + "}"; + } + } + + /** + * Enumeration for Table Types + */ + protected enum TableType { + LOCAL_TEMPORARY("LOCAL TEMPORARY"), SYSTEM_TABLE("SYSTEM TABLE"), SYSTEM_VIEW("SYSTEM VIEW"), TABLE("TABLE", new String[] { "BASE TABLE" }), + VIEW("VIEW"), UNKNOWN("UNKNOWN"); + + private String name; + private byte[] nameAsBytes; + private String[] synonyms; + + TableType(String tableTypeName) { + this(tableTypeName, null); + } + + TableType(String tableTypeName, String[] tableTypeSynonyms) { + this.name = tableTypeName; + this.nameAsBytes = tableTypeName.getBytes(); + this.synonyms = tableTypeSynonyms; + } + + String getName() { + return this.name; + } + + byte[] asBytes() { + return this.nameAsBytes; + } + + boolean equalsTo(String tableTypeName) { + return this.name.equalsIgnoreCase(tableTypeName); + } + + static TableType getTableTypeEqualTo(String tableTypeName) { + for (TableType tableType : TableType.values()) { + if (tableType.equalsTo(tableTypeName)) { + return tableType; + } + } + return UNKNOWN; + } + + boolean compliesWith(String tableTypeName) { + if (equalsTo(tableTypeName)) { + return true; + } + if (this.synonyms != null) { + for (String synonym : this.synonyms) { + if (synonym.equalsIgnoreCase(tableTypeName)) { + return true; + } + } + } + return false; + } + + static TableType getTableTypeCompliantWith(String tableTypeName) { + for (TableType tableType : TableType.values()) { + if (tableType.compliesWith(tableTypeName)) { + return tableType; + } + } + return UNKNOWN; + } + } + + /** + * Enumeration for Procedure Types + */ + protected enum ProcedureType { + PROCEDURE, FUNCTION; + } + + protected static final int MAX_IDENTIFIER_LENGTH = 64; + + private static final int DEFERRABILITY = 13; + + private static final int DELETE_RULE = 10; + + private static final int FK_NAME = 11; + + private static final int FKCOLUMN_NAME = 7; + + private static final int FKTABLE_CAT = 4; + + private static final int FKTABLE_NAME = 6; + + private static final int FKTABLE_SCHEM = 5; + + private static final int KEY_SEQ = 8; + + private static final int PK_NAME = 12; + + private static final int PKCOLUMN_NAME = 3; + + // + // Column indexes used by all DBMD foreign key ResultSets + // + private static final int PKTABLE_CAT = 0; + + private static final int PKTABLE_NAME = 2; + + private static final int PKTABLE_SCHEM = 1; + + /** The table type for generic tables that support foreign keys. */ + private static final String SUPPORTS_FK = "SUPPORTS_FK"; + + protected static final byte[] TABLE_AS_BYTES = "TABLE".getBytes(); + + protected static final byte[] SYSTEM_TABLE_AS_BYTES = "SYSTEM TABLE".getBytes(); + + private static final int UPDATE_RULE = 9; + + protected static final byte[] VIEW_AS_BYTES = "VIEW".getBytes(); + + // MySQL reserved words (all versions superset) + private static final String[] MYSQL_KEYWORDS = new String[] { "ACCESSIBLE", "ADD", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC", "ASENSITIVE", "BEFORE", + "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOTH", "BY", "CALL", "CASCADE", "CASE", "CHANGE", "CHAR", "CHARACTER", "CHECK", "COLLATE", "COLUMN", + "CONDITION", "CONSTRAINT", "CONTINUE", "CONVERT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", + "DATABASE", "DATABASES", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", "DAY_SECOND", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DELAYED", "DELETE", + "DESC", "DESCRIBE", "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DOUBLE", "DROP", "DUAL", "EACH", "ELSE", "ELSEIF", "ENCLOSED", "ESCAPED", + "EXISTS", "EXIT", "EXPLAIN", "FALSE", "FETCH", "FLOAT", "FLOAT4", "FLOAT8", "FOR", "FORCE", "FOREIGN", "FROM", "FULLTEXT", "GENERATED", "GET", + "GRANT", "GROUP", "HAVING", "HIGH_PRIORITY", "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IF", "IGNORE", "IN", "INDEX", "INFILE", "INNER", + "INOUT", "INSENSITIVE", "INSERT", "INT", "INT1", "INT2", "INT3", "INT4", "INT8", "INTEGER", "INTERVAL", "INTO", "IO_AFTER_GTIDS", "IO_BEFORE_GTIDS", + "IS", "ITERATE", "JOIN", "KEY", "KEYS", "KILL", "LEADING", "LEAVE", "LEFT", "LIKE", "LIMIT", "LINEAR", "LINES", "LOAD", "LOCALTIME", + "LOCALTIMESTAMP", "LOCK", "LONG", "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", "MASTER_BIND", "MASTER_SSL_VERIFY_SERVER_CERT", "MATCH", + "MAXVALUE", "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", "MIDDLEINT", "MINUTE_MICROSECOND", "MINUTE_SECOND", "MOD", "MODIFIES", "NATURAL", "NOT", + "NO_WRITE_TO_BINLOG", "NULL", "NUMERIC", "ON", "OPTIMIZE", "OPTIMIZER_COSTS", "OPTION", "OPTIONALLY", "OR", "ORDER", "OUT", "OUTER", "OUTFILE", + "PARTITION", "PRECISION", "PRIMARY", "PROCEDURE", "PURGE", "RANGE", "READ", "READS", "READ_WRITE", "REAL", "REFERENCES", "REGEXP", "RELEASE", + "RENAME", "REPEAT", "REPLACE", "REQUIRE", "RESIGNAL", "RESTRICT", "RETURN", "REVOKE", "RIGHT", "RLIKE", "SCHEMA", "SCHEMAS", "SECOND_MICROSECOND", + "SELECT", "SENSITIVE", "SEPARATOR", "SET", "SHOW", "SIGNAL", "SMALLINT", "SPATIAL", "SPECIFIC", "SQL", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", + "SQL_BIG_RESULT", "SQL_CALC_FOUND_ROWS", "SQL_SMALL_RESULT", "SSL", "STARTING", "STORED", "STRAIGHT_JOIN", "TABLE", "TERMINATED", "THEN", + "TINYBLOB", "TINYINT", "TINYTEXT", "TO", "TRAILING", "TRIGGER", "TRUE", "UNDO", "UNION", "UNIQUE", "UNLOCK", "UNSIGNED", "UPDATE", "USAGE", "USE", + "USING", "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP", "VALUES", "VARBINARY", "VARCHAR", "VARCHARACTER", "VARYING", "VIRTUAL", "WHEN", "WHERE", "WHILE", + "WITH", "WRITE", "XOR", "YEAR_MONTH", "ZEROFILL" }; + + // SQL:2003 reserved words from 'ISO/IEC 9075-2:2003 (E), 2003-07-25' + private static final String[] SQL2003_KEYWORDS = new String[] { "ABS", "ALL", "ALLOCATE", "ALTER", "AND", "ANY", "ARE", "ARRAY", "AS", "ASENSITIVE", + "ASYMMETRIC", "AT", "ATOMIC", "AUTHORIZATION", "AVG", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOOLEAN", "BOTH", "BY", "CALL", "CALLED", + "CARDINALITY", "CASCADED", "CASE", "CAST", "CEIL", "CEILING", "CHAR", "CHARACTER", "CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOB", "CLOSE", + "COALESCE", "COLLATE", "COLLECT", "COLUMN", "COMMIT", "CONDITION", "CONNECT", "CONSTRAINT", "CONVERT", "CORR", "CORRESPONDING", "COUNT", + "COVAR_POP", "COVAR_SAMP", "CREATE", "CROSS", "CUBE", "CUME_DIST", "CURRENT", "CURRENT_DATE", "CURRENT_DEFAULT_TRANSFORM_GROUP", "CURRENT_PATH", + "CURRENT_ROLE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_TRANSFORM_GROUP_FOR_TYPE", "CURRENT_USER", "CURSOR", "CYCLE", "DATE", "DAY", + "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DELETE", "DENSE_RANK", "DEREF", "DESCRIBE", "DETERMINISTIC", "DISCONNECT", "DISTINCT", + "DOUBLE", "DROP", "DYNAMIC", "EACH", "ELEMENT", "ELSE", "END", "END-EXEC", "ESCAPE", "EVERY", "EXCEPT", "EXEC", "EXECUTE", "EXISTS", "EXP", + "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FILTER", "FLOAT", "FLOOR", "FOR", "FOREIGN", "FREE", "FROM", "FULL", "FUNCTION", "FUSION", "GET", + "GLOBAL", "GRANT", "GROUP", "GROUPING", "HAVING", "HOLD", "HOUR", "IDENTITY", "IN", "INDICATOR", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INT", + "INTEGER", "INTERSECT", "INTERSECTION", "INTERVAL", "INTO", "IS", "JOIN", "LANGUAGE", "LARGE", "LATERAL", "LEADING", "LEFT", "LIKE", "LN", "LOCAL", + "LOCALTIME", "LOCALTIMESTAMP", "LOWER", "MATCH", "MAX", "MEMBER", "MERGE", "METHOD", "MIN", "MINUTE", "MOD", "MODIFIES", "MODULE", "MONTH", + "MULTISET", "NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NEW", "NO", "NONE", "NORMALIZE", "NOT", "NULL", "NULLIF", "NUMERIC", "OCTET_LENGTH", "OF", + "OLD", "ON", "ONLY", "OPEN", "OR", "ORDER", "OUT", "OUTER", "OVER", "OVERLAPS", "OVERLAY", "PARAMETER", "PARTITION", "PERCENTILE_CONT", + "PERCENTILE_DISC", "PERCENT_RANK", "POSITION", "POWER", "PRECISION", "PREPARE", "PRIMARY", "PROCEDURE", "RANGE", "RANK", "READS", "REAL", + "RECURSIVE", "REF", "REFERENCES", "REFERENCING", "REGR_AVGX", "REGR_AVGY", "REGR_COUNT", "REGR_INTERCEPT", "REGR_R2", "REGR_SLOPE", "REGR_SXX", + "REGR_SXY", "REGR_SYY", "RELEASE", "RESULT", "RETURN", "RETURNS", "REVOKE", "RIGHT", "ROLLBACK", "ROLLUP", "ROW", "ROWS", "ROW_NUMBER", "SAVEPOINT", + "SCOPE", "SCROLL", "SEARCH", "SECOND", "SELECT", "SENSITIVE", "SESSION_USER", "SET", "SIMILAR", "SMALLINT", "SOME", "SPECIFIC", "SPECIFICTYPE", + "SQL", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", "SQRT", "START", "STATIC", "STDDEV_POP", "STDDEV_SAMP", "SUBMULTISET", "SUBSTRING", "SUM", + "SYMMETRIC", "SYSTEM", "SYSTEM_USER", "TABLE", "TABLESAMPLE", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", + "TRANSLATE", "TRANSLATION", "TREAT", "TRIGGER", "TRIM", "TRUE", "UESCAPE", "UNION", "UNIQUE", "UNKNOWN", "UNNEST", "UPDATE", "UPPER", "USER", + "USING", "VALUE", "VALUES", "VARCHAR", "VARYING", "VAR_POP", "VAR_SAMP", "WHEN", "WHENEVER", "WHERE", "WIDTH_BUCKET", "WINDOW", "WITH", "WITHIN", + "WITHOUT", "YEAR" }; + + private static volatile String mysqlKeywords = null; + + /** The connection to the database */ + protected JdbcConnection conn; + + protected NativeSession session; + + /** The 'current' database name being used */ + protected String database = null; + + /** What character to use when quoting identifiers */ + protected final String quotedId; + + protected boolean nullCatalogMeansCurrent; + protected boolean pedantic; + protected boolean tinyInt1isBit; + protected boolean transformedBitIsBoolean; + protected boolean useHostsInPrivileges; + + protected ResultSetFactory resultSetFactory; + + private String metadataEncoding; + private int metadataCollationIndex; + + protected static DatabaseMetaData getInstance(JdbcConnection connToSet, String databaseToSet, boolean checkForInfoSchema, ResultSetFactory resultSetFactory) + throws SQLException { + if (checkForInfoSchema && connToSet.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useInformationSchema).getValue()) { + return new DatabaseMetaDataUsingInfoSchema(connToSet, databaseToSet, resultSetFactory); + } + + return new DatabaseMetaData(connToSet, databaseToSet, resultSetFactory); + } + + /** + * Creates a new DatabaseMetaData object. + * + * @param connToSet + * @param databaseToSet + */ + protected DatabaseMetaData(JdbcConnection connToSet, String databaseToSet, ResultSetFactory resultSetFactory) { + this.conn = connToSet; + this.session = (NativeSession) connToSet.getSession(); + this.database = databaseToSet; + this.resultSetFactory = resultSetFactory; + this.exceptionInterceptor = this.conn.getExceptionInterceptor(); + this.nullCatalogMeansCurrent = this.conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_nullCatalogMeansCurrent).getValue(); + this.pedantic = this.conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_pedantic).getValue(); + this.tinyInt1isBit = this.conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_tinyInt1isBit).getValue(); + this.transformedBitIsBoolean = this.conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_transformedBitIsBoolean).getValue(); + this.useHostsInPrivileges = this.conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useHostsInPrivileges).getValue(); + this.quotedId = this.session.getIdentifierQuoteString(); + } + + /** + * Can all the procedures returned by getProcedures be called by the current + * user? + * + * @return true if so + * @throws SQLException + */ + public boolean allProceduresAreCallable() throws SQLException { + return false; + } + + /** + * Can all the tables returned by getTable be SELECTed by the current user? + * + * @return true if so + * @throws SQLException + */ + public boolean allTablesAreSelectable() throws SQLException { + return false; + } + + protected void convertToJdbcFunctionList(String catalog, ResultSet proceduresRs, boolean needsClientFiltering, String db, + List> procedureRows, int nameIndex, Field[] fields) throws SQLException { + while (proceduresRs.next()) { + boolean shouldAdd = true; + + if (needsClientFiltering) { + shouldAdd = false; + + String procDb = proceduresRs.getString(1); + + if (db == null && procDb == null) { + shouldAdd = true; + } else if (db != null && db.equals(procDb)) { + shouldAdd = true; + } + } + + if (shouldAdd) { + String functionName = proceduresRs.getString(nameIndex); + + byte[][] rowData = null; + + if (fields != null && fields.length == 9) { + + rowData = new byte[9][]; + rowData[0] = catalog == null ? null : s2b(catalog); // PROCEDURE_CAT + rowData[1] = null; // PROCEDURE_SCHEM + rowData[2] = s2b(functionName); // PROCEDURE_NAME + rowData[3] = null; // reserved1 + rowData[4] = null; // reserved2 + rowData[5] = null; // reserved3 + rowData[6] = s2b(proceduresRs.getString("comment")); // REMARKS + rowData[7] = s2b(Integer.toString(procedureReturnsResult)); // PROCEDURE_TYPE + rowData[8] = s2b(functionName); + } else { + + rowData = new byte[6][]; + + rowData[0] = catalog == null ? null : s2b(catalog); // FUNCTION_CAT + rowData[1] = null; // FUNCTION_SCHEM + rowData[2] = s2b(functionName); // FUNCTION_NAME + rowData[3] = s2b(proceduresRs.getString("comment")); // REMARKS + rowData[4] = s2b(Integer.toString(getFunctionNoTableConstant())); // FUNCTION_TYPE + rowData[5] = s2b(functionName); // SPECFIC NAME + } + + procedureRows.add( + new ComparableWrapper(getFullyQualifiedName(catalog, functionName), new ByteArrayRow(rowData, getExceptionInterceptor()))); + } + } + } + + /** + * Builds and returns a fully qualified name, quoted if necessary, for the given catalog and database entity. + */ + protected String getFullyQualifiedName(String catalog, String entity) { + StringBuilder fullyQualifiedName = new StringBuilder(StringUtils.quoteIdentifier(catalog == null ? "" : catalog, this.quotedId, this.pedantic)); + fullyQualifiedName.append('.'); + fullyQualifiedName.append(StringUtils.quoteIdentifier(entity, this.quotedId, this.pedantic)); + return fullyQualifiedName.toString(); + } + + /** + * Getter to DatabaseMetaData.functionNoTable constant. + * + * @return java.sql.DatabaseMetaData#functionNoTable + */ + protected int getFunctionNoTableConstant() { + return functionNoTable; + } + + protected void convertToJdbcProcedureList(boolean fromSelect, String catalog, ResultSet proceduresRs, boolean needsClientFiltering, String db, + List> procedureRows, int nameIndex) throws SQLException { + while (proceduresRs.next()) { + boolean shouldAdd = true; + + if (needsClientFiltering) { + shouldAdd = false; + + String procDb = proceduresRs.getString(1); + + if (db == null && procDb == null) { + shouldAdd = true; + } else if (db != null && db.equals(procDb)) { + shouldAdd = true; + } + } + + if (shouldAdd) { + String procedureName = proceduresRs.getString(nameIndex); + byte[][] rowData = new byte[9][]; + rowData[0] = catalog == null ? null : s2b(catalog); + rowData[1] = null; + rowData[2] = s2b(procedureName); + rowData[3] = null; + rowData[4] = null; + rowData[5] = null; + rowData[6] = s2b(proceduresRs.getString("comment")); + + boolean isFunction = fromSelect ? "FUNCTION".equalsIgnoreCase(proceduresRs.getString("type")) : false; + rowData[7] = s2b(isFunction ? Integer.toString(procedureReturnsResult) : Integer.toString(procedureNoResult)); + + rowData[8] = s2b(procedureName); + + procedureRows.add(new ComparableWrapper(getFullyQualifiedName(catalog, procedureName), + new ByteArrayRow(rowData, getExceptionInterceptor()))); + } + } + } + + private Row convertTypeDescriptorToProcedureRow(byte[] procNameAsBytes, byte[] procCatAsBytes, String paramName, boolean isOutParam, boolean isInParam, + boolean isReturnParam, TypeDescriptor typeDesc, boolean forGetFunctionColumns, int ordinal) throws SQLException { + byte[][] row = forGetFunctionColumns ? new byte[17][] : new byte[20][]; + row[0] = procCatAsBytes; // PROCEDURE_CAT + row[1] = null; // PROCEDURE_SCHEM + row[2] = procNameAsBytes; // PROCEDURE/NAME + row[3] = s2b(paramName); // COLUMN_NAME + row[4] = s2b(String.valueOf(getColumnType(isOutParam, isInParam, isReturnParam, forGetFunctionColumns))); // COLUMN_TYPE + row[5] = s2b(Short.toString((short) typeDesc.mysqlType.getJdbcType())); // DATA_TYPE + row[6] = s2b(typeDesc.mysqlType.getName()); // TYPE_NAME + row[7] = typeDesc.columnSize == null ? null : s2b(typeDesc.columnSize.toString()); // PRECISION + row[8] = row[7]; // LENGTH + row[9] = typeDesc.decimalDigits == null ? null : s2b(typeDesc.decimalDigits.toString()); // SCALE + row[10] = s2b(Integer.toString(typeDesc.numPrecRadix)); // RADIX + // Map 'column****' to 'procedure****' + switch (typeDesc.nullability) { + case columnNoNulls: + row[11] = s2b(String.valueOf(procedureNoNulls)); // NULLABLE + break; + + case columnNullable: + row[11] = s2b(String.valueOf(procedureNullable)); // NULLABLE + break; + + case columnNullableUnknown: + row[11] = s2b(String.valueOf(procedureNullableUnknown)); // NULLABLE + break; + + default: + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.1"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + + row[12] = null; + + if (forGetFunctionColumns) { + row[13] = null; // CHAR_OCTECT_LENGTH + row[14] = s2b(String.valueOf(ordinal)); // ORDINAL_POSITION + row[15] = s2b(typeDesc.isNullable); // IS_NULLABLE + row[16] = procNameAsBytes; // SPECIFIC_NAME + + } else { + row[13] = null; // COLUMN_DEF + row[14] = null; // SQL_DATA_TYPE (future use) + row[15] = null; // SQL_DATETIME_SUB (future use) + row[16] = null; // CHAR_OCTET_LENGTH + row[17] = s2b(String.valueOf(ordinal)); // ORDINAL_POSITION + row[18] = s2b(typeDesc.isNullable); // IS_NULLABLE + row[19] = procNameAsBytes; // SPECIFIC_NAME + } + + return new ByteArrayRow(row, getExceptionInterceptor()); + } + + /** + * Determines the COLUMN_TYPE information based on parameter type (IN, OUT or INOUT) or function return parameter. + * + * @param isOutParam + * Indicates whether it's an output parameter. + * @param isInParam + * Indicates whether it's an input parameter. + * @param isReturnParam + * Indicates whether it's a function return parameter. + * @param forGetFunctionColumns + * Indicates whether the column belong to a function. This argument is required for JDBC4, in which case + * this method must be overridden to provide the correct functionality. + * + * @return The corresponding COLUMN_TYPE as in java.sql.getProcedureColumns API. + */ + protected int getColumnType(boolean isOutParam, boolean isInParam, boolean isReturnParam, boolean forGetFunctionColumns) { + return getProcedureOrFunctionColumnType(isOutParam, isInParam, isReturnParam, forGetFunctionColumns); + } + + /** + * Determines the COLUMN_TYPE information based on parameter type (IN, OUT or INOUT) or function return parameter. + * + * @param isOutParam + * Indicates whether it's an output parameter. + * @param isInParam + * Indicates whether it's an input parameter. + * @param isReturnParam + * Indicates whether it's a function return parameter. + * @param forGetFunctionColumns + * Indicates whether the column belong to a function. + * + * @return The corresponding COLUMN_TYPE as in java.sql.getProcedureColumns API. + */ + protected static int getProcedureOrFunctionColumnType(boolean isOutParam, boolean isInParam, boolean isReturnParam, boolean forGetFunctionColumns) { + + if (isInParam && isOutParam) { + return forGetFunctionColumns ? functionColumnInOut : procedureColumnInOut; + } else if (isInParam) { + return forGetFunctionColumns ? functionColumnIn : procedureColumnIn; + } else if (isOutParam) { + return forGetFunctionColumns ? functionColumnOut : procedureColumnOut; + } else if (isReturnParam) { + return forGetFunctionColumns ? functionReturn : procedureColumnReturn; + } else { + return forGetFunctionColumns ? functionColumnUnknown : procedureColumnUnknown; + } + } + + private ExceptionInterceptor exceptionInterceptor; + + protected ExceptionInterceptor getExceptionInterceptor() { + return this.exceptionInterceptor; + } + + /** + * Does a data definition statement within a transaction force the + * transaction to commit? + * + * @return true if so + * @throws SQLException + */ + public boolean dataDefinitionCausesTransactionCommit() throws SQLException { + return true; + } + + /** + * Is a data definition statement within a transaction ignored? + * + * @return true if so + * @throws SQLException + */ + public boolean dataDefinitionIgnoredInTransactions() throws SQLException { + return false; + } + + /** + * JDBC 2.0 Determine whether or not a visible row delete can be detected by + * calling ResultSet.rowDeleted(). If deletesAreDetected() returns false, + * then deleted rows are removed from the result set. + * + * @param type + * set type, i.e. ResultSet.TYPE_XXX + * @return true if changes are detected by the resultset type + * @exception SQLException + * if a database-access error occurs. + */ + public boolean deletesAreDetected(int type) throws SQLException { + return false; + } + + // ---------------------------------------------------------------------- + + /** + * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY blobs? + * + * @return true if so + * @throws SQLException + */ + public boolean doesMaxRowSizeIncludeBlobs() throws SQLException { + return true; + } + + /** + * Extracts foreign key info for one table. + * + * @param rows + * the list of rows to add to + * @param rs + * the result set from 'SHOW CREATE TABLE' + * @param catalog + * the database name + * @return the list of rows with new rows added + * @throws SQLException + * if a database access error occurs + */ + public List extractForeignKeyForTable(ArrayList rows, java.sql.ResultSet rs, String catalog) throws SQLException { + byte[][] row = new byte[3][]; + row[0] = rs.getBytes(1); + row[1] = s2b(SUPPORTS_FK); + + String createTableString = rs.getString(2); + StringTokenizer lineTokenizer = new StringTokenizer(createTableString, "\n"); + StringBuilder commentBuf = new StringBuilder("comment; "); + boolean firstTime = true; + + while (lineTokenizer.hasMoreTokens()) { + String line = lineTokenizer.nextToken().trim(); + + String constraintName = null; + + if (StringUtils.startsWithIgnoreCase(line, "CONSTRAINT")) { + boolean usingBackTicks = true; + int beginPos = StringUtils.indexOfQuoteDoubleAware(line, this.quotedId, 0); + + if (beginPos == -1) { + beginPos = line.indexOf("\""); + usingBackTicks = false; + } + + if (beginPos != -1) { + int endPos = -1; + + if (usingBackTicks) { + endPos = StringUtils.indexOfQuoteDoubleAware(line, this.quotedId, beginPos + 1); + } else { + endPos = StringUtils.indexOfQuoteDoubleAware(line, "\"", beginPos + 1); + } + + if (endPos != -1) { + constraintName = line.substring(beginPos + 1, endPos); + line = line.substring(endPos + 1, line.length()).trim(); + } + } + } + + if (line.startsWith("FOREIGN KEY")) { + if (line.endsWith(",")) { + line = line.substring(0, line.length() - 1); + } + + int indexOfFK = line.indexOf("FOREIGN KEY"); + + String localColumnName = null; + String referencedCatalogName = StringUtils.quoteIdentifier(catalog, this.quotedId, this.pedantic); + String referencedTableName = null; + String referencedColumnName = null; + + if (indexOfFK != -1) { + int afterFk = indexOfFK + "FOREIGN KEY".length(); + + int indexOfRef = StringUtils.indexOfIgnoreCase(afterFk, line, "REFERENCES", this.quotedId, this.quotedId, StringUtils.SEARCH_MODE__ALL); + + if (indexOfRef != -1) { + + int indexOfParenOpen = line.indexOf('(', afterFk); + int indexOfParenClose = StringUtils.indexOfIgnoreCase(indexOfParenOpen, line, ")", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__ALL); + + if (indexOfParenOpen == -1 || indexOfParenClose == -1) { + // throw SQLError.createSQLException(); + } + + localColumnName = line.substring(indexOfParenOpen + 1, indexOfParenClose); + + int afterRef = indexOfRef + "REFERENCES".length(); + + int referencedColumnBegin = StringUtils.indexOfIgnoreCase(afterRef, line, "(", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__ALL); + + if (referencedColumnBegin != -1) { + referencedTableName = line.substring(afterRef, referencedColumnBegin); + + int referencedColumnEnd = StringUtils.indexOfIgnoreCase(referencedColumnBegin + 1, line, ")", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__ALL); + + if (referencedColumnEnd != -1) { + referencedColumnName = line.substring(referencedColumnBegin + 1, referencedColumnEnd); + } + + int indexOfCatalogSep = StringUtils.indexOfIgnoreCase(0, referencedTableName, ".", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__ALL); + + if (indexOfCatalogSep != -1) { + referencedCatalogName = referencedTableName.substring(0, indexOfCatalogSep); + referencedTableName = referencedTableName.substring(indexOfCatalogSep + 1); + } + } + } + } + + if (!firstTime) { + commentBuf.append("; "); + } else { + firstTime = false; + } + + if (constraintName != null) { + commentBuf.append(constraintName); + } else { + commentBuf.append("not_available"); + } + + commentBuf.append("("); + commentBuf.append(localColumnName); + commentBuf.append(") REFER "); + commentBuf.append(referencedCatalogName); + commentBuf.append("/"); + commentBuf.append(referencedTableName); + commentBuf.append("("); + commentBuf.append(referencedColumnName); + commentBuf.append(")"); + + int lastParenIndex = line.lastIndexOf(")"); + + if (lastParenIndex != (line.length() - 1)) { + String cascadeOptions = line.substring(lastParenIndex + 1); + commentBuf.append(" "); + commentBuf.append(cascadeOptions); + } + } + } + + row[2] = s2b(commentBuf.toString()); + rows.add(new ByteArrayRow(row, getExceptionInterceptor())); + + return rows; + } + + /** + * Creates a result set similar enough to 'SHOW TABLE STATUS' to allow the + * same code to work on extracting the foreign key data + * + * @param connToUse + * the database connection to use + * @param metadata + * the DatabaseMetaData instance calling this method + * @param catalog + * the database name to extract foreign key info for + * @param tableName + * the table to extract foreign key info for + * @return A result set that has the structure of 'show table status' + * @throws SQLException + * if a database access error occurs. + */ + public ResultSet extractForeignKeyFromCreateTable(String catalog, String tableName) throws SQLException { + ArrayList tableList = new ArrayList<>(); + java.sql.ResultSet rs = null; + java.sql.Statement stmt = null; + + if (tableName != null) { + tableList.add(tableName); + } else { + try { + rs = getTables(catalog, null, null, new String[] { "TABLE" }); + + while (rs.next()) { + tableList.add(rs.getString("TABLE_NAME")); + } + } finally { + if (rs != null) { + rs.close(); + } + + rs = null; + } + } + + ArrayList rows = new ArrayList<>(); + Field[] fields = new Field[3]; + fields[0] = new Field("", "Name", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, Integer.MAX_VALUE); + fields[1] = new Field("", "Type", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[2] = new Field("", "Comment", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, Integer.MAX_VALUE); + + int numTables = tableList.size(); + stmt = this.conn.getMetadataSafeStatement(); + + try { + for (int i = 0; i < numTables; i++) { + String tableToExtract = tableList.get(i); + + String query = new StringBuilder("SHOW CREATE TABLE ").append(getFullyQualifiedName(catalog, tableToExtract)).toString(); + + try { + rs = stmt.executeQuery(query); + } catch (SQLException sqlEx) { + // Table might've disappeared on us, not really an error + String sqlState = sqlEx.getSQLState(); + + if (!"42S02".equals(sqlState) && sqlEx.getErrorCode() != MysqlErrorNumbers.ER_NO_SUCH_TABLE) { + throw sqlEx; + } + + continue; + } + + while (rs.next()) { + extractForeignKeyForTable(rows, rs, catalog); + } + } + } finally { + if (rs != null) { + rs.close(); + } + + rs = null; + + if (stmt != null) { + stmt.close(); + } + + stmt = null; + } + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(rows, new DefaultColumnDefinition(fields))); + } + + public java.sql.ResultSet getAttributes(String arg0, String arg1, String arg2, String arg3) throws SQLException { + Field[] fields = new Field[21]; + fields[0] = new Field("", "TYPE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[1] = new Field("", "TYPE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[2] = new Field("", "TYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[3] = new Field("", "ATTR_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[4] = new Field("", "DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 32); + fields[5] = new Field("", "ATTR_TYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[6] = new Field("", "ATTR_SIZE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 32); + fields[7] = new Field("", "DECIMAL_DIGITS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 32); + fields[8] = new Field("", "NUM_PREC_RADIX", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 32); + fields[9] = new Field("", "NULLABLE ", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 32); + fields[10] = new Field("", "REMARKS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[11] = new Field("", "ATTR_DEF", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[12] = new Field("", "SQL_DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 32); + fields[13] = new Field("", "SQL_DATETIME_SUB", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 32); + fields[14] = new Field("", "CHAR_OCTET_LENGTH", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 32); + fields[15] = new Field("", "ORDINAL_POSITION", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 32); + fields[16] = new Field("", "IS_NULLABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[17] = new Field("", "SCOPE_CATALOG", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[18] = new Field("", "SCOPE_SCHEMA", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[19] = new Field("", "SCOPE_TABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[20] = new Field("", "SOURCE_DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 32); + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(new ArrayList(), new DefaultColumnDefinition(fields))); + } + + public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, final String table, int scope, boolean nullable) throws SQLException { + if (table == null) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + Field[] fields = new Field[8]; + fields[0] = new Field("", "SCOPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 5); + fields[1] = new Field("", "COLUMN_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[2] = new Field("", "DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 32); + fields[3] = new Field("", "TYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[4] = new Field("", "COLUMN_SIZE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[5] = new Field("", "BUFFER_LENGTH", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[6] = new Field("", "DECIMAL_DIGITS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 10); + fields[7] = new Field("", "PSEUDO_COLUMN", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 5); + + final ArrayList rows = new ArrayList<>(); + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + ResultSet results = null; + + try { + StringBuilder queryBuf = new StringBuilder("SHOW COLUMNS FROM "); + queryBuf.append(StringUtils.quoteIdentifier(table, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + queryBuf.append(" FROM "); + queryBuf.append(StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + + results = stmt.executeQuery(queryBuf.toString()); + + while (results.next()) { + String keyType = results.getString("Key"); + + if (keyType != null) { + if (StringUtils.startsWithIgnoreCase(keyType, "PRI")) { + byte[][] rowVal = new byte[8][]; + rowVal[0] = Integer.toString(java.sql.DatabaseMetaData.bestRowSession).getBytes(); + rowVal[1] = results.getBytes("Field"); + + String type = results.getString("Type"); + int size = stmt.getMaxFieldSize(); + int decimals = 0; + + /* + * Parse the Type column from MySQL + */ + if (type.indexOf("enum") != -1) { + String temp = type.substring(type.indexOf("("), type.indexOf(")")); + java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(temp, ","); + int maxLength = 0; + + while (tokenizer.hasMoreTokens()) { + maxLength = Math.max(maxLength, (tokenizer.nextToken().length() - 2)); + } + + size = maxLength; + decimals = 0; + type = "enum"; + } else if (type.indexOf("(") != -1) { + if (type.indexOf(",") != -1) { + size = Integer.parseInt(type.substring(type.indexOf("(") + 1, type.indexOf(","))); + decimals = Integer.parseInt(type.substring(type.indexOf(",") + 1, type.indexOf(")"))); + } else { + size = Integer.parseInt(type.substring(type.indexOf("(") + 1, type.indexOf(")"))); + } + + type = type.substring(0, type.indexOf("(")); + } + + MysqlType ft = MysqlType.getByName(type.toUpperCase()); + rowVal[2] = s2b(String.valueOf(ft.getJdbcType())); + rowVal[3] = s2b(type); + rowVal[4] = Integer.toString(size + decimals).getBytes(); + rowVal[5] = Integer.toString(size + decimals).getBytes(); + rowVal[6] = Integer.toString(decimals).getBytes(); + rowVal[7] = Integer.toString(java.sql.DatabaseMetaData.bestRowNotPseudo).getBytes(); + + rows.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + } + } + } + } catch (SQLException sqlEx) { + if (!MysqlErrorNumbers.SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND.equals(sqlEx.getSQLState())) { + throw sqlEx; + } + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + java.sql.ResultSet results = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(rows, new DefaultColumnDefinition(fields))); + + return results; + + } + + /* + * Extract parameter details for Procedures and Functions by parsing the DDL query obtained from SHOW CREATE [PROCEDURE|FUNCTION] ... statements. + * The result rows returned follow the required structure for getProcedureColumns() and getFunctionColumns() methods. + * + * Internal use only. + */ + private void getCallStmtParameterTypes(String catalog, String quotedProcName, ProcedureType procType, String parameterNamePattern, List resultRows, + boolean forGetFunctionColumns) throws SQLException { + java.sql.Statement paramRetrievalStmt = null; + java.sql.ResultSet paramRetrievalRs = null; + + String parameterDef = null; + + byte[] procNameAsBytes = null; + byte[] procCatAsBytes = null; + + boolean isProcedureInAnsiMode = false; + String storageDefnDelims = null; + String storageDefnClosures = null; + + try { + paramRetrievalStmt = this.conn.getMetadataSafeStatement(); + + String oldCatalog = this.conn.getCatalog(); + if (this.conn.lowerCaseTableNames() && catalog != null && catalog.length() != 0 && oldCatalog != null && oldCatalog.length() != 0) { + // Workaround for bug in server wrt. to SHOW CREATE PROCEDURE not respecting lower-case table names + + ResultSet rs = null; + + try { + this.conn.setCatalog(StringUtils.unQuoteIdentifier(catalog, this.quotedId)); + rs = paramRetrievalStmt.executeQuery("SELECT DATABASE()"); + rs.next(); + + catalog = rs.getString(1); + + } finally { + + this.conn.setCatalog(oldCatalog); + + if (rs != null) { + rs.close(); + } + } + } + + if (paramRetrievalStmt.getMaxRows() != 0) { + paramRetrievalStmt.setMaxRows(0); + } + + int dotIndex = -1; + + if (!" ".equals(this.quotedId)) { + dotIndex = StringUtils.indexOfIgnoreCase(0, quotedProcName, ".", this.quotedId, this.quotedId, + this.session.getServerSession().isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + } else { + dotIndex = quotedProcName.indexOf("."); + } + + String dbName = null; + + if (dotIndex != -1 && (dotIndex + 1) < quotedProcName.length()) { + dbName = quotedProcName.substring(0, dotIndex); + quotedProcName = quotedProcName.substring(dotIndex + 1); + } else { + dbName = StringUtils.quoteIdentifier(catalog, this.quotedId, this.pedantic); + } + + // Moved from above so that procName is *without* database as expected by the rest of code + // Removing QuoteChar to get output as it was before PROC_CAT fixes + String tmpProcName = StringUtils.unQuoteIdentifier(quotedProcName, this.quotedId); + procNameAsBytes = StringUtils.getBytes(tmpProcName, "UTF-8"); + + tmpProcName = StringUtils.unQuoteIdentifier(dbName, this.quotedId); + procCatAsBytes = StringUtils.getBytes(tmpProcName, "UTF-8"); + + // there is no need to quote the identifier here since 'dbName' and 'procName' are guaranteed to be already quoted. + StringBuilder procNameBuf = new StringBuilder(); + procNameBuf.append(dbName); + procNameBuf.append('.'); + procNameBuf.append(quotedProcName); + + String fieldName = null; + if (procType == PROCEDURE) { + paramRetrievalRs = paramRetrievalStmt.executeQuery("SHOW CREATE PROCEDURE " + procNameBuf.toString()); + fieldName = "Create Procedure"; + } else { + paramRetrievalRs = paramRetrievalStmt.executeQuery("SHOW CREATE FUNCTION " + procNameBuf.toString()); + fieldName = "Create Function"; + } + + if (paramRetrievalRs.next()) { + String procedureDef = paramRetrievalRs.getString(fieldName); + + if (!this.conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_noAccessToProcedureBodies).getValue() + && (procedureDef == null || procedureDef.length() == 0)) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.4"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + + try { + String sqlMode = paramRetrievalRs.getString("sql_mode"); + + if (StringUtils.indexOfIgnoreCase(sqlMode, "ANSI") != -1) { + isProcedureInAnsiMode = true; + } + } catch (SQLException sqlEx) { + // doesn't exist + } + + String identifierMarkers = isProcedureInAnsiMode ? "`\"" : "`"; + String identifierAndStringMarkers = "'" + identifierMarkers; + storageDefnDelims = "(" + identifierMarkers; + storageDefnClosures = ")" + identifierMarkers; + + if (procedureDef != null && procedureDef.length() != 0) { + // sanitize/normalize by stripping out comments + procedureDef = StringUtils.stripComments(procedureDef, identifierAndStringMarkers, identifierAndStringMarkers, true, false, true, true); + + int openParenIndex = StringUtils.indexOfIgnoreCase(0, procedureDef, "(", this.quotedId, this.quotedId, + this.session.getServerSession().isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + int endOfParamDeclarationIndex = 0; + + endOfParamDeclarationIndex = endPositionOfParameterDeclaration(openParenIndex, procedureDef, this.quotedId); + + if (procType == FUNCTION) { + + // Grab the return column since it needs + // to go first in the output result set + int returnsIndex = StringUtils.indexOfIgnoreCase(0, procedureDef, " RETURNS ", this.quotedId, this.quotedId, + this.session.getServerSession().isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + + int endReturnsDef = findEndOfReturnsClause(procedureDef, returnsIndex); + + // Trim off whitespace after "RETURNS" + + int declarationStart = returnsIndex + "RETURNS ".length(); + + while (declarationStart < procedureDef.length()) { + if (Character.isWhitespace(procedureDef.charAt(declarationStart))) { + declarationStart++; + } else { + break; + } + } + + String returnsDefn = procedureDef.substring(declarationStart, endReturnsDef).trim(); + TypeDescriptor returnDescriptor = new TypeDescriptor(returnsDefn, "YES"); + + resultRows.add(convertTypeDescriptorToProcedureRow(procNameAsBytes, procCatAsBytes, "", false, false, true, returnDescriptor, + forGetFunctionColumns, 0)); + } + + if ((openParenIndex == -1) || (endOfParamDeclarationIndex == -1)) { + // parse error? + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.5"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + + parameterDef = procedureDef.substring(openParenIndex + 1, endOfParamDeclarationIndex); + } + + } + } finally { + SQLException sqlExRethrow = null; + + if (paramRetrievalRs != null) { + try { + paramRetrievalRs.close(); + } catch (SQLException sqlEx) { + sqlExRethrow = sqlEx; + } + + paramRetrievalRs = null; + } + + if (paramRetrievalStmt != null) { + try { + paramRetrievalStmt.close(); + } catch (SQLException sqlEx) { + sqlExRethrow = sqlEx; + } + + paramRetrievalStmt = null; + } + + if (sqlExRethrow != null) { + throw sqlExRethrow; + } + } + + if (parameterDef != null) { + int ordinal = 1; + + List parseList = StringUtils.split(parameterDef, ",", storageDefnDelims, storageDefnClosures, true); + + int parseListLen = parseList.size(); + + for (int i = 0; i < parseListLen; i++) { + String declaration = parseList.get(i); + + if (declaration.trim().length() == 0) { + break; // no parameters actually declared, but whitespace spans lines + } + + // Bug#52167, tokenizer will break if declaration contains special characters like \n + declaration = declaration.replaceAll("[\\t\\n\\x0B\\f\\r]", " "); + StringTokenizer declarationTok = new StringTokenizer(declaration, " \t"); + + String paramName = null; + boolean isOutParam = false; + boolean isInParam = false; + + if (declarationTok.hasMoreTokens()) { + String possibleParamName = declarationTok.nextToken(); + + if (possibleParamName.equalsIgnoreCase("OUT")) { + isOutParam = true; + + if (declarationTok.hasMoreTokens()) { + paramName = declarationTok.nextToken(); + } else { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.6"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + } else if (possibleParamName.equalsIgnoreCase("INOUT")) { + isOutParam = true; + isInParam = true; + + if (declarationTok.hasMoreTokens()) { + paramName = declarationTok.nextToken(); + } else { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.6"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + } else if (possibleParamName.equalsIgnoreCase("IN")) { + isOutParam = false; + isInParam = true; + + if (declarationTok.hasMoreTokens()) { + paramName = declarationTok.nextToken(); + } else { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.6"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + } else { + isOutParam = false; + isInParam = true; + + paramName = possibleParamName; + } + + TypeDescriptor typeDesc = null; + + if (declarationTok.hasMoreTokens()) { + StringBuilder typeInfoBuf = new StringBuilder(declarationTok.nextToken()); + + while (declarationTok.hasMoreTokens()) { + typeInfoBuf.append(" "); + typeInfoBuf.append(declarationTok.nextToken()); + } + + String typeInfo = typeInfoBuf.toString(); + + typeDesc = new TypeDescriptor(typeInfo, "YES"); + } else { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.7"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + + if ((paramName.startsWith("`") && paramName.endsWith("`")) + || (isProcedureInAnsiMode && paramName.startsWith("\"") && paramName.endsWith("\""))) { + paramName = paramName.substring(1, paramName.length() - 1); + } + + if (parameterNamePattern == null || StringUtils.wildCompareIgnoreCase(paramName, parameterNamePattern)) { + Row row = convertTypeDescriptorToProcedureRow(procNameAsBytes, procCatAsBytes, paramName, isOutParam, isInParam, false, typeDesc, + forGetFunctionColumns, ordinal++); + + resultRows.add(row); + } + } else { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.8"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + } + } else { + // Is this an error? JDBC spec doesn't make it clear if stored procedure doesn't exist, is it an error.... + } + } + + /** + * Finds the end of the parameter declaration from the output of "SHOW + * CREATE PROCEDURE". + * + * @param beginIndex + * should be the index of the procedure body that contains the + * first "(". + * @param procedureDef + * the procedure body + * @param quoteChar + * the identifier quote character in use + * @return the ending index of the parameter declaration, not including the + * closing ")" + * @throws SQLException + * if a parse error occurs. + */ + private int endPositionOfParameterDeclaration(int beginIndex, String procedureDef, String quoteChar) throws SQLException { + int currentPos = beginIndex + 1; + int parenDepth = 1; // counting the first openParen + + while (parenDepth > 0 && currentPos < procedureDef.length()) { + int closedParenIndex = StringUtils.indexOfIgnoreCase(currentPos, procedureDef, ")", quoteChar, quoteChar, + this.session.getServerSession().isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + + if (closedParenIndex != -1) { + int nextOpenParenIndex = StringUtils.indexOfIgnoreCase(currentPos, procedureDef, "(", quoteChar, quoteChar, + this.session.getServerSession().isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + + if (nextOpenParenIndex != -1 && nextOpenParenIndex < closedParenIndex) { + parenDepth++; + currentPos = closedParenIndex + 1; // set after closed paren that increases depth + } else { + parenDepth--; + currentPos = closedParenIndex; // start search from same position + } + } else { + // we should always get closed paren of some sort + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.5"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + } + + return currentPos; + } + + /** + * Finds the end of the RETURNS clause for SQL Functions by using any of the + * keywords allowed after the RETURNS clause, or a label. + * + * @param procedureDefn + * the function body containing the definition of the function + * @param quoteChar + * the identifier quote string in use + * @param positionOfReturnKeyword + * the position of "RETURNS" in the definition + * @return the end of the returns clause + * @throws SQLException + * if a parse error occurs + */ + private int findEndOfReturnsClause(String procedureDefn, int positionOfReturnKeyword) throws SQLException { + /* + * characteristic: LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL | + * NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { + * DEFINER | INVOKER } | COMMENT 'string' + */ + String openingMarkers = this.quotedId + "("; + String closingMarkers = this.quotedId + ")"; + + String[] tokens = new String[] { "LANGUAGE", "NOT", "DETERMINISTIC", "CONTAINS", "NO", "READ", "MODIFIES", "SQL", "COMMENT", "BEGIN", "RETURN" }; + + int startLookingAt = positionOfReturnKeyword + "RETURNS".length() + 1; + + int endOfReturn = -1; + + for (int i = 0; i < tokens.length; i++) { + int nextEndOfReturn = StringUtils.indexOfIgnoreCase(startLookingAt, procedureDefn, tokens[i], openingMarkers, closingMarkers, + this.session.getServerSession().isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + + if (nextEndOfReturn != -1) { + if (endOfReturn == -1 || (nextEndOfReturn < endOfReturn)) { + endOfReturn = nextEndOfReturn; + } + } + } + + if (endOfReturn != -1) { + return endOfReturn; + } + + // Label? + endOfReturn = StringUtils.indexOfIgnoreCase(startLookingAt, procedureDefn, ":", openingMarkers, closingMarkers, + this.session.getServerSession().isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + + if (endOfReturn != -1) { + // seek back until whitespace + for (int i = endOfReturn; i > 0; i--) { + if (Character.isWhitespace(procedureDefn.charAt(i))) { + return i; + } + } + } + + // We can't parse it. + + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.5"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + /** + * Parses the cascade option string and returns the DBMD constant that + * represents it (for deletes) + * + * @param cascadeOptions + * the comment from 'SHOW TABLE STATUS' + * @return the DBMD constant that represents the cascade option + */ + private int getCascadeDeleteOption(String cascadeOptions) { + int onDeletePos = cascadeOptions.indexOf("ON DELETE"); + + if (onDeletePos != -1) { + String deleteOptions = cascadeOptions.substring(onDeletePos, cascadeOptions.length()); + + if (deleteOptions.startsWith("ON DELETE CASCADE")) { + return java.sql.DatabaseMetaData.importedKeyCascade; + } else if (deleteOptions.startsWith("ON DELETE SET NULL")) { + return java.sql.DatabaseMetaData.importedKeySetNull; + } else if (deleteOptions.startsWith("ON DELETE RESTRICT")) { + return java.sql.DatabaseMetaData.importedKeyRestrict; + } else if (deleteOptions.startsWith("ON DELETE NO ACTION")) { + return java.sql.DatabaseMetaData.importedKeyNoAction; + } + } + + return java.sql.DatabaseMetaData.importedKeyNoAction; + } + + /** + * Parses the cascade option string and returns the DBMD constant that + * represents it (for Updates) + * + * @param cascadeOptions + * the comment from 'SHOW TABLE STATUS' + * @return the DBMD constant that represents the cascade option + */ + private int getCascadeUpdateOption(String cascadeOptions) { + int onUpdatePos = cascadeOptions.indexOf("ON UPDATE"); + + if (onUpdatePos != -1) { + String updateOptions = cascadeOptions.substring(onUpdatePos, cascadeOptions.length()); + + if (updateOptions.startsWith("ON UPDATE CASCADE")) { + return java.sql.DatabaseMetaData.importedKeyCascade; + } else if (updateOptions.startsWith("ON UPDATE SET NULL")) { + return java.sql.DatabaseMetaData.importedKeySetNull; + } else if (updateOptions.startsWith("ON UPDATE RESTRICT")) { + return java.sql.DatabaseMetaData.importedKeyRestrict; + } else if (updateOptions.startsWith("ON UPDATE NO ACTION")) { + return java.sql.DatabaseMetaData.importedKeyNoAction; + } + } + + return java.sql.DatabaseMetaData.importedKeyNoAction; + } + + protected IteratorWithCleanup getCatalogIterator(String catalogSpec) throws SQLException { + IteratorWithCleanup allCatalogsIter; + if (catalogSpec != null) { + allCatalogsIter = new SingleStringIterator(this.pedantic ? catalogSpec : StringUtils.unQuoteIdentifier(catalogSpec, this.quotedId)); + } else if (this.nullCatalogMeansCurrent) { + allCatalogsIter = new SingleStringIterator(this.database); + } else { + allCatalogsIter = new ResultSetIterator(getCatalogs(), 1); + } + + return allCatalogsIter; + } + + public java.sql.ResultSet getCatalogs() throws SQLException { + java.sql.ResultSet results = null; + java.sql.Statement stmt = null; + + try { + stmt = this.conn.getMetadataSafeStatement(); + results = stmt.executeQuery("SHOW DATABASES"); + + int catalogsCount = 0; + if (results.last()) { + catalogsCount = results.getRow(); + results.beforeFirst(); + } + + List resultsAsList = new ArrayList<>(catalogsCount); + while (results.next()) { + resultsAsList.add(results.getString(1)); + } + Collections.sort(resultsAsList); + + Field[] fields = new Field[1]; + fields[0] = new Field("", "TABLE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, + results.getMetaData().getColumnDisplaySize(1)); + + ArrayList tuples = new ArrayList<>(catalogsCount); + for (String cat : resultsAsList) { + byte[][] rowVal = new byte[1][]; + rowVal[0] = s2b(cat); + tuples.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + } + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(tuples, new DefaultColumnDefinition(fields))); + } finally { + if (results != null) { + try { + results.close(); + } catch (SQLException sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + results = null; + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + stmt = null; + } + } + } + + /** + * What's the separator between catalog and table name? + * + * @return the separator string + * @throws SQLException + */ + public String getCatalogSeparator() throws SQLException { + return "."; + } + + // The following group of methods exposes various limitations based on the target database with the current driver. Unless otherwise specified, a result of + // zero means there is no limit, or the limit is not known. + + /** + * What's the database vendor's preferred term for "catalog"? + * + * @return the vendor term + * @throws SQLException + */ + public String getCatalogTerm() throws SQLException { + return "database"; + } + + public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException { + Field[] fields = new Field[8]; + fields[0] = new Field("", "TABLE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 64); + fields[1] = new Field("", "TABLE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 1); + fields[2] = new Field("", "TABLE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 64); + fields[3] = new Field("", "COLUMN_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 64); + fields[4] = new Field("", "GRANTOR", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 77); + fields[5] = new Field("", "GRANTEE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 77); + fields[6] = new Field("", "PRIVILEGE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 64); + fields[7] = new Field("", "IS_GRANTABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 3); + + StringBuilder grantQueryBuf = new StringBuilder("SELECT c.host, c.db, t.grantor, c.user, c.table_name, c.column_name, c.column_priv"); + grantQueryBuf.append(" FROM mysql.columns_priv c, mysql.tables_priv t"); + grantQueryBuf.append(" WHERE c.host = t.host AND c.db = t.db AND c.table_name = t.table_name"); + if (catalog != null) { + grantQueryBuf.append(" AND c.db LIKE ?"); + } + grantQueryBuf.append(" AND c.table_name = ?"); + if (columnNamePattern != null) { + grantQueryBuf.append(" AND c.column_name LIKE ?"); + } + + PreparedStatement pStmt = null; + ResultSet results = null; + ArrayList grantRows = new ArrayList<>(); + + try { + pStmt = prepareMetaDataSafeStatement(grantQueryBuf.toString()); + int nextId = 1; + if (catalog != null) { + pStmt.setString(nextId++, catalog); + } + pStmt.setString(nextId++, table); + if (columnNamePattern != null) { + pStmt.setString(nextId, columnNamePattern); + } + + results = pStmt.executeQuery(); + + while (results.next()) { + String host = results.getString(1); + String db = results.getString(2); + String grantor = results.getString(3); + String user = results.getString(4); + + if ((user == null) || (user.length() == 0)) { + user = "%"; + } + + StringBuilder fullUser = new StringBuilder(user); + + if ((host != null) && this.useHostsInPrivileges) { + fullUser.append("@"); + fullUser.append(host); + } + + String columnName = results.getString(6); + String allPrivileges = results.getString(7); + + if (allPrivileges != null) { + allPrivileges = allPrivileges.toUpperCase(Locale.ENGLISH); + + StringTokenizer st = new StringTokenizer(allPrivileges, ","); + + while (st.hasMoreTokens()) { + String privilege = st.nextToken().trim(); + byte[][] tuple = new byte[8][]; + tuple[0] = s2b(db); + tuple[1] = null; + tuple[2] = s2b(table); + tuple[3] = s2b(columnName); + tuple[4] = grantor != null ? s2b(grantor) : null; + tuple[5] = s2b(fullUser.toString()); + tuple[6] = s2b(privilege); + tuple[7] = null; + grantRows.add(new ByteArrayRow(tuple, getExceptionInterceptor())); + } + } + } + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + + if (pStmt != null) { + try { + pStmt.close(); + } catch (Exception ex) { + } + + pStmt = null; + } + } + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(grantRows, new DefaultColumnDefinition(fields))); + } + + public java.sql.ResultSet getColumns(final String catalog, final String schemaPattern, final String tableNamePattern, String columnNamePattern) + throws SQLException { + + final String colPattern = columnNamePattern; + + Field[] fields = createColumnsFields(); + + final ArrayList rows = new ArrayList<>(); + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + + ArrayList tableNameList = new ArrayList<>(); + + java.sql.ResultSet tables = null; + + try { + tables = getTables(catalogStr, schemaPattern, tableNamePattern, new String[0]); + + while (tables.next()) { + String tableNameFromList = tables.getString("TABLE_NAME"); + tableNameList.add(tableNameFromList); + } + } finally { + if (tables != null) { + try { + tables.close(); + } catch (Exception sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + tables = null; + } + } + + for (String tableName : tableNameList) { + + ResultSet results = null; + + try { + StringBuilder queryBuf = new StringBuilder("SHOW FULL COLUMNS FROM "); + queryBuf.append(StringUtils.quoteIdentifier(tableName, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + queryBuf.append(" FROM "); + queryBuf.append(StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + if (colPattern != null) { + queryBuf.append(" LIKE "); + queryBuf.append(StringUtils.quoteIdentifier(colPattern, "'", true)); + } + + // Return correct ordinals if column name pattern is not '%' + // Currently, MySQL doesn't show enough data to do this, so we do it the 'hard' way...Once _SYSTEM tables are in, this should be + // much easier + boolean fixUpOrdinalsRequired = false; + Map ordinalFixUpMap = null; + + if (colPattern != null && !colPattern.equals("%")) { + fixUpOrdinalsRequired = true; + + StringBuilder fullColumnQueryBuf = new StringBuilder("SHOW FULL COLUMNS FROM "); + fullColumnQueryBuf + .append(StringUtils.quoteIdentifier(tableName, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + fullColumnQueryBuf.append(" FROM "); + fullColumnQueryBuf + .append(StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + + results = stmt.executeQuery(fullColumnQueryBuf.toString()); + + ordinalFixUpMap = new HashMap<>(); + + int fullOrdinalPos = 1; + + while (results.next()) { + String fullOrdColName = results.getString("Field"); + + ordinalFixUpMap.put(fullOrdColName, Integer.valueOf(fullOrdinalPos++)); + } + results.close(); + } + + results = stmt.executeQuery(queryBuf.toString()); + + int ordPos = 1; + + while (results.next()) { + TypeDescriptor typeDesc = new TypeDescriptor(results.getString("Type"), results.getString("Null")); + + byte[][] rowVal = new byte[24][]; + rowVal[0] = s2b(catalogStr); // TABLE_CAT + rowVal[1] = null; // TABLE_SCHEM (No schemas in MySQL) + rowVal[2] = s2b(tableName); // TABLE_NAME + rowVal[3] = results.getBytes("Field"); + rowVal[4] = Short.toString((short) typeDesc.mysqlType.getJdbcType()).getBytes();// DATA_TYPE (jdbc) + rowVal[5] = s2b(typeDesc.mysqlType.getName()); // TYPE_NAME (native) + if (typeDesc.columnSize == null) { // COLUMN_SIZE + rowVal[6] = null; + } else { + String collation = results.getString("Collation"); + int mbminlen = 1; + if (collation != null) { + // not null collation could only be returned by server for character types, so we don't need to check type name + if (collation.indexOf("ucs2") > -1 || collation.indexOf("utf16") > -1) { + mbminlen = 2; + } else if (collation.indexOf("utf32") > -1) { + mbminlen = 4; + } + } + rowVal[6] = mbminlen == 1 ? s2b(typeDesc.columnSize.toString()) + : s2b(((Integer) (typeDesc.columnSize / mbminlen)).toString()); + } + rowVal[7] = s2b(Integer.toString(typeDesc.bufferLength)); + rowVal[8] = typeDesc.decimalDigits == null ? null : s2b(typeDesc.decimalDigits.toString()); + rowVal[9] = s2b(Integer.toString(typeDesc.numPrecRadix)); + rowVal[10] = s2b(Integer.toString(typeDesc.nullability)); + + // + // Doesn't always have this field, depending on version + // + try { + rowVal[11] = results.getBytes("Comment"); // REMARK column + } catch (Exception E) { + rowVal[11] = new byte[0]; // REMARK column + } + + rowVal[12] = results.getBytes("Default"); // COLUMN_DEF + rowVal[13] = new byte[] { (byte) '0' }; // SQL_DATA_TYPE + rowVal[14] = new byte[] { (byte) '0' }; // SQL_DATE_TIME_SUB + + if (StringUtils.indexOfIgnoreCase(typeDesc.mysqlType.getName(), "CHAR") != -1 + || StringUtils.indexOfIgnoreCase(typeDesc.mysqlType.getName(), "BLOB") != -1 + || StringUtils.indexOfIgnoreCase(typeDesc.mysqlType.getName(), "TEXT") != -1 + || StringUtils.indexOfIgnoreCase(typeDesc.mysqlType.getName(), "ENUM") != -1 + || StringUtils.indexOfIgnoreCase(typeDesc.mysqlType.getName(), "SET") != -1 + || StringUtils.indexOfIgnoreCase(typeDesc.mysqlType.getName(), "BINARY") != -1) { + rowVal[15] = rowVal[6]; // CHAR_OCTET_LENGTH + } else { + rowVal[15] = null; + } + + // ORDINAL_POSITION + if (!fixUpOrdinalsRequired) { + rowVal[16] = Integer.toString(ordPos++).getBytes(); + } else { + String origColName = results.getString("Field"); + Integer realOrdinal = ordinalFixUpMap.get(origColName); + + if (realOrdinal != null) { + rowVal[16] = realOrdinal.toString().getBytes(); + } else { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.10"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + getExceptionInterceptor()); + } + } + + rowVal[17] = s2b(typeDesc.isNullable); + + // We don't support REF or DISTINCT types + rowVal[18] = null; + rowVal[19] = null; + rowVal[20] = null; + rowVal[21] = null; + + rowVal[22] = s2b(""); + + String extra = results.getString("Extra"); + + if (extra != null) { + rowVal[22] = s2b(StringUtils.indexOfIgnoreCase(extra, "auto_increment") != -1 ? "YES" : "NO"); + rowVal[23] = s2b(StringUtils.indexOfIgnoreCase(extra, "generated") != -1 ? "YES" : "NO"); + } + + rows.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + } + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + java.sql.ResultSet results = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(rows, new DefaultColumnDefinition(fields))); + + return results; + } + + protected Field[] createColumnsFields() { + Field[] fields = new Field[24]; + fields[0] = new Field("", "TABLE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[1] = new Field("", "TABLE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[2] = new Field("", "TABLE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[3] = new Field("", "COLUMN_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[4] = new Field("", "DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 5); + fields[5] = new Field("", "TYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 16); // TODO why is it 16 bytes long? we have longer types specifications + fields[6] = new Field("", "COLUMN_SIZE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, + Integer.toString(Integer.MAX_VALUE).length()); + fields[7] = new Field("", "BUFFER_LENGTH", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[8] = new Field("", "DECIMAL_DIGITS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[9] = new Field("", "NUM_PREC_RADIX", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[10] = new Field("", "NULLABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[11] = new Field("", "REMARKS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[12] = new Field("", "COLUMN_DEF", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[13] = new Field("", "SQL_DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[14] = new Field("", "SQL_DATETIME_SUB", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[15] = new Field("", "CHAR_OCTET_LENGTH", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, + Integer.toString(Integer.MAX_VALUE).length()); + fields[16] = new Field("", "ORDINAL_POSITION", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[17] = new Field("", "IS_NULLABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 3); + fields[18] = new Field("", "SCOPE_CATALOG", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[19] = new Field("", "SCOPE_SCHEMA", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[20] = new Field("", "SCOPE_TABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[21] = new Field("", "SOURCE_DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 10); + fields[22] = new Field("", "IS_AUTOINCREMENT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 3); + fields[23] = new Field("", "IS_GENERATEDCOLUMN", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 3); + return fields; + } + + /** + * JDBC 2.0 Return the connection that produced this metadata object. + * + * @return the connection that produced this metadata object. + * @throws SQLException + * if a database error occurs + */ + public java.sql.Connection getConnection() throws SQLException { + return this.conn; + } + + public java.sql.ResultSet getCrossReference(final String primaryCatalog, final String primarySchema, final String primaryTable, final String foreignCatalog, + final String foreignSchema, final String foreignTable) throws SQLException { + if (primaryTable == null) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + Field[] fields = createFkMetadataFields(); + + final ArrayList tuples = new ArrayList<>(); + + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(foreignCatalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + + ResultSet fkresults = null; + + try { + + /* + * Get foreign key information for table + */ + fkresults = extractForeignKeyFromCreateTable(catalogStr, null); + + String foreignTableWithCase = getTableNameWithCase(foreignTable); + String primaryTableWithCase = getTableNameWithCase(primaryTable); + + /* + * Parse imported foreign key information + */ + + String dummy; + + while (fkresults.next()) { + String tableType = fkresults.getString("Type"); + + if ((tableType != null) && (tableType.equalsIgnoreCase("innodb") || tableType.equalsIgnoreCase(SUPPORTS_FK))) { + String comment = fkresults.getString("Comment").trim(); + + if (comment != null) { + StringTokenizer commentTokens = new StringTokenizer(comment, ";", false); + + if (commentTokens.hasMoreTokens()) { + dummy = commentTokens.nextToken(); + + // Skip InnoDB comment + } + + while (commentTokens.hasMoreTokens()) { + String keys = commentTokens.nextToken(); + LocalAndReferencedColumns parsedInfo = parseTableStatusIntoLocalAndReferencedColumns(keys); + + int keySeq = 0; + + Iterator referencingColumns = parsedInfo.localColumnsList.iterator(); + Iterator referencedColumns = parsedInfo.referencedColumnsList.iterator(); + + while (referencingColumns.hasNext()) { + String referencingColumn = StringUtils.unQuoteIdentifier(referencingColumns.next(), DatabaseMetaData.this.quotedId); + + // one tuple for each table between parenthesis + byte[][] tuple = new byte[14][]; + tuple[4] = ((foreignCatalog == null) ? null : s2b(foreignCatalog)); + tuple[5] = ((foreignSchema == null) ? null : s2b(foreignSchema)); + dummy = fkresults.getString("Name"); // FKTABLE_NAME + + if (dummy.compareTo(foreignTableWithCase) != 0) { + continue; + } + + tuple[6] = s2b(dummy); + + tuple[7] = s2b(referencingColumn); // FKCOLUMN_NAME + tuple[0] = ((primaryCatalog == null) ? null : s2b(primaryCatalog)); + tuple[1] = ((primarySchema == null) ? null : s2b(primarySchema)); + + // Skip foreign key if it doesn't refer to the right table + if (parsedInfo.referencedTable.compareTo(primaryTableWithCase) != 0) { + continue; + } + + tuple[2] = s2b(parsedInfo.referencedTable); // PKTABLE_NAME + tuple[3] = s2b(StringUtils.unQuoteIdentifier(referencedColumns.next(), DatabaseMetaData.this.quotedId)); // PKCOLUMN_NAME + tuple[8] = Integer.toString(keySeq).getBytes(); // KEY_SEQ + + int[] actions = getForeignKeyActions(keys); + + tuple[9] = Integer.toString(actions[1]).getBytes(); + tuple[10] = Integer.toString(actions[0]).getBytes(); + tuple[11] = null; // FK_NAME + tuple[12] = null; // PK_NAME + tuple[13] = Integer.toString(java.sql.DatabaseMetaData.importedKeyNotDeferrable).getBytes(); + tuples.add(new ByteArrayRow(tuple, getExceptionInterceptor())); + keySeq++; + } + } + } + } + } + + } finally { + if (fkresults != null) { + try { + fkresults.close(); + } catch (Exception sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + fkresults = null; + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + java.sql.ResultSet results = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(tuples, new DefaultColumnDefinition(fields))); + + return results; + } + + protected Field[] createFkMetadataFields() { + Field[] fields = new Field[14]; + fields[0] = new Field("", "PKTABLE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[1] = new Field("", "PKTABLE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[2] = new Field("", "PKTABLE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[3] = new Field("", "PKCOLUMN_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[4] = new Field("", "FKTABLE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[5] = new Field("", "FKTABLE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[6] = new Field("", "FKTABLE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[7] = new Field("", "FKCOLUMN_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[8] = new Field("", "KEY_SEQ", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 2); + fields[9] = new Field("", "UPDATE_RULE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 2); + fields[10] = new Field("", "DELETE_RULE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 2); + fields[11] = new Field("", "FK_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[12] = new Field("", "PK_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[13] = new Field("", "DEFERRABILITY", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 2); + return fields; + } + + public int getDatabaseMajorVersion() throws SQLException { + return this.conn.getServerVersion().getMajor(); + } + + public int getDatabaseMinorVersion() throws SQLException { + return this.conn.getServerVersion().getMinor(); + } + + /** + * What's the name of this database product? + * + * @return database product name + * @throws SQLException + */ + public String getDatabaseProductName() throws SQLException { + return "MySQL"; + } + + /** + * What's the version of this database product? + * + * @return database version + * @throws SQLException + */ + public String getDatabaseProductVersion() throws SQLException { + return this.conn.getServerVersion().toString(); + } + + /** + * What's the database's default transaction isolation level? The values are + * defined in java.sql.Connection. + * + * @return the default isolation level + * @throws SQLException + * if a database access error occurs + * @see JdbcConnection + */ + public int getDefaultTransactionIsolation() throws SQLException { + return java.sql.Connection.TRANSACTION_READ_COMMITTED; + } + + /** + * What's this JDBC driver's major version number? + * + * @return JDBC driver major version + */ + public int getDriverMajorVersion() { + return NonRegisteringDriver.getMajorVersionInternal(); + } + + /** + * What's this JDBC driver's minor version number? + * + * @return JDBC driver minor version number + */ + public int getDriverMinorVersion() { + return NonRegisteringDriver.getMinorVersionInternal(); + } + + /** + * What's the name of this JDBC driver? + * + * @return JDBC driver name + * @throws SQLException + */ + public String getDriverName() throws SQLException { + return Constants.CJ_NAME; + } + + /** + * What's the version of this JDBC driver? + * + * @return JDBC driver version + * @throws java.sql.SQLException + */ + public String getDriverVersion() throws java.sql.SQLException { + return Constants.CJ_FULL_NAME + " (Revision: " + Constants.CJ_REVISION + ")"; + } + + public java.sql.ResultSet getExportedKeys(String catalog, String schema, final String table) throws SQLException { + if (table == null) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + Field[] fields = createFkMetadataFields(); + + final ArrayList rows = new ArrayList<>(); + + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + ResultSet fkresults = null; + + try { + + /* + * Get foreign key information for table + */ + // we can use 'SHOW CREATE TABLE' + fkresults = extractForeignKeyFromCreateTable(catalogStr, null); + + // lower-case table name might be turned on + String tableNameWithCase = getTableNameWithCase(table); + + /* + * Parse imported foreign key information + */ + + while (fkresults.next()) { + String tableType = fkresults.getString("Type"); + + if ((tableType != null) && (tableType.equalsIgnoreCase("innodb") || tableType.equalsIgnoreCase(SUPPORTS_FK))) { + String comment = fkresults.getString("Comment").trim(); + + if (comment != null) { + StringTokenizer commentTokens = new StringTokenizer(comment, ";", false); + + if (commentTokens.hasMoreTokens()) { + commentTokens.nextToken(); // Skip + // InnoDB + // comment + + while (commentTokens.hasMoreTokens()) { + String keys = commentTokens.nextToken(); + getExportKeyResults(catalogStr, tableNameWithCase, keys, rows, fkresults.getString("Name")); + } + } + } + } + } + + } finally { + if (fkresults != null) { + try { + fkresults.close(); + } catch (SQLException sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + fkresults = null; + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + java.sql.ResultSet results = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(rows, new DefaultColumnDefinition(fields))); + + return results; + } + + /** + * Adds to the tuples list the exported keys of exportingTable based on the + * keysComment from the 'show table status' sql command. KeysComment is that + * part of the comment field that follows the "InnoDB free ...;" prefix. + * + * @param catalog + * the database to use + * @param exportingTable + * the table keys are being exported from + * @param keysComment + * the comment from 'show table status' + * @param tuples + * the rows to add results to + * @param fkTableName + * the foreign key table name + * @throws SQLException + * if a database access error occurs + */ + protected void getExportKeyResults(String catalog, String exportingTable, String keysComment, List tuples, String fkTableName) throws SQLException { + getResultsImpl(catalog, exportingTable, keysComment, tuples, fkTableName, true); + } + + /** + * Get all the "extra" characters that can be used in unquoted identifier + * names (those beyond a-z, 0-9 and _). + * + * @return the string containing the extra characters + * @throws SQLException + */ + public String getExtraNameCharacters() throws SQLException { + return "#@"; + } + + /** + * Returns the DELETE and UPDATE foreign key actions from the given 'SHOW + * TABLE STATUS' string, with the DELETE action being the first item in the + * array, and the UPDATE action being the second. + * + * @param commentString + * the comment from 'SHOW TABLE STATUS' + * @return int[] [0] = delete action, [1] = update action + */ + protected int[] getForeignKeyActions(String commentString) { + int[] actions = new int[] { java.sql.DatabaseMetaData.importedKeyNoAction, java.sql.DatabaseMetaData.importedKeyNoAction }; + + int lastParenIndex = commentString.lastIndexOf(")"); + + if (lastParenIndex != (commentString.length() - 1)) { + String cascadeOptions = commentString.substring(lastParenIndex + 1).trim().toUpperCase(Locale.ENGLISH); + + actions[0] = getCascadeDeleteOption(cascadeOptions); + actions[1] = getCascadeUpdateOption(cascadeOptions); + } + + return actions; + } + + /** + * What's the string used to quote SQL identifiers? This returns a space " " + * if identifier quoting isn't supported. A JDBC compliant driver always + * uses a double quote character. + * + * @return the quoting string + * @throws SQLException + */ + public String getIdentifierQuoteString() throws SQLException { + return this.session.getIdentifierQuoteString(); + } + + public java.sql.ResultSet getImportedKeys(String catalog, String schema, final String table) throws SQLException { + if (table == null) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + Field[] fields = createFkMetadataFields(); + + final ArrayList rows = new ArrayList<>(); + + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + ResultSet fkresults = null; + + try { + + /* + * Get foreign key information for table + */ + // we can use 'SHOW CREATE TABLE' + fkresults = extractForeignKeyFromCreateTable(catalogStr, table); + + /* + * Parse imported foreign key information + */ + + while (fkresults.next()) { + String tableType = fkresults.getString("Type"); + + if ((tableType != null) && (tableType.equalsIgnoreCase("innodb") || tableType.equalsIgnoreCase(SUPPORTS_FK))) { + String comment = fkresults.getString("Comment").trim(); + + if (comment != null) { + StringTokenizer commentTokens = new StringTokenizer(comment, ";", false); + + if (commentTokens.hasMoreTokens()) { + commentTokens.nextToken(); // Skip InnoDB comment + + while (commentTokens.hasMoreTokens()) { + String keys = commentTokens.nextToken(); + getImportKeyResults(catalogStr, table, keys, rows); + } + } + } + } + } + } finally { + if (fkresults != null) { + try { + fkresults.close(); + } catch (SQLException sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + + fkresults = null; + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + java.sql.ResultSet results = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(rows, new DefaultColumnDefinition(fields))); + + return results; + } + + /** + * Populates the tuples list with the imported keys of importingTable based + * on the keysComment from the 'show table status' sql command. KeysComment + * is that part of the comment field that follows the "InnoDB free ...;" + * prefix. + * + * @param catalog + * the database to use + * @param importingTable + * the table keys are being imported to + * @param keysComment + * the comment from 'show table status' + * @param tuples + * the rows to add results to + * @throws SQLException + * if a database access error occurs + */ + protected void getImportKeyResults(String catalog, String importingTable, String keysComment, List tuples) throws SQLException { + getResultsImpl(catalog, importingTable, keysComment, tuples, null, false); + } + + public java.sql.ResultSet getIndexInfo(String catalog, String schema, final String table, final boolean unique, boolean approximate) throws SQLException { + /* + * MySQL stores index information in the following fields: Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part + */ + + Field[] fields = createIndexInfoFields(); + + final SortedMap sortedRows = new TreeMap<>(); + final ArrayList rows = new ArrayList<>(); + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + + ResultSet results = null; + + try { + StringBuilder queryBuf = new StringBuilder("SHOW INDEX FROM "); + queryBuf.append(StringUtils.quoteIdentifier(table, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + queryBuf.append(" FROM "); + queryBuf.append(StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + + try { + results = stmt.executeQuery(queryBuf.toString()); + } catch (SQLException sqlEx) { + int errorCode = sqlEx.getErrorCode(); + + // If SQLState is 42S02, ignore this SQLException it means the table doesn't exist.... + if (!"42S02".equals(sqlEx.getSQLState())) { + // Sometimes not mapped correctly for pre-4.1 so use error code instead. + if (errorCode != MysqlErrorNumbers.ER_NO_SUCH_TABLE) { + throw sqlEx; + } + } + } + + while (results != null && results.next()) { + byte[][] row = new byte[14][]; + row[0] = ((catalogStr == null) ? new byte[0] : s2b(catalogStr)); + row[1] = null; + row[2] = results.getBytes("Table"); + + boolean indexIsUnique = results.getInt("Non_unique") == 0; + + row[3] = (!indexIsUnique ? s2b("true") : s2b("false")); + row[4] = new byte[0]; + row[5] = results.getBytes("Key_name"); + short indexType = java.sql.DatabaseMetaData.tableIndexOther; + row[6] = Integer.toString(indexType).getBytes(); + row[7] = results.getBytes("Seq_in_index"); + row[8] = results.getBytes("Column_name"); + row[9] = results.getBytes("Collation"); + + long cardinality = results.getLong("Cardinality"); + + row[10] = s2b(String.valueOf(cardinality)); + row[11] = s2b("0"); + row[12] = null; + + IndexMetaDataKey indexInfoKey = new IndexMetaDataKey(!indexIsUnique, indexType, results.getString("Key_name").toLowerCase(), + results.getShort("Seq_in_index")); + + if (unique) { + if (indexIsUnique) { + sortedRows.put(indexInfoKey, new ByteArrayRow(row, getExceptionInterceptor())); + } + } else { + // All rows match + sortedRows.put(indexInfoKey, new ByteArrayRow(row, getExceptionInterceptor())); + } + } + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + } + } + }.doForAll(); + + Iterator sortedRowsIterator = sortedRows.values().iterator(); + while (sortedRowsIterator.hasNext()) { + rows.add(sortedRowsIterator.next()); + } + + java.sql.ResultSet indexInfo = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(rows, new DefaultColumnDefinition(fields))); + + return indexInfo; + } finally { + if (stmt != null) { + stmt.close(); + } + } + } + + protected Field[] createIndexInfoFields() { + Field[] fields = new Field[13]; + fields[0] = new Field("", "TABLE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[1] = new Field("", "TABLE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[2] = new Field("", "TABLE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[3] = new Field("", "NON_UNIQUE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.BOOLEAN, 4); + fields[4] = new Field("", "INDEX_QUALIFIER", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 1); + fields[5] = new Field("", "INDEX_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[6] = new Field("", "TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 32); + fields[7] = new Field("", "ORDINAL_POSITION", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 5); + fields[8] = new Field("", "COLUMN_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[9] = new Field("", "ASC_OR_DESC", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 1); + fields[10] = new Field("", "CARDINALITY", this.metadataCollationIndex, this.metadataEncoding, MysqlType.BIGINT, 20); + fields[11] = new Field("", "PAGES", this.metadataCollationIndex, this.metadataEncoding, MysqlType.BIGINT, 20); + fields[12] = new Field("", "FILTER_CONDITION", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + return fields; + } + + public int getJDBCMajorVersion() throws SQLException { + return 4; + } + + public int getJDBCMinorVersion() throws SQLException { + return 2; + } + + /** + * How many hex characters can you have in an inline binary literal? + * + * @return max literal length + * @throws SQLException + */ + public int getMaxBinaryLiteralLength() throws SQLException { + return 16777208; + } + + /** + * What's the maximum length of a catalog name? + * + * @return max name length in bytes + * @throws SQLException + */ + public int getMaxCatalogNameLength() throws SQLException { + return 32; + } + + /** + * What's the max length for a character literal? + * + * @return max literal length + * @throws SQLException + */ + public int getMaxCharLiteralLength() throws SQLException { + return 16777208; + } + + /** + * What's the limit on column name length? + * + * @return max literal length + * @throws SQLException + */ + public int getMaxColumnNameLength() throws SQLException { + return 64; + } + + /** + * What's the maximum number of columns in a "GROUP BY" clause? + * + * @return max number of columns + * @throws SQLException + */ + public int getMaxColumnsInGroupBy() throws SQLException { + return 64; + } + + /** + * What's the maximum number of columns allowed in an index? + * + * @return max columns + * @throws SQLException + */ + public int getMaxColumnsInIndex() throws SQLException { + return 16; + } + + /** + * What's the maximum number of columns in an "ORDER BY" clause? + * + * @return max columns + * @throws SQLException + */ + public int getMaxColumnsInOrderBy() throws SQLException { + return 64; + } + + /** + * What's the maximum number of columns in a "SELECT" list? + * + * @return max columns + * @throws SQLException + */ + public int getMaxColumnsInSelect() throws SQLException { + return 256; + } + + /** + * What's maximum number of columns in a table? + * + * @return max columns + * @throws SQLException + */ + public int getMaxColumnsInTable() throws SQLException { + return 512; + } + + /** + * How many active connections can we have at a time to this database? + * + * @return max connections + * @throws SQLException + */ + public int getMaxConnections() throws SQLException { + return 0; + } + + /** + * What's the maximum cursor name length? + * + * @return max cursor name length in bytes + * @throws SQLException + */ + public int getMaxCursorNameLength() throws SQLException { + return 64; + } + + /** + * What's the maximum length of an index (in bytes)? + * + * @return max index length in bytes + * @throws SQLException + */ + public int getMaxIndexLength() throws SQLException { + return 256; + } + + /** + * What's the maximum length of a procedure name? + * + * @return max name length in bytes + * @throws SQLException + */ + public int getMaxProcedureNameLength() throws SQLException { + return 0; + } + + /** + * What's the maximum length of a single row? + * + * @return max row size in bytes + * @throws SQLException + */ + public int getMaxRowSize() throws SQLException { + return Integer.MAX_VALUE - 8; // Max buffer size - HEADER + } + + /** + * What's the maximum length allowed for a schema name? + * + * @return max name length in bytes + * @throws SQLException + */ + public int getMaxSchemaNameLength() throws SQLException { + return 0; + } + + /** + * What's the maximum length of a SQL statement? + * + * @return max length in bytes + * @throws SQLException + */ + public int getMaxStatementLength() throws SQLException { + return maxBufferSize - 4; // Max buffer - header + } + + /** + * How many active statements can we have open at one time to this database? + * + * @return the maximum + * @throws SQLException + */ + public int getMaxStatements() throws SQLException { + return 0; + } + + /** + * What's the maximum length of a table name? + * + * @return max name length in bytes + * @throws SQLException + */ + public int getMaxTableNameLength() throws SQLException { + return 64; + } + + /** + * What's the maximum number of tables in a SELECT? + * + * @return the maximum + * @throws SQLException + */ + public int getMaxTablesInSelect() throws SQLException { + return 256; + } + + /** + * What's the maximum length of a user name? + * + * @return max name length in bytes + * @throws SQLException + */ + public int getMaxUserNameLength() throws SQLException { + return 16; + } + + /** + * Get a comma separated list of math functions. + * + * @return the list + * @throws SQLException + */ + public String getNumericFunctions() throws SQLException { + return "ABS,ACOS,ASIN,ATAN,ATAN2,BIT_COUNT,CEILING,COS,COT,DEGREES,EXP,FLOOR,LOG,LOG10,MAX,MIN,MOD,PI,POW," + + "POWER,RADIANS,RAND,ROUND,SIN,SQRT,TAN,TRUNCATE"; + } + + public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, final String table) throws SQLException { + Field[] fields = new Field[6]; + fields[0] = new Field("", "TABLE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[1] = new Field("", "TABLE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[2] = new Field("", "TABLE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[3] = new Field("", "COLUMN_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[4] = new Field("", "KEY_SEQ", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 5); + fields[5] = new Field("", "PK_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + + if (table == null) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + final ArrayList rows = new ArrayList<>(); + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + ResultSet rs = null; + + try { + + StringBuilder queryBuf = new StringBuilder("SHOW KEYS FROM "); + queryBuf.append(StringUtils.quoteIdentifier(table, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + queryBuf.append(" FROM "); + queryBuf.append(StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + + rs = stmt.executeQuery(queryBuf.toString()); + + TreeMap sortMap = new TreeMap<>(); + + while (rs.next()) { + String keyType = rs.getString("Key_name"); + + if (keyType != null) { + if (keyType.equalsIgnoreCase("PRIMARY") || keyType.equalsIgnoreCase("PRI")) { + byte[][] tuple = new byte[6][]; + tuple[0] = ((catalogStr == null) ? new byte[0] : s2b(catalogStr)); + tuple[1] = null; + tuple[2] = s2b(table); + + String columnName = rs.getString("Column_name"); + tuple[3] = s2b(columnName); + tuple[4] = s2b(rs.getString("Seq_in_index")); + tuple[5] = s2b(keyType); + sortMap.put(columnName, tuple); + } + } + } + + // Now pull out in column name sorted order + Iterator sortedIterator = sortMap.values().iterator(); + + while (sortedIterator.hasNext()) { + rows.add(new ByteArrayRow(sortedIterator.next(), getExceptionInterceptor())); + } + + } finally { + if (rs != null) { + try { + rs.close(); + } catch (Exception ex) { + } + + rs = null; + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + java.sql.ResultSet results = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(rows, new DefaultColumnDefinition(fields))); + + return results; + } + + public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) + throws SQLException { + Field[] fields = createProcedureColumnsFields(); + + return getProcedureOrFunctionColumns(fields, catalog, schemaPattern, procedureNamePattern, columnNamePattern, true, + this.conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_getProceduresReturnsFunctions).getValue()); + } + + protected Field[] createProcedureColumnsFields() { + Field[] fields = new Field[20]; + fields[0] = new Field("", "PROCEDURE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 512); + fields[1] = new Field("", "PROCEDURE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 512); + fields[2] = new Field("", "PROCEDURE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 512); + fields[3] = new Field("", "COLUMN_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 512); + fields[4] = new Field("", "COLUMN_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 64); + fields[5] = new Field("", "DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 6); + fields[6] = new Field("", "TYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 64); + fields[7] = new Field("", "PRECISION", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12); + fields[8] = new Field("", "LENGTH", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12); + fields[9] = new Field("", "SCALE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 12); + fields[10] = new Field("", "RADIX", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 6); + fields[11] = new Field("", "NULLABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 6); + fields[12] = new Field("", "REMARKS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 512); + fields[13] = new Field("", "COLUMN_DEF", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 512); + fields[14] = new Field("", "SQL_DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12); + fields[15] = new Field("", "SQL_DATETIME_SUB", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12); + fields[16] = new Field("", "CHAR_OCTET_LENGTH", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12); + fields[17] = new Field("", "ORDINAL_POSITION", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12); + fields[18] = new Field("", "IS_NULLABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 512); + fields[19] = new Field("", "SPECIFIC_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 512); + return fields; + } + + protected java.sql.ResultSet getProcedureOrFunctionColumns(Field[] fields, String catalog, String schemaPattern, String procedureOrFunctionNamePattern, + String columnNamePattern, boolean returnProcedures, boolean returnFunctions) throws SQLException { + + List> procsOrFuncsToExtractList = new ArrayList<>(); + //Main container to be passed to getProceduresAndOrFunctions + ResultSet procsAndOrFuncsRs = null; + + try { + //getProceduresAndOrFunctions does NOT expect procedureOrFunctionNamePattern in form of DB_NAME.SP_NAME thus we need to remove it + String tmpProcedureOrFunctionNamePattern = null; + //Check if NOT a pattern first, then "sanitize" + if ((procedureOrFunctionNamePattern != null) && (!procedureOrFunctionNamePattern.equals("%"))) { + tmpProcedureOrFunctionNamePattern = StringUtils.sanitizeProcOrFuncName(procedureOrFunctionNamePattern); + } + + //Sanity check, if NamePattern is still NULL, we have a wildcard and not the name + if (tmpProcedureOrFunctionNamePattern == null) { + tmpProcedureOrFunctionNamePattern = procedureOrFunctionNamePattern; + } else { + //So we have a name to check meaning more actual processing + //Keep the Catalog parsed, maybe we'll need it at some point in the future... + String tmpCatalog = catalog; + List parseList = StringUtils.splitDBdotName(tmpProcedureOrFunctionNamePattern, tmpCatalog, this.quotedId, + this.session.getServerSession().isNoBackslashEscapesSet()); + + //There *should* be 2 rows, if any. + if (parseList.size() == 2) { + tmpCatalog = parseList.get(0); + tmpProcedureOrFunctionNamePattern = parseList.get(1); + } else { + //keep values as they are + } + } + + procsAndOrFuncsRs = getProceduresAndOrFunctions(createFieldMetadataForGetProcedures(), catalog, schemaPattern, tmpProcedureOrFunctionNamePattern, + returnProcedures, returnFunctions); + + boolean hasResults = false; + while (procsAndOrFuncsRs.next()) { + procsOrFuncsToExtractList.add(new ComparableWrapper<>(getFullyQualifiedName(procsAndOrFuncsRs.getString(1), procsAndOrFuncsRs.getString(3)), + procsAndOrFuncsRs.getShort(8) == procedureNoResult ? PROCEDURE : FUNCTION)); + hasResults = true; + } + + // FIX for Bug#56305, allowing the code to proceed with empty fields causing NPE later + if (!hasResults) { + // throw SQLError.createSQLException( + // "User does not have access to metadata required to determine " + + // "stored procedure parameter types. If rights can not be granted, configure connection with \"noAccessToProcedureBodies=true\" " + + // "to have driver generate parameters that represent INOUT strings irregardless of actual parameter types.", + // MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } else { + Collections.sort(procsOrFuncsToExtractList); + } + + // Required to be sorted in name-order by JDBC spec, in 'normal' case getProcedures takes care of this for us, but if system tables are + // inaccessible, we need to sort... so just do this to be safe... + // Collections.sort(proceduresToExtractList); + } finally { + SQLException rethrowSqlEx = null; + + if (procsAndOrFuncsRs != null) { + try { + procsAndOrFuncsRs.close(); + } catch (SQLException sqlEx) { + rethrowSqlEx = sqlEx; + } + } + + if (rethrowSqlEx != null) { + throw rethrowSqlEx; + } + } + + ArrayList resultRows = new ArrayList<>(); + int idx = 0; + String procNameToCall = ""; + + for (ComparableWrapper procOrFunc : procsOrFuncsToExtractList) { + String procName = procOrFunc.getKey(); + ProcedureType procType = procOrFunc.getValue(); + + //Continuing from above (database_name.sp_name) + if (!" ".equals(this.quotedId)) { + idx = StringUtils.indexOfIgnoreCase(0, procName, ".", this.quotedId, this.quotedId, + this.session.getServerSession().isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL); + } else { + idx = procName.indexOf("."); + } + + if (idx > 0) { + catalog = StringUtils.unQuoteIdentifier(procName.substring(0, idx), this.quotedId); + procNameToCall = procName; // Leave as CAT.PROC, needed later + } else { + //No catalog. Not sure how to handle right now... + procNameToCall = procName; + } + getCallStmtParameterTypes(catalog, procNameToCall, procType, columnNamePattern, resultRows, fields.length == 17 /* for getFunctionColumns */); + } + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(resultRows, new DefaultColumnDefinition(fields))); + } + + public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { + Field[] fields = createFieldMetadataForGetProcedures(); + + return getProceduresAndOrFunctions(fields, catalog, schemaPattern, procedureNamePattern, true, + this.conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_getProceduresReturnsFunctions).getValue()); + } + + protected Field[] createFieldMetadataForGetProcedures() { + Field[] fields = new Field[9]; + fields[0] = new Field("", "PROCEDURE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[1] = new Field("", "PROCEDURE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[2] = new Field("", "PROCEDURE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[3] = new Field("", "reserved1", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[4] = new Field("", "reserved2", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[5] = new Field("", "reserved3", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[6] = new Field("", "REMARKS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[7] = new Field("", "PROCEDURE_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 6); + fields[8] = new Field("", "SPECIFIC_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + + return fields; + } + + /** + * @param fields + * @param catalog + * @param schemaPattern + * @param procedureNamePattern + * @param returnProcedures + * @param returnFunctions + * @throws SQLException + */ + protected java.sql.ResultSet getProceduresAndOrFunctions(final Field[] fields, String catalog, String schemaPattern, String procedureNamePattern, + final boolean returnProcedures, final boolean returnFunctions) throws SQLException { + final ArrayList procedureRows = new ArrayList<>(); + + final String procNamePattern = procedureNamePattern; + + final List> procedureRowsToSort = new ArrayList<>(); + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + String db = catalogStr; + + ResultSet proceduresRs = null; + boolean needsClientFiltering = true; + + StringBuilder selectFromMySQLProcSQL = new StringBuilder(); + + selectFromMySQLProcSQL.append("SELECT name, type, comment FROM mysql.proc WHERE"); + if (returnProcedures && !returnFunctions) { + selectFromMySQLProcSQL.append(" type = 'PROCEDURE' AND "); + } else if (!returnProcedures && returnFunctions) { + selectFromMySQLProcSQL.append(" type = 'FUNCTION' AND "); + } + + selectFromMySQLProcSQL.append(" db <=> ?"); + + if (procNamePattern != null && procNamePattern.length() > 0) { + selectFromMySQLProcSQL.append(" AND name LIKE ?"); + } + + selectFromMySQLProcSQL.append(" ORDER BY name, type"); + + java.sql.PreparedStatement proceduresStmt = prepareMetaDataSafeStatement(selectFromMySQLProcSQL.toString()); + + try { + // + // Try using system tables first, as this is a little bit more efficient.... + // + if (db != null) { + if (DatabaseMetaData.this.conn.lowerCaseTableNames()) { + db = db.toLowerCase(); + } + proceduresStmt.setString(1, db); + } else { + proceduresStmt.setNull(1, MysqlType.VARCHAR.getJdbcType()); + } + + int nameIndex = 1; + + if (procNamePattern != null && procNamePattern.length() > 0) { + proceduresStmt.setString(2, procNamePattern); + } + + try { + proceduresRs = proceduresStmt.executeQuery(); + needsClientFiltering = false; + + if (returnProcedures) { + convertToJdbcProcedureList(true, db, proceduresRs, needsClientFiltering, db, procedureRowsToSort, nameIndex); + } + + if (returnFunctions) { + convertToJdbcFunctionList(db, proceduresRs, needsClientFiltering, db, procedureRowsToSort, nameIndex, fields); + } + + } catch (SQLException sqlEx) { + nameIndex = 2; + + // System tables aren't accessible, so use 'SHOW [FUNCTION|PROCEDURE] STATUS instead. + // Functions first: + if (returnFunctions) { + proceduresStmt.close(); + + String sql = procNamePattern != null && procNamePattern.length() > 0 ? "SHOW FUNCTION STATUS LIKE ?" : "SHOW FUNCTION STATUS"; + proceduresStmt = prepareMetaDataSafeStatement(sql); + if (procNamePattern != null && procNamePattern.length() > 0) { + proceduresStmt.setString(1, procNamePattern); + } + proceduresRs = proceduresStmt.executeQuery(); + + convertToJdbcFunctionList(db, proceduresRs, needsClientFiltering, db, procedureRowsToSort, nameIndex, fields); + } + + // Procedures next: + if (returnProcedures) { + proceduresStmt.close(); + + String sql = procNamePattern != null && procNamePattern.length() > 0 ? "SHOW PROCEDURE STATUS LIKE ?" : "SHOW PROCEDURE STATUS"; + proceduresStmt = prepareMetaDataSafeStatement(sql); + if (procNamePattern != null && procNamePattern.length() > 0) { + proceduresStmt.setString(1, procNamePattern); + } + proceduresRs = proceduresStmt.executeQuery(); + + convertToJdbcProcedureList(false, db, proceduresRs, needsClientFiltering, db, procedureRowsToSort, nameIndex); + } + } + + } finally { + SQLException rethrowSqlEx = null; + + if (proceduresRs != null) { + try { + proceduresRs.close(); + } catch (SQLException sqlEx) { + rethrowSqlEx = sqlEx; + } + } + + if (proceduresStmt != null) { + try { + proceduresStmt.close(); + } catch (SQLException sqlEx) { + rethrowSqlEx = sqlEx; + } + } + + if (rethrowSqlEx != null) { + throw rethrowSqlEx; + } + } + } + }.doForAll(); + + Collections.sort(procedureRowsToSort); + for (ComparableWrapper procRow : procedureRowsToSort) { + procedureRows.add(procRow.getValue()); + } + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(procedureRows, new DefaultColumnDefinition(fields))); + } + + /** + * What's the database vendor's preferred term for "procedure"? + * + * @return the vendor term + * @throws SQLException + * if an error occurs (don't know why it would in this case...) + */ + public String getProcedureTerm() throws SQLException { + return "PROCEDURE"; + } + + public int getResultSetHoldability() throws SQLException { + return ResultSet.HOLD_CURSORS_OVER_COMMIT; + } + + private void getResultsImpl(String catalog, String table, String keysComment, List tuples, String fkTableName, boolean isExport) throws SQLException { + + LocalAndReferencedColumns parsedInfo = parseTableStatusIntoLocalAndReferencedColumns(keysComment); + + if (isExport && !parsedInfo.referencedTable.equals(table)) { + return; + } + + if (parsedInfo.localColumnsList.size() != parsedInfo.referencedColumnsList.size()) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.12"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + Iterator localColumnNames = parsedInfo.localColumnsList.iterator(); + Iterator referColumnNames = parsedInfo.referencedColumnsList.iterator(); + + int keySeqIndex = 1; + + while (localColumnNames.hasNext()) { + byte[][] tuple = new byte[14][]; + String lColumnName = StringUtils.unQuoteIdentifier(localColumnNames.next(), this.quotedId); + String rColumnName = StringUtils.unQuoteIdentifier(referColumnNames.next(), this.quotedId); + tuple[FKTABLE_CAT] = ((catalog == null) ? new byte[0] : s2b(catalog)); + tuple[FKTABLE_SCHEM] = null; + tuple[FKTABLE_NAME] = s2b((isExport) ? fkTableName : table); + tuple[FKCOLUMN_NAME] = s2b(lColumnName); + tuple[PKTABLE_CAT] = s2b(parsedInfo.referencedCatalog); + tuple[PKTABLE_SCHEM] = null; + tuple[PKTABLE_NAME] = s2b((isExport) ? table : parsedInfo.referencedTable); + tuple[PKCOLUMN_NAME] = s2b(rColumnName); + tuple[KEY_SEQ] = s2b(Integer.toString(keySeqIndex++)); + + int[] actions = getForeignKeyActions(keysComment); + + tuple[UPDATE_RULE] = s2b(Integer.toString(actions[1])); + tuple[DELETE_RULE] = s2b(Integer.toString(actions[0])); + tuple[FK_NAME] = s2b(parsedInfo.constraintName); + tuple[PK_NAME] = null; // not available from show table status + tuple[DEFERRABILITY] = s2b(Integer.toString(java.sql.DatabaseMetaData.importedKeyNotDeferrable)); + tuples.add(new ByteArrayRow(tuple, getExceptionInterceptor())); + } + } + + public java.sql.ResultSet getSchemas() throws SQLException { + Field[] fields = new Field[2]; + fields[0] = new Field("", "TABLE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + fields[1] = new Field("", "TABLE_CATALOG", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 0); + + ArrayList tuples = new ArrayList<>(); + java.sql.ResultSet results = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(tuples, new DefaultColumnDefinition(fields))); + + return results; + } + + /** + * What's the database vendor's preferred term for "schema"? + * + * @return the vendor term + * @throws SQLException + */ + public String getSchemaTerm() throws SQLException { + return ""; + } + + /** + * This is the string that can be used to escape '_' or '%' in the string + * pattern style catalog search parameters. + *

+ * The '_' character represents any single character. + *

+ *

+ * The '%' character represents any sequence of zero or more characters. + *

+ * + * @return the string used to escape wildcard characters + * @throws SQLException + */ + public String getSearchStringEscape() throws SQLException { + return "\\"; + } + + /** + * Get a comma separated list of all a database's SQL keywords that are NOT also SQL92/SQL2003 keywords. + * + * @return the list + * @throws SQLException + */ + public String getSQLKeywords() throws SQLException { + if (mysqlKeywords != null) { + return mysqlKeywords; + } + + synchronized (DatabaseMetaData.class) { + // double check, maybe it's already set + if (mysqlKeywords != null) { + return mysqlKeywords; + } + + Set mysqlKeywordSet = new TreeSet<>(); + StringBuilder mysqlKeywordsBuffer = new StringBuilder(); + + Collections.addAll(mysqlKeywordSet, MYSQL_KEYWORDS); + mysqlKeywordSet.removeAll(Arrays.asList(SQL2003_KEYWORDS)); + + for (String keyword : mysqlKeywordSet) { + mysqlKeywordsBuffer.append(",").append(keyword); + } + + mysqlKeywords = mysqlKeywordsBuffer.substring(1); + return mysqlKeywords; + } + } + + public int getSQLStateType() throws SQLException { + return java.sql.DatabaseMetaData.sqlStateSQL99; + } + + /** + * Get a comma separated list of string functions. + * + * @return the list + * @throws SQLException + */ + public String getStringFunctions() throws SQLException { + return "ASCII,BIN,BIT_LENGTH,CHAR,CHARACTER_LENGTH,CHAR_LENGTH,CONCAT,CONCAT_WS,CONV,ELT,EXPORT_SET,FIELD,FIND_IN_SET,HEX,INSERT," + + "INSTR,LCASE,LEFT,LENGTH,LOAD_FILE,LOCATE,LOCATE,LOWER,LPAD,LTRIM,MAKE_SET,MATCH,MID,OCT,OCTET_LENGTH,ORD,POSITION," + + "QUOTE,REPEAT,REPLACE,REVERSE,RIGHT,RPAD,RTRIM,SOUNDEX,SPACE,STRCMP,SUBSTRING,SUBSTRING,SUBSTRING,SUBSTRING," + + "SUBSTRING_INDEX,TRIM,UCASE,UPPER"; + } + + public java.sql.ResultSet getSuperTables(String arg0, String arg1, String arg2) throws SQLException { + Field[] fields = new Field[4]; + fields[0] = new Field("", "TABLE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[1] = new Field("", "TABLE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[2] = new Field("", "TABLE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[3] = new Field("", "SUPERTABLE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(new ArrayList(), new DefaultColumnDefinition(fields))); + } + + public java.sql.ResultSet getSuperTypes(String arg0, String arg1, String arg2) throws SQLException { + Field[] fields = new Field[6]; + fields[0] = new Field("", "TYPE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[1] = new Field("", "TYPE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[2] = new Field("", "TYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[3] = new Field("", "SUPERTYPE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[4] = new Field("", "SUPERTYPE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[5] = new Field("", "SUPERTYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(new ArrayList(), new DefaultColumnDefinition(fields))); + } + + /** + * Get a comma separated list of system functions. + * + * @return the list + * @throws SQLException + */ + public String getSystemFunctions() throws SQLException { + return "DATABASE,USER,SYSTEM_USER,SESSION_USER,PASSWORD,ENCRYPT,LAST_INSERT_ID,VERSION"; + } + + protected String getTableNameWithCase(String table) { + String tableNameWithCase = (this.conn.lowerCaseTableNames() ? table.toLowerCase() : table); + + return tableNameWithCase; + } + + public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException { + + Field[] fields = new Field[7]; + fields[0] = new Field("", "TABLE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 64); + fields[1] = new Field("", "TABLE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 1); + fields[2] = new Field("", "TABLE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 64); + fields[3] = new Field("", "GRANTOR", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 77); + fields[4] = new Field("", "GRANTEE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 77); + fields[5] = new Field("", "PRIVILEGE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 64); + fields[6] = new Field("", "IS_GRANTABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 3); + + StringBuilder grantQueryBuf = new StringBuilder("SELECT host,db,table_name,grantor,user,table_priv FROM mysql.tables_priv"); + + StringBuilder conditionBuf = new StringBuilder(); + if (catalog != null) { + conditionBuf.append(" db LIKE ?"); + } + if (tableNamePattern != null) { + if (conditionBuf.length() > 0) { + conditionBuf.append(" AND"); + } + conditionBuf.append(" table_name LIKE ?"); + } + if (conditionBuf.length() > 0) { + grantQueryBuf.append(" WHERE"); + grantQueryBuf.append(conditionBuf); + } + + ResultSet results = null; + ArrayList grantRows = new ArrayList<>(); + PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(grantQueryBuf.toString()); + int nextId = 1; + if (catalog != null) { + pStmt.setString(nextId++, catalog); + } + if (tableNamePattern != null) { + pStmt.setString(nextId, tableNamePattern); + } + + results = pStmt.executeQuery(); + + while (results.next()) { + String host = results.getString(1); + String db = results.getString(2); + String table = results.getString(3); + String grantor = results.getString(4); + String user = results.getString(5); + + if ((user == null) || (user.length() == 0)) { + user = "%"; + } + + StringBuilder fullUser = new StringBuilder(user); + + if ((host != null) && this.useHostsInPrivileges) { + fullUser.append("@"); + fullUser.append(host); + } + + String allPrivileges = results.getString(6); + + if (allPrivileges != null) { + allPrivileges = allPrivileges.toUpperCase(Locale.ENGLISH); + + StringTokenizer st = new StringTokenizer(allPrivileges, ","); + + while (st.hasMoreTokens()) { + String privilege = st.nextToken().trim(); + + // Loop through every column in the table + java.sql.ResultSet columnResults = null; + + try { + columnResults = getColumns(catalog, schemaPattern, table, null); + + while (columnResults.next()) { + byte[][] tuple = new byte[8][]; + tuple[0] = s2b(db); + tuple[1] = null; + tuple[2] = s2b(table); + tuple[3] = grantor != null ? s2b(grantor) : null; + tuple[4] = s2b(fullUser.toString()); + tuple[5] = s2b(privilege); + tuple[6] = null; + grantRows.add(new ByteArrayRow(tuple, getExceptionInterceptor())); + } + } finally { + if (columnResults != null) { + try { + columnResults.close(); + } catch (Exception ex) { + } + } + } + } + } + } + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + + if (pStmt != null) { + try { + pStmt.close(); + } catch (Exception ex) { + } + + pStmt = null; + } + } + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(grantRows, new DefaultColumnDefinition(fields))); + } + + public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, final String[] types) throws SQLException { + + final SortedMap sortedRows = new TreeMap<>(); + final ArrayList tuples = new ArrayList<>(); + + final Statement stmt = this.conn.getMetadataSafeStatement(); + + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + if (tableNamePattern != null) { + List parseList = StringUtils.splitDBdotName(tableNamePattern, catalog, this.quotedId, + this.session.getServerSession().isNoBackslashEscapesSet()); + //There *should* be 2 rows, if any. + if (parseList.size() == 2) { + tableNamePattern = parseList.get(1); + } + } + + final String tableNamePat = tableNamePattern; + + try { + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + boolean operatingOnSystemDB = "information_schema".equalsIgnoreCase(catalogStr) || "mysql".equalsIgnoreCase(catalogStr) + || "performance_schema".equalsIgnoreCase(catalogStr); + + ResultSet results = null; + + try { + + try { + StringBuilder sqlBuf = new StringBuilder("SHOW FULL TABLES FROM "); + sqlBuf.append(StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + if (tableNamePat != null) { + sqlBuf.append(" LIKE "); + sqlBuf.append(StringUtils.quoteIdentifier(tableNamePat, "'", true)); + } + + results = stmt.executeQuery(sqlBuf.toString()); + } catch (SQLException sqlEx) { + if (MysqlErrorNumbers.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlEx.getSQLState())) { + throw sqlEx; + } + + return; + } + + boolean shouldReportTables = false; + boolean shouldReportViews = false; + boolean shouldReportSystemTables = false; + boolean shouldReportSystemViews = false; + boolean shouldReportLocalTemporaries = false; + + if (types == null || types.length == 0) { + shouldReportTables = true; + shouldReportViews = true; + shouldReportSystemTables = true; + shouldReportSystemViews = true; + shouldReportLocalTemporaries = true; + } else { + for (int i = 0; i < types.length; i++) { + if (TableType.TABLE.equalsTo(types[i])) { + shouldReportTables = true; + + } else if (TableType.VIEW.equalsTo(types[i])) { + shouldReportViews = true; + + } else if (TableType.SYSTEM_TABLE.equalsTo(types[i])) { + shouldReportSystemTables = true; + + } else if (TableType.SYSTEM_VIEW.equalsTo(types[i])) { + shouldReportSystemViews = true; + + } else if (TableType.LOCAL_TEMPORARY.equalsTo(types[i])) { + shouldReportLocalTemporaries = true; + } + } + } + + int typeColumnIndex = 0; + boolean hasTableTypes = false; + + try { + // Both column names have been in use in the source tree so far.... + typeColumnIndex = results.findColumn("table_type"); + hasTableTypes = true; + } catch (SQLException sqlEx) { + + // We should probably check SQLState here, but that can change depending on the server version and user properties, however, + // we'll get a 'true' SQLException when we actually try to find the 'Type' column + // + try { + typeColumnIndex = results.findColumn("Type"); + hasTableTypes = true; + } catch (SQLException sqlEx2) { + hasTableTypes = false; + } + } + + while (results.next()) { + byte[][] row = new byte[10][]; + row[0] = (catalogStr == null) ? null : s2b(catalogStr); + row[1] = null; + row[2] = results.getBytes(1); + row[4] = new byte[0]; + row[5] = null; + row[6] = null; + row[7] = null; + row[8] = null; + row[9] = null; + + if (hasTableTypes) { + String tableType = results.getString(typeColumnIndex); + + switch (TableType.getTableTypeCompliantWith(tableType)) { + case TABLE: + boolean reportTable = false; + TableMetaDataKey tablesKey = null; + + if (operatingOnSystemDB && shouldReportSystemTables) { + row[3] = TableType.SYSTEM_TABLE.asBytes(); + tablesKey = new TableMetaDataKey(TableType.SYSTEM_TABLE.getName(), catalogStr, null, results.getString(1)); + reportTable = true; + + } else if (!operatingOnSystemDB && shouldReportTables) { + row[3] = TableType.TABLE.asBytes(); + tablesKey = new TableMetaDataKey(TableType.TABLE.getName(), catalogStr, null, results.getString(1)); + reportTable = true; + } + + if (reportTable) { + sortedRows.put(tablesKey, new ByteArrayRow(row, getExceptionInterceptor())); + } + break; + + case VIEW: + if (shouldReportViews) { + row[3] = TableType.VIEW.asBytes(); + sortedRows.put(new TableMetaDataKey(TableType.VIEW.getName(), catalogStr, null, results.getString(1)), + new ByteArrayRow(row, getExceptionInterceptor())); + } + break; + + case SYSTEM_TABLE: + if (shouldReportSystemTables) { + row[3] = TableType.SYSTEM_TABLE.asBytes(); + sortedRows.put(new TableMetaDataKey(TableType.SYSTEM_TABLE.getName(), catalogStr, null, results.getString(1)), + new ByteArrayRow(row, getExceptionInterceptor())); + } + break; + + case SYSTEM_VIEW: + if (shouldReportSystemViews) { + row[3] = TableType.SYSTEM_VIEW.asBytes(); + sortedRows.put(new TableMetaDataKey(TableType.SYSTEM_VIEW.getName(), catalogStr, null, results.getString(1)), + new ByteArrayRow(row, getExceptionInterceptor())); + } + break; + + case LOCAL_TEMPORARY: + if (shouldReportLocalTemporaries) { + row[3] = TableType.LOCAL_TEMPORARY.asBytes(); + sortedRows.put(new TableMetaDataKey(TableType.LOCAL_TEMPORARY.getName(), catalogStr, null, results.getString(1)), + new ByteArrayRow(row, getExceptionInterceptor())); + } + break; + + default: + row[3] = TableType.TABLE.asBytes(); + sortedRows.put(new TableMetaDataKey(TableType.TABLE.getName(), catalogStr, null, results.getString(1)), + new ByteArrayRow(row, getExceptionInterceptor())); + break; + } + } else { + // TODO: Check if this branch is needed for 5.7 server (maybe refactor hasTableTypes) + if (shouldReportTables) { + // Pre-MySQL-5.0.1, tables only + row[3] = TableType.TABLE.asBytes(); + sortedRows.put(new TableMetaDataKey(TableType.TABLE.getName(), catalogStr, null, results.getString(1)), + new ByteArrayRow(row, getExceptionInterceptor())); + } + } + } + + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + } + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + tuples.addAll(sortedRows.values()); + java.sql.ResultSet tables = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(tuples, createTablesFields())); + + return tables; + } + + protected ColumnDefinition createTablesFields() { + Field[] fields = new Field[10]; + fields[0] = new Field("", "TABLE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 255); + fields[1] = new Field("", "TABLE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 0); + fields[2] = new Field("", "TABLE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 255); + fields[3] = new Field("", "TABLE_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 5); + fields[4] = new Field("", "REMARKS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 0); + fields[5] = new Field("", "TYPE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 0); + fields[6] = new Field("", "TYPE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 0); + fields[7] = new Field("", "TYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 0); + fields[8] = new Field("", "SELF_REFERENCING_COL_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 0); + fields[9] = new Field("", "REF_GENERATION", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 0); + return new DefaultColumnDefinition(fields); + } + + public java.sql.ResultSet getTableTypes() throws SQLException { + ArrayList tuples = new ArrayList<>(); + Field[] fields = new Field[] { new Field("", "TABLE_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 256) }; + + tuples.add(new ByteArrayRow(new byte[][] { TableType.LOCAL_TEMPORARY.asBytes() }, getExceptionInterceptor())); + tuples.add(new ByteArrayRow(new byte[][] { TableType.SYSTEM_TABLE.asBytes() }, getExceptionInterceptor())); + tuples.add(new ByteArrayRow(new byte[][] { TableType.SYSTEM_VIEW.asBytes() }, getExceptionInterceptor())); + tuples.add(new ByteArrayRow(new byte[][] { TableType.TABLE.asBytes() }, getExceptionInterceptor())); + tuples.add(new ByteArrayRow(new byte[][] { TableType.VIEW.asBytes() }, getExceptionInterceptor())); + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(tuples, new DefaultColumnDefinition(fields))); + } + + /** + * Get a comma separated list of time and date functions. + * + * @return the list + * @throws SQLException + */ + public String getTimeDateFunctions() throws SQLException { + return "DAYOFWEEK,WEEKDAY,DAYOFMONTH,DAYOFYEAR,MONTH,DAYNAME,MONTHNAME,QUARTER,WEEK,YEAR,HOUR,MINUTE,SECOND,PERIOD_ADD," + + "PERIOD_DIFF,TO_DAYS,FROM_DAYS,DATE_FORMAT,TIME_FORMAT,CURDATE,CURRENT_DATE,CURTIME,CURRENT_TIME,NOW,SYSDATE," + + "CURRENT_TIMESTAMP,UNIX_TIMESTAMP,FROM_UNIXTIME,SEC_TO_TIME,TIME_TO_SEC"; + } + + /** + * + * @param mysqlTypeName + * we use a string name here to allow aliases for the same MysqlType to be listed too + * @return + * @throws SQLException + */ + private byte[][] getTypeInfo(String mysqlTypeName) throws SQLException { + + MysqlType mt = MysqlType.getByName(mysqlTypeName); + byte[][] rowVal = new byte[18][]; + + rowVal[0] = s2b(mysqlTypeName); // Type name + rowVal[1] = Integer.toString(mt.getJdbcType()).getBytes(); // JDBC Data type + // JDBC spec reserved only 'int' type for precision, thus we need to cut longer values + rowVal[2] = Integer.toString(mt.getPrecision() > Integer.MAX_VALUE ? Integer.MAX_VALUE : mt.getPrecision().intValue()).getBytes(); // Precision + switch (mt) { + case TINYBLOB: + case BLOB: + case MEDIUMBLOB: + case LONGBLOB: + case TINYTEXT: + case TEXT: + case MEDIUMTEXT: + case LONGTEXT: + case JSON: + case BINARY: + case VARBINARY: + case CHAR: + case VARCHAR: + case ENUM: + case SET: + case DATE: + case TIME: + case DATETIME: + case TIMESTAMP: + case GEOMETRY: + case UNKNOWN: + rowVal[3] = s2b("'"); // Literal Prefix + rowVal[4] = s2b("'"); // Literal Suffix + break; + default: + rowVal[3] = s2b(""); // Literal Prefix + rowVal[4] = s2b(""); // Literal Suffix + } + rowVal[5] = s2b(mt.getCreateParams()); // Create Params + rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable).getBytes(); // Nullable + rowVal[7] = s2b("true"); // Case Sensitive + rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable).getBytes(); // Searchable + rowVal[9] = s2b(mt.isAllowed(MysqlType.FIELD_FLAG_UNSIGNED) ? "true" : "false"); // Unsignable + rowVal[10] = s2b("false"); // Fixed Prec Scale + rowVal[11] = s2b("false"); // Auto Increment + rowVal[12] = s2b(mt.getName()); // Locale Type Name + switch (mt) { + case DECIMAL: // TODO is it right? DECIMAL isn't a floating-point number... + case DECIMAL_UNSIGNED: + case DOUBLE: + case DOUBLE_UNSIGNED: + rowVal[13] = s2b("-308"); // Minimum Scale + rowVal[14] = s2b("308"); // Maximum Scale + break; + case FLOAT: + case FLOAT_UNSIGNED: + rowVal[13] = s2b("-38"); // Minimum Scale + rowVal[14] = s2b("38"); // Maximum Scale + break; + default: + rowVal[13] = s2b("0"); // Minimum Scale + rowVal[14] = s2b("0"); // Maximum Scale + } + + rowVal[15] = s2b("0"); // SQL Data Type (not used) + rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used) + rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10) + + return rowVal; + } + + public java.sql.ResultSet getTypeInfo() throws SQLException { + Field[] fields = new Field[18]; + fields[0] = new Field("", "TYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[1] = new Field("", "DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 5); + fields[2] = new Field("", "PRECISION", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[3] = new Field("", "LITERAL_PREFIX", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 4); + fields[4] = new Field("", "LITERAL_SUFFIX", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 4); + fields[5] = new Field("", "CREATE_PARAMS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[6] = new Field("", "NULLABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 5); + fields[7] = new Field("", "CASE_SENSITIVE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.BOOLEAN, 3); + fields[8] = new Field("", "SEARCHABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 3); + fields[9] = new Field("", "UNSIGNED_ATTRIBUTE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.BOOLEAN, 3); + fields[10] = new Field("", "FIXED_PREC_SCALE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.BOOLEAN, 3); + fields[11] = new Field("", "AUTO_INCREMENT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.BOOLEAN, 3); + fields[12] = new Field("", "LOCAL_TYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[13] = new Field("", "MINIMUM_SCALE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 5); + fields[14] = new Field("", "MAXIMUM_SCALE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 5); + fields[15] = new Field("", "SQL_DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[16] = new Field("", "SQL_DATETIME_SUB", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[17] = new Field("", "NUM_PREC_RADIX", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + + ArrayList tuples = new ArrayList<>(); + + /* + * The following are ordered by java.sql.Types, and then by how closely the MySQL type matches the JDBC Type (per spec) + */ + tuples.add(new ByteArrayRow(getTypeInfo("BIT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("BOOL"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("TINYINT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("TINYINT UNSIGNED"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("BIGINT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("BIGINT UNSIGNED"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("LONG VARBINARY"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("MEDIUMBLOB"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("LONGBLOB"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("BLOB"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("VARBINARY"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("TINYBLOB"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("BINARY"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("LONG VARCHAR"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("MEDIUMTEXT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("LONGTEXT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("TEXT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("CHAR"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("ENUM"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("SET"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("DECIMAL"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("NUMERIC"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("INTEGER"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("INTEGER UNSIGNED"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("INT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("INT UNSIGNED"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("MEDIUMINT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("MEDIUMINT UNSIGNED"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("SMALLINT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("SMALLINT UNSIGNED"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("FLOAT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("DOUBLE"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("DOUBLE PRECISION"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("REAL"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("VARCHAR"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("TINYTEXT"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("DATE"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("YEAR"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("TIME"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("DATETIME"), getExceptionInterceptor())); + tuples.add(new ByteArrayRow(getTypeInfo("TIMESTAMP"), getExceptionInterceptor())); + + // TODO add missed types (aliases) + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(tuples, new DefaultColumnDefinition(fields))); + } + + public java.sql.ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLException { + Field[] fields = new Field[7]; + fields[0] = new Field("", "TYPE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 32); + fields[1] = new Field("", "TYPE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 32); + fields[2] = new Field("", "TYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 32); + fields[3] = new Field("", "CLASS_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 32); + fields[4] = new Field("", "DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[5] = new Field("", "REMARKS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 32); + fields[6] = new Field("", "BASE_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 10); + + ArrayList tuples = new ArrayList<>(); + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(tuples, new DefaultColumnDefinition(fields))); + } + + /** + * What's the url for this database? + * + * @return the url or null if it can't be generated + * @throws SQLException + */ + public String getURL() throws SQLException { + return this.conn.getURL(); + } + + /** + * What's our user name as known to the database? + * + * @return our database user name + * @throws SQLException + */ + public String getUserName() throws SQLException { + if (this.useHostsInPrivileges) { + Statement stmt = null; + ResultSet rs = null; + + try { + stmt = this.conn.getMetadataSafeStatement(); + + rs = stmt.executeQuery("SELECT USER()"); + rs.next(); + + return rs.getString(1); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (Exception ex) { + AssertionFailedException.shouldNotHappen(ex); + } + + rs = null; + } + + if (stmt != null) { + try { + stmt.close(); + } catch (Exception ex) { + AssertionFailedException.shouldNotHappen(ex); + } + + stmt = null; + } + } + } + + return this.conn.getUser(); + } + + public java.sql.ResultSet getVersionColumns(String catalog, String schema, final String table) throws SQLException { + + if (table == null) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + Field[] fields = new Field[8]; + fields[0] = new Field("", "SCOPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 5); + fields[1] = new Field("", "COLUMN_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 32); + fields[2] = new Field("", "DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 5); + fields[3] = new Field("", "TYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 16); + fields[4] = new Field("", "COLUMN_SIZE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 16); + fields[5] = new Field("", "BUFFER_LENGTH", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 16); + fields[6] = new Field("", "DECIMAL_DIGITS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 16); + fields[7] = new Field("", "PSEUDO_COLUMN", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 5); + + final ArrayList rows = new ArrayList<>(); + + final Statement stmt = this.conn.getMetadataSafeStatement(); + + try { + + new IterateBlock(getCatalogIterator(catalog)) { + @Override + void forEach(String catalogStr) throws SQLException { + + ResultSet results = null; + + try { + StringBuilder whereBuf = new StringBuilder(" Extra LIKE '%on update CURRENT_TIMESTAMP%'"); + List rsFields = new ArrayList<>(); + + if (whereBuf.length() > 0 || rsFields.size() > 0) { + StringBuilder queryBuf = new StringBuilder("SHOW COLUMNS FROM "); + queryBuf.append(StringUtils.quoteIdentifier(table, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + queryBuf.append(" FROM "); + queryBuf.append(StringUtils.quoteIdentifier(catalogStr, DatabaseMetaData.this.quotedId, DatabaseMetaData.this.pedantic)); + queryBuf.append(" WHERE"); + queryBuf.append(whereBuf.toString()); + + results = stmt.executeQuery(queryBuf.toString()); + + while (results.next()) { + TypeDescriptor typeDesc = new TypeDescriptor(results.getString("Type"), results.getString("Null")); + byte[][] rowVal = new byte[8][]; + rowVal[0] = null; // SCOPE is not used + rowVal[1] = results.getBytes("Field"); // COLUMN_NAME + rowVal[2] = Short.toString((short) typeDesc.mysqlType.getJdbcType()).getBytes(); // DATA_TYPE + rowVal[3] = s2b(typeDesc.mysqlType.getName()); // TYPE_NAME + rowVal[4] = typeDesc.columnSize == null ? null : s2b(typeDesc.columnSize.toString()); // COLUMN_SIZE + rowVal[5] = s2b(Integer.toString(typeDesc.bufferLength)); // BUFFER_LENGTH + rowVal[6] = typeDesc.decimalDigits == null ? null : s2b(typeDesc.decimalDigits.toString()); // DECIMAL_DIGITS + rowVal[7] = Integer.toString(java.sql.DatabaseMetaData.versionColumnNotPseudo).getBytes(); // PSEUDO_COLUMN + rows.add(new ByteArrayRow(rowVal, getExceptionInterceptor())); + } + } + } catch (SQLException sqlEx) { + if (!MysqlErrorNumbers.SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND.equals(sqlEx.getSQLState())) { + throw sqlEx; + } + } finally { + if (results != null) { + try { + results.close(); + } catch (Exception ex) { + } + + results = null; + } + } + + } + }.doForAll(); + } finally { + if (stmt != null) { + stmt.close(); + } + } + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(rows, new DefaultColumnDefinition(fields))); + } + + /** + * JDBC 2.0 Determine whether or not a visible row insert can be detected by + * calling ResultSet.rowInserted(). + * + * @param type + * set type, i.e. ResultSet.TYPE_XXX + * @return true if changes are detected by the resultset type + * @exception SQLException + * if a database-access error occurs. + */ + public boolean insertsAreDetected(int type) throws SQLException { + return false; + } + + /** + * Does a catalog appear at the start of a qualified table name? (Otherwise + * it appears at the end) + * + * @return true if it appears at the start + * @throws SQLException + */ + public boolean isCatalogAtStart() throws SQLException { + return true; + } + + /** + * Is the database in read-only mode? + * + * @return true if so + * @throws SQLException + */ + public boolean isReadOnly() throws SQLException { + return false; + } + + public boolean locatorsUpdateCopy() throws SQLException { + return !this.conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_emulateLocators).getValue(); + } + + /** + * Are concatenations between NULL and non-NULL values NULL? A JDBC + * compliant driver always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean nullPlusNonNullIsNull() throws SQLException { + return true; + } + + /** + * Are NULL values sorted at the end regardless of sort order? + * + * @return true if so + * @throws SQLException + */ + public boolean nullsAreSortedAtEnd() throws SQLException { + return false; + } + + /** + * Are NULL values sorted at the start regardless of sort order? + * + * @return true if so + * @throws SQLException + */ + public boolean nullsAreSortedAtStart() throws SQLException { + return false; + } + + /** + * Are NULL values sorted high? + * + * @return true if so + * @throws SQLException + */ + public boolean nullsAreSortedHigh() throws SQLException { + return false; + } + + /** + * Are NULL values sorted low? + * + * @return true if so + * @throws SQLException + */ + public boolean nullsAreSortedLow() throws SQLException { + return !nullsAreSortedHigh(); + } + + /** + * @param type + * @throws SQLException + */ + public boolean othersDeletesAreVisible(int type) throws SQLException { + return false; + } + + /** + * @param type + * @throws SQLException + */ + public boolean othersInsertsAreVisible(int type) throws SQLException { + return false; + } + + /** + * JDBC 2.0 Determine whether changes made by others are visible. + * + * @param type + * set type, i.e. ResultSet.TYPE_XXX + * @return true if changes are visible for the result set type + * @exception SQLException + * if a database-access error occurs. + */ + public boolean othersUpdatesAreVisible(int type) throws SQLException { + return false; + } + + /** + * @param type + * @throws SQLException + */ + public boolean ownDeletesAreVisible(int type) throws SQLException { + return false; + } + + /** + * @param type + * @throws SQLException + */ + public boolean ownInsertsAreVisible(int type) throws SQLException { + return false; + } + + /** + * JDBC 2.0 Determine whether a result set's own changes visible. + * + * @param type + * set type, i.e. ResultSet.TYPE_XXX + * @return true if changes are visible for the result set type + * @exception SQLException + * if a database-access error occurs. + */ + public boolean ownUpdatesAreVisible(int type) throws SQLException { + return false; + } + + protected LocalAndReferencedColumns parseTableStatusIntoLocalAndReferencedColumns(String keysComment) throws SQLException { + // keys will equal something like this: (parent_service_id child_service_id) REFER ds/subservices(parent_service_id child_service_id) + // + // simple-columned keys: (m) REFER airline/tt(a) + // + // multi-columned keys : (m n) REFER airline/vv(a b) + // + // parse of the string into three phases: + // 1: parse the opening parentheses to determine how many results there will be + // 2: read in the schema name/table name + // 3: parse the closing parentheses + + String columnsDelimitter = ","; // what version did this change in? + + int indexOfOpenParenLocalColumns = StringUtils.indexOfIgnoreCase(0, keysComment, "(", this.quotedId, this.quotedId, StringUtils.SEARCH_MODE__ALL); + + if (indexOfOpenParenLocalColumns == -1) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.14"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + String constraintName = StringUtils.unQuoteIdentifier(keysComment.substring(0, indexOfOpenParenLocalColumns).trim(), this.quotedId); + keysComment = keysComment.substring(indexOfOpenParenLocalColumns, keysComment.length()); + + String keysCommentTrimmed = keysComment.trim(); + + int indexOfCloseParenLocalColumns = StringUtils.indexOfIgnoreCase(0, keysCommentTrimmed, ")", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__ALL); + + if (indexOfCloseParenLocalColumns == -1) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.15"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + String localColumnNamesString = keysCommentTrimmed.substring(1, indexOfCloseParenLocalColumns); + + int indexOfRefer = StringUtils.indexOfIgnoreCase(0, keysCommentTrimmed, "REFER ", this.quotedId, this.quotedId, StringUtils.SEARCH_MODE__ALL); + + if (indexOfRefer == -1) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.16"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + int indexOfOpenParenReferCol = StringUtils.indexOfIgnoreCase(indexOfRefer, keysCommentTrimmed, "(", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__MRK_COM_WS); + + if (indexOfOpenParenReferCol == -1) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.17"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + String referCatalogTableString = keysCommentTrimmed.substring(indexOfRefer + "REFER ".length(), indexOfOpenParenReferCol); + + int indexOfSlash = StringUtils.indexOfIgnoreCase(0, referCatalogTableString, "/", this.quotedId, this.quotedId, StringUtils.SEARCH_MODE__MRK_COM_WS); + + if (indexOfSlash == -1) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.18"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + String referCatalog = StringUtils.unQuoteIdentifier(referCatalogTableString.substring(0, indexOfSlash), this.quotedId); + String referTable = StringUtils.unQuoteIdentifier(referCatalogTableString.substring(indexOfSlash + 1).trim(), this.quotedId); + + int indexOfCloseParenRefer = StringUtils.indexOfIgnoreCase(indexOfOpenParenReferCol, keysCommentTrimmed, ")", this.quotedId, this.quotedId, + StringUtils.SEARCH_MODE__ALL); + + if (indexOfCloseParenRefer == -1) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.19"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); + } + + String referColumnNamesString = keysCommentTrimmed.substring(indexOfOpenParenReferCol + 1, indexOfCloseParenRefer); + + List referColumnsList = StringUtils.split(referColumnNamesString, columnsDelimitter, this.quotedId, this.quotedId, false); + List localColumnsList = StringUtils.split(localColumnNamesString, columnsDelimitter, this.quotedId, this.quotedId, false); + + return new LocalAndReferencedColumns(localColumnsList, referColumnsList, constraintName, referCatalog, referTable); + } + + /** + * Converts the given string to bytes, using the connection's character + * encoding, or if not available, the JVM default encoding. + * + * @param s + */ + protected byte[] s2b(String s) throws SQLException { + if (s == null) { + return null; + } + + try { + return StringUtils.getBytes(s, this.conn.getCharacterSetMetadata()); + } catch (CJException e) { + throw SQLExceptionsMapping.translateException(e, getExceptionInterceptor()); + } + } + + /** + * Does the database store mixed case unquoted SQL identifiers in lower + * case? + * + * @return true if so + * @throws SQLException + */ + public boolean storesLowerCaseIdentifiers() throws SQLException { + return this.conn.storesLowerCaseTableName(); + } + + /** + * Does the database store mixed case quoted SQL identifiers in lower case? + * A JDBC compliant driver will always return false. + * + * @return true if so + * @throws SQLException + */ + public boolean storesLowerCaseQuotedIdentifiers() throws SQLException { + return this.conn.storesLowerCaseTableName(); + } + + /** + * Does the database store mixed case unquoted SQL identifiers in mixed + * case? + * + * @return true if so + * @throws SQLException + */ + public boolean storesMixedCaseIdentifiers() throws SQLException { + return !this.conn.storesLowerCaseTableName(); + } + + /** + * Does the database store mixed case quoted SQL identifiers in mixed case? + * A JDBC compliant driver will always return false. + * + * @return true if so + * @throws SQLException + */ + public boolean storesMixedCaseQuotedIdentifiers() throws SQLException { + return !this.conn.storesLowerCaseTableName(); + } + + /** + * Does the database store mixed case unquoted SQL identifiers in upper + * case? + * + * @return true if so + * @throws SQLException + */ + public boolean storesUpperCaseIdentifiers() throws SQLException { + return false; + } + + /** + * Does the database store mixed case quoted SQL identifiers in upper case? + * A JDBC compliant driver will always return true. + * + * @return true if so + * @throws SQLException + */ + public boolean storesUpperCaseQuotedIdentifiers() throws SQLException { + return true; // not actually true, but required by JDBC spec!? + } + + /** + * Is "ALTER TABLE" with add column supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsAlterTableWithAddColumn() throws SQLException { + return true; + } + + /** + * Is "ALTER TABLE" with drop column supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsAlterTableWithDropColumn() throws SQLException { + return true; + } + + /** + * Is the ANSI92 entry level SQL grammar supported? All JDBC compliant + * drivers must return true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsANSI92EntryLevelSQL() throws SQLException { + return true; + } + + /** + * Is the ANSI92 full SQL grammar supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsANSI92FullSQL() throws SQLException { + return false; + } + + /** + * Is the ANSI92 intermediate SQL grammar supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsANSI92IntermediateSQL() throws SQLException { + return false; + } + + /** + * JDBC 2.0 Return true if the driver supports batch updates, else return + * false. + * + * @throws SQLException + */ + public boolean supportsBatchUpdates() throws SQLException { + return true; + } + + /** + * Can a catalog name be used in a data manipulation statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCatalogsInDataManipulation() throws SQLException { + return true; + } + + /** + * Can a catalog name be used in a index definition statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCatalogsInIndexDefinitions() throws SQLException { + return true; + } + + /** + * Can a catalog name be used in a privilege definition statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException { + return true; + } + + /** + * Can a catalog name be used in a procedure call statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCatalogsInProcedureCalls() throws SQLException { + return true; + } + + /** + * Can a catalog name be used in a table definition statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCatalogsInTableDefinitions() throws SQLException { + return true; + } + + /** + * Is column aliasing supported? + *

+ * If so, the SQL AS clause can be used to provide names for computed columns or to provide alias names for columns as required. A JDBC compliant driver + * always returns true. + *

+ * + * @return true if so + * @throws SQLException + */ + public boolean supportsColumnAliasing() throws SQLException { + return true; + } + + /** + * Is the CONVERT function between SQL types supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsConvert() throws SQLException { + return false; + } + + public boolean supportsConvert(int fromType, int toType) throws SQLException { + return MysqlType.supportsConvert(fromType, toType); + } + + /** + * Is the ODBC Core SQL grammar supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCoreSQLGrammar() throws SQLException { + return true; + } + + /** + * Are correlated subqueries supported? A JDBC compliant driver always + * returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsCorrelatedSubqueries() throws SQLException { + return true; + } + + /** + * Are both data definition and data manipulation statements within a + * transaction supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException { + return false; + } + + /** + * Are only data manipulation statements within a transaction supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsDataManipulationTransactionsOnly() throws SQLException { + return false; + } + + /** + * If table correlation names are supported, are they restricted to be + * different from the names of the tables? A JDBC compliant driver always + * returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsDifferentTableCorrelationNames() throws SQLException { + return true; + } + + /** + * Are expressions in "ORDER BY" lists supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsExpressionsInOrderBy() throws SQLException { + return true; + } + + /** + * Is the ODBC Extended SQL grammar supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsExtendedSQLGrammar() throws SQLException { + return false; + } + + /** + * Are full nested outer joins supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsFullOuterJoins() throws SQLException { + return false; + } + + public boolean supportsGetGeneratedKeys() { + return true; + } + + /** + * Is some form of "GROUP BY" clause supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsGroupBy() throws SQLException { + return true; + } + + /** + * Can a "GROUP BY" clause add columns not in the SELECT provided it + * specifies all the columns in the SELECT? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsGroupByBeyondSelect() throws SQLException { + return true; + } + + /** + * Can a "GROUP BY" clause use columns not in the SELECT? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsGroupByUnrelated() throws SQLException { + return true; + } + + /** + * Is the SQL Integrity Enhancement Facility supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsIntegrityEnhancementFacility() throws SQLException { + if (!this.conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_overrideSupportsIntegrityEnhancementFacility).getValue()) { + return false; + } + + return true; + } + + /** + * Is the escape character in "LIKE" clauses supported? A JDBC compliant + * driver always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsLikeEscapeClause() throws SQLException { + return true; + } + + /** + * Is there limited support for outer joins? (This will be true if + * supportFullOuterJoins is true.) + * + * @return true if so + * @throws SQLException + */ + public boolean supportsLimitedOuterJoins() throws SQLException { + return true; + } + + /** + * Is the ODBC Minimum SQL grammar supported? All JDBC compliant drivers + * must return true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsMinimumSQLGrammar() throws SQLException { + return true; + } + + /** + * Does the database support mixed case unquoted SQL identifiers? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsMixedCaseIdentifiers() throws SQLException { + return !this.conn.lowerCaseTableNames(); + } + + /** + * Does the database support mixed case quoted SQL identifiers? A JDBC + * compliant driver will always return true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException { + return !this.conn.lowerCaseTableNames(); + } + + public boolean supportsMultipleOpenResults() throws SQLException { + return true; + } + + /** + * Are multiple ResultSets from a single execute supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsMultipleResultSets() throws SQLException { + return true; + } + + /** + * Can we have multiple transactions open at once (on different + * connections)? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsMultipleTransactions() throws SQLException { + return true; + } + + public boolean supportsNamedParameters() throws SQLException { + return false; + } + + /** + * Can columns be defined as non-nullable? A JDBC compliant driver always + * returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsNonNullableColumns() throws SQLException { + return true; + } + + /** + * Can cursors remain open across commits? + * + * @return true if so + * @throws SQLException + * if a database access error occurs + */ + public boolean supportsOpenCursorsAcrossCommit() throws SQLException { + return false; + } + + /** + * Can cursors remain open across rollbacks? + * + * @return true if so + * @throws SQLException + * if an error occurs + */ + public boolean supportsOpenCursorsAcrossRollback() throws SQLException { + return false; + } + + /** + * Can statements remain open across commits? + * + * @return true if so + * @throws SQLException + * if an error occurs + */ + public boolean supportsOpenStatementsAcrossCommit() throws SQLException { + return false; + } + + /** + * Can statements remain open across rollbacks? + * + * @return true if so + * @throws SQLException + * if an error occurs + */ + public boolean supportsOpenStatementsAcrossRollback() throws SQLException { + return false; + } + + /** + * Can an "ORDER BY" clause use columns not in the SELECT? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsOrderByUnrelated() throws SQLException { + return false; + } + + /** + * Is some form of outer join supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsOuterJoins() throws SQLException { + return true; + } + + /** + * Is positioned DELETE supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsPositionedDelete() throws SQLException { + return false; + } + + /** + * Is positioned UPDATE supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsPositionedUpdate() throws SQLException { + return false; + } + + /** + * JDBC 2.0 Does the database support the concurrency type in combination + * with the given result set type? + * + * @param type + * defined in java.sql.ResultSet + * @param concurrency + * type defined in java.sql.ResultSet + * @return true if so + * @exception SQLException + * if a database-access error occurs. + * @see JdbcConnection + */ + public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException { + switch (type) { + case ResultSet.TYPE_SCROLL_INSENSITIVE: + if ((concurrency == ResultSet.CONCUR_READ_ONLY) || (concurrency == ResultSet.CONCUR_UPDATABLE)) { + return true; + } + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.20"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + + case ResultSet.TYPE_FORWARD_ONLY: + if ((concurrency == ResultSet.CONCUR_READ_ONLY) || (concurrency == ResultSet.CONCUR_UPDATABLE)) { + return true; + } + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.20"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + + case ResultSet.TYPE_SCROLL_SENSITIVE: + return false; + default: + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.20"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + } + + public boolean supportsResultSetHoldability(int holdability) throws SQLException { + return (holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT); + } + + /** + * JDBC 2.0 Does the database support the given result set type? + * + * @param type + * defined in java.sql.ResultSet + * @return true if so + * @exception SQLException + * if a database-access error occurs. + * @see JdbcConnection + */ + public boolean supportsResultSetType(int type) throws SQLException { + return (type == ResultSet.TYPE_SCROLL_INSENSITIVE); + } + + public boolean supportsSavepoints() throws SQLException { + return true; + } + + /** + * Can a schema name be used in a data manipulation statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSchemasInDataManipulation() throws SQLException { + return false; + } + + /** + * Can a schema name be used in an index definition statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSchemasInIndexDefinitions() throws SQLException { + return false; + } + + /** + * Can a schema name be used in a privilege definition statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException { + return false; + } + + /** + * Can a schema name be used in a procedure call statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSchemasInProcedureCalls() throws SQLException { + return false; + } + + /** + * Can a schema name be used in a table definition statement? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSchemasInTableDefinitions() throws SQLException { + return false; + } + + /** + * Is SELECT for UPDATE supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSelectForUpdate() throws SQLException { + return true; + } + + public boolean supportsStatementPooling() throws SQLException { + return false; + } + + /** + * Are stored procedure calls using the stored procedure escape syntax + * supported? + * + * @return true if so + * @throws SQLException + */ + public boolean supportsStoredProcedures() throws SQLException { + return true; + } + + /** + * Are subqueries in comparison expressions supported? A JDBC compliant + * driver always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSubqueriesInComparisons() throws SQLException { + return true; + } + + /** + * Are subqueries in exists expressions supported? A JDBC compliant driver + * always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSubqueriesInExists() throws SQLException { + return true; + } + + /** + * Are subqueries in "in" statements supported? A JDBC compliant driver + * always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSubqueriesInIns() throws SQLException { + return true; + } + + /** + * Are subqueries in quantified expressions supported? A JDBC compliant + * driver always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsSubqueriesInQuantifieds() throws SQLException { + return true; + } + + /** + * Are table correlation names supported? A JDBC compliant driver always + * returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsTableCorrelationNames() throws SQLException { + return true; + } + + /** + * Does the database support the given transaction isolation level? + * + * @param level + * the values are defined in java.sql.Connection + * @return true if so + * @throws SQLException + * if a database access error occurs + * @see JdbcConnection + */ + public boolean supportsTransactionIsolationLevel(int level) throws SQLException { + switch (level) { + case java.sql.Connection.TRANSACTION_READ_COMMITTED: + case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED: + case java.sql.Connection.TRANSACTION_REPEATABLE_READ: + case java.sql.Connection.TRANSACTION_SERIALIZABLE: + return true; + + default: + return false; + } + } + + /** + * Are transactions supported? If not, commit is a noop and the isolation + * level is TRANSACTION_NONE. + * + * @return true if transactions are supported + * @throws SQLException + */ + public boolean supportsTransactions() throws SQLException { + return true; + } + + /** + * Is SQL UNION supported? A JDBC compliant driver always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsUnion() throws SQLException { + return true; + } + + /** + * Is SQL UNION ALL supported? A JDBC compliant driver always returns true. + * + * @return true if so + * @throws SQLException + */ + public boolean supportsUnionAll() throws SQLException { + return true; + } + + /** + * JDBC 2.0 Determine whether or not a visible row update can be detected by + * calling ResultSet.rowUpdated(). + * + * @param type + * set type, i.e. ResultSet.TYPE_XXX + * @return true if changes are detected by the resultset type + * @exception SQLException + * if a database-access error occurs. + */ + public boolean updatesAreDetected(int type) throws SQLException { + return false; + } + + /** + * Does the database use a file for each table? + * + * @return true if the database uses a local file for each table + * @throws SQLException + */ + public boolean usesLocalFilePerTable() throws SQLException { + return false; + } + + /** + * Does the database store tables in a local file? + * + * @return true if so + * @throws SQLException + */ + public boolean usesLocalFiles() throws SQLException { + return false; + } + + public ResultSet getClientInfoProperties() throws SQLException { + // We don't have any built-ins, we actually support whatever the client wants to provide, however we don't have a way to express this with the interface + // given + Field[] fields = new Field[4]; + fields[0] = new Field("", "NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 255); + fields[1] = new Field("", "MAX_LEN", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 10); + fields[2] = new Field("", "DEFAULT_VALUE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 255); + fields[3] = new Field("", "DESCRIPTION", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 255); + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(new ArrayList(), new DefaultColumnDefinition(fields))); + } + + public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) throws SQLException { + Field[] fields = createFunctionColumnsFields(); + + return getProcedureOrFunctionColumns(fields, catalog, schemaPattern, functionNamePattern, columnNamePattern, false, true); + } + + protected Field[] createFunctionColumnsFields() { + Field[] fields = { new Field("", "FUNCTION_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 512), + new Field("", "FUNCTION_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 512), + new Field("", "FUNCTION_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 512), + new Field("", "COLUMN_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 512), + new Field("", "COLUMN_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 64), + new Field("", "DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 6), + new Field("", "TYPE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 64), + new Field("", "PRECISION", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12), + new Field("", "LENGTH", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12), + new Field("", "SCALE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 12), + new Field("", "RADIX", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 6), + new Field("", "NULLABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 6), + new Field("", "REMARKS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 512), + new Field("", "CHAR_OCTET_LENGTH", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 32), + new Field("", "ORDINAL_POSITION", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 32), + new Field("", "IS_NULLABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 12), + new Field("", "SPECIFIC_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 64) }; + return fields; + } + + public java.sql.ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException { + Field[] fields = new Field[6]; + fields[0] = new Field("", "FUNCTION_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[1] = new Field("", "FUNCTION_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[2] = new Field("", "FUNCTION_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[3] = new Field("", "REMARKS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + fields[4] = new Field("", "FUNCTION_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.SMALLINT, 6); + fields[5] = new Field("", "SPECIFIC_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.CHAR, 255); + + return getProceduresAndOrFunctions(fields, catalog, schemaPattern, functionNamePattern, false, true); + } + + public boolean providesQueryObjectGenerator() throws SQLException { + return false; + } + + /** + * @param catalog + * @param schemaPattern + * @throws SQLException + */ + public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { + Field[] fields = { new Field("", "TABLE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 255), + new Field("", "TABLE_CATALOG", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 255) }; + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(new ArrayList(), new DefaultColumnDefinition(fields))); + } + + public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { + return true; + } + + /** + * Get a prepared statement to query information_schema tables. + * + * @return PreparedStatement + * @throws SQLException + */ + protected java.sql.PreparedStatement prepareMetaDataSafeStatement(String sql) throws SQLException { + // Can't use server-side here as we coerce a lot of types to match the spec. + java.sql.PreparedStatement pStmt = this.conn.clientPrepareStatement(sql); + + if (pStmt.getMaxRows() != 0) { + pStmt.setMaxRows(0); + } + + ((com.mysql.cj.jdbc.JdbcStatement) pStmt).setHoldResultsOpenOverClose(true); + + return pStmt; + } + + public java.sql.ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException { + Field[] fields = { new Field("", "TABLE_CAT", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 512), + new Field("", "TABLE_SCHEM", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 512), + new Field("", "TABLE_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 512), + new Field("", "COLUMN_NAME", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 512), + new Field("", "DATA_TYPE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12), + new Field("", "COLUMN_SIZE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12), + new Field("", "DECIMAL_DIGITS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12), + new Field("", "NUM_PREC_RADIX", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12), + new Field("", "COLUMN_USAGE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 512), + new Field("", "REMARKS", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 512), + new Field("", "CHAR_OCTET_LENGTH", this.metadataCollationIndex, this.metadataEncoding, MysqlType.INT, 12), + new Field("", "IS_NULLABLE", this.metadataCollationIndex, this.metadataEncoding, MysqlType.VARCHAR, 512) }; + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(new ArrayList(), new DefaultColumnDefinition(fields))); + } + + public boolean generatedKeyAlwaysReturned() throws SQLException { + return true; + } + + /** + * Returns an object that implements the given interface to allow access to non-standard methods, + * or standard methods not exposed by the proxy. + * The result may be either the object found to implement the interface or a proxy for that object. + * If the receiver implements the interface then that is the object. If the receiver is a wrapper + * and the wrapped object implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped object. If the receiver is not a + * wrapper and does not implement the interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping + // anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("Common.UnableToUnwrap", new Object[] { iface.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.conn.getExceptionInterceptor()); + } + } + + /** + * Returns true if this either implements the interface argument or is directly or indirectly a wrapper + * for an object that does. Returns false otherwise. If this implements the interface then return true, + * else if this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped + * object. If this does not implement the interface and is not a wrapper, return false. + * This method should be implemented as a low-cost operation compared to unwrap so that + * callers can use this method to avoid expensive unwrap calls that may fail. If this method + * returns true then calling unwrap with the same argument should succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a wrapper + * for an object with the given interface. + * @since 1.6 + */ + public boolean isWrapperFor(Class iface) throws SQLException { + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + public RowIdLifetime getRowIdLifetime() throws SQLException { + return RowIdLifetime.ROWID_UNSUPPORTED; + } + + public boolean autoCommitFailureClosesAllResultSets() throws SQLException { + return false; + } + + public String getMetadataEncoding() { + return this.metadataEncoding; + } + + public void setMetadataEncoding(String metadataEncoding) { + this.metadataEncoding = metadataEncoding; + } + + public int getMetadataCollationIndex() { + return this.metadataCollationIndex; + } + + public void setMetadataCollationIndex(int metadataCollationIndex) { + this.metadataCollationIndex = metadataCollationIndex; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/DatabaseMetaDataUsingInfoSchema.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/DatabaseMetaDataUsingInfoSchema.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/DatabaseMetaDataUsingInfoSchema.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,1155 @@ +/* + * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +import com.mysql.cj.Messages; +import com.mysql.cj.MysqlType; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.result.ResultSetFactory; +import com.mysql.cj.result.Field; +import com.mysql.cj.util.StringUtils; + +/** + * DatabaseMetaData implementation that uses INFORMATION_SCHEMA + */ +public class DatabaseMetaDataUsingInfoSchema extends DatabaseMetaData { + + protected enum FunctionConstant { + // COLUMN_TYPE values + FUNCTION_COLUMN_UNKNOWN, FUNCTION_COLUMN_IN, FUNCTION_COLUMN_INOUT, FUNCTION_COLUMN_OUT, FUNCTION_COLUMN_RETURN, FUNCTION_COLUMN_RESULT, + // NULLABLE values + FUNCTION_NO_NULLS, FUNCTION_NULLABLE, FUNCTION_NULLABLE_UNKNOWN; + } + + protected DatabaseMetaDataUsingInfoSchema(JdbcConnection connToSet, String databaseToSet, ResultSetFactory resultSetFactory) throws SQLException { + super(connToSet, databaseToSet, resultSetFactory); + } + + protected ResultSet executeMetadataQuery(java.sql.PreparedStatement pStmt) throws SQLException { + ResultSet rs = pStmt.executeQuery(); + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).setOwningStatement(null); + + return rs; + } + + @Override + public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException { + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + catalog = this.pedantic ? catalog : StringUtils.unQuoteIdentifier(catalog, this.quotedId); + + StringBuilder sqlBuf = new StringBuilder("SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME,"); + sqlBuf.append(" COLUMN_NAME, NULL AS GRANTOR, GRANTEE, PRIVILEGE_TYPE AS PRIVILEGE, IS_GRANTABLE FROM INFORMATION_SCHEMA.COLUMN_PRIVILEGES WHERE"); + if (catalog != null) { + sqlBuf.append(" TABLE_SCHEMA LIKE ? AND"); + } + + sqlBuf.append(" TABLE_NAME =?"); + if (columnNamePattern != null) { + sqlBuf.append(" AND COLUMN_NAME LIKE ?"); + } + sqlBuf.append(" ORDER BY COLUMN_NAME, PRIVILEGE_TYPE"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + int nextId = 1; + pStmt.setString(nextId++, catalog); + pStmt.setString(nextId++, table); + if (columnNamePattern != null) { + pStmt.setString(nextId, columnNamePattern); + } + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).getColumnDefinition() + .setFields(new Field[] { new Field("", "TABLE_CAT", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 64), + new Field("", "TABLE_SCHEM", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 1), + new Field("", "TABLE_NAME", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 64), + new Field("", "COLUMN_NAME", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 64), + new Field("", "GRANTOR", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 77), + new Field("", "GRANTEE", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 77), + new Field("", "PRIVILEGE", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 64), + new Field("", "IS_GRANTABLE", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 3) }); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + @Override + public ResultSet getColumns(String catalog, String schemaPattern, String tableName, String columnNamePattern) throws SQLException { + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + catalog = this.pedantic ? catalog : StringUtils.unQuoteIdentifier(catalog, this.quotedId); + + StringBuilder sqlBuf = new StringBuilder("SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, COLUMN_NAME,"); + + appendJdbcTypeMappingQuery(sqlBuf, "DATA_TYPE", "COLUMN_TYPE"); + sqlBuf.append(" AS DATA_TYPE, "); + + sqlBuf.append("UPPER(CASE"); + sqlBuf.append( + " WHEN LOCATE('UNSIGNED', UPPER(COLUMN_TYPE)) != 0 AND LOCATE('UNSIGNED', UPPER(DATA_TYPE)) = 0 AND LOCATE('SET', UPPER(DATA_TYPE)) <> 1 AND LOCATE('ENUM', UPPER(DATA_TYPE)) <> 1 THEN CONCAT(DATA_TYPE, ' UNSIGNED')"); + if (this.tinyInt1isBit) { + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='TINYINT' THEN CASE"); + if (this.transformedBitIsBoolean) { + sqlBuf.append(" WHEN LOCATE('(1)', COLUMN_TYPE) != 0 THEN 'BOOLEAN'"); + } else { + sqlBuf.append(" WHEN LOCATE('(1)', COLUMN_TYPE) != 0 THEN 'BIT'"); + } + sqlBuf.append(" WHEN LOCATE('UNSIGNED', UPPER(COLUMN_TYPE)) != 0 AND LOCATE('UNSIGNED', UPPER(DATA_TYPE)) = 0 THEN 'TINYINT UNSIGNED'"); + sqlBuf.append(" ELSE DATA_TYPE END "); + } + + // spatial data types + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='POINT' THEN 'GEOMETRY'"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='LINESTRING' THEN 'GEOMETRY'"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='POLYGON' THEN 'GEOMETRY'"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='MULTIPOINT' THEN 'GEOMETRY'"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='MULTILINESTRING' THEN 'GEOMETRY'"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='MULTIPOLYGON' THEN 'GEOMETRY'"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='GEOMETRYCOLLECTION' THEN 'GEOMETRY'"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='GEOMCOLLECTION' THEN 'GEOMETRY'"); + + sqlBuf.append(" ELSE UPPER(DATA_TYPE) END) AS TYPE_NAME,"); + + sqlBuf.append("UPPER(CASE"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='DATE' THEN 10"); // supported range is '1000-01-01' to '9999-12-31' + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='TIME' THEN 16"); // supported range is '-838:59:59.000000' to '838:59:59.000000' + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='DATETIME' THEN 26"); // supported range is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999' + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='TIMESTAMP' THEN 26"); // supported range is '1970-01-01 00:00:01.000000' UTC to '2038-01-19 03:14:07.999999' UTC + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='YEAR' THEN 4"); + if (this.tinyInt1isBit) { + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='TINYINT' AND LOCATE('(1)', COLUMN_TYPE) != 0 THEN 1"); + } + // workaround for Bug#69042 (16712664), "MEDIUMINT PRECISION/TYPE INCORRECT IN INFORMATION_SCHEMA.COLUMNS", I_S bug returns NUMERIC_PRECISION=7 for MEDIUMINT UNSIGNED when it must be 8. + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='MEDIUMINT' AND LOCATE('UNSIGNED', UPPER(COLUMN_TYPE)) != 0 THEN 8"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='JSON' THEN 1073741824"); // JSON columns is limited to the value of the max_allowed_packet system variable (max value 1073741824) + + // spatial data types + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='GEOMETRY' THEN 65535"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='POINT' THEN 65535"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='LINESTRING' THEN 65535"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='POLYGON' THEN 65535"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='MULTIPOINT' THEN 65535"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='MULTILINESTRING' THEN 65535"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='MULTIPOLYGON' THEN 65535"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='GEOMETRYCOLLECTION' THEN 65535"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='GEOMCOLLECTION' THEN 65535"); + + sqlBuf.append(" WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN NUMERIC_PRECISION"); + sqlBuf.append(" WHEN CHARACTER_MAXIMUM_LENGTH > "); + sqlBuf.append(Integer.MAX_VALUE); + sqlBuf.append(" THEN "); + sqlBuf.append(Integer.MAX_VALUE); + sqlBuf.append(" ELSE CHARACTER_MAXIMUM_LENGTH"); + sqlBuf.append(" END) AS COLUMN_SIZE,"); + + sqlBuf.append(maxBufferSize); + sqlBuf.append(" AS BUFFER_LENGTH,"); + + sqlBuf.append("UPPER(CASE"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='DECIMAL' THEN NUMERIC_SCALE"); + sqlBuf.append(" WHEN UPPER(DATA_TYPE)='FLOAT' OR UPPER(DATA_TYPE)='DOUBLE' THEN"); + sqlBuf.append(" CASE WHEN NUMERIC_SCALE IS NULL THEN 0"); + sqlBuf.append(" ELSE NUMERIC_SCALE END"); + sqlBuf.append(" ELSE NULL END) AS DECIMAL_DIGITS,"); + + sqlBuf.append("10 AS NUM_PREC_RADIX,"); + + sqlBuf.append("UPPER(CASE"); + sqlBuf.append(" WHEN IS_NULLABLE='NO' THEN "); + sqlBuf.append(columnNoNulls); + sqlBuf.append(" ELSE CASE WHEN IS_NULLABLE='YES' THEN "); + sqlBuf.append(columnNullable); + sqlBuf.append(" ELSE "); + sqlBuf.append(columnNullableUnknown); + sqlBuf.append(" END END) AS NULLABLE,"); + + sqlBuf.append("COLUMN_COMMENT AS REMARKS,"); + sqlBuf.append("COLUMN_DEFAULT AS COLUMN_DEF,"); + sqlBuf.append("0 AS SQL_DATA_TYPE,"); + sqlBuf.append("0 AS SQL_DATETIME_SUB,"); + + sqlBuf.append("CASE WHEN CHARACTER_OCTET_LENGTH > "); + sqlBuf.append(Integer.MAX_VALUE); + sqlBuf.append(" THEN "); + sqlBuf.append(Integer.MAX_VALUE); + sqlBuf.append(" ELSE CHARACTER_OCTET_LENGTH END AS CHAR_OCTET_LENGTH,"); + + sqlBuf.append("ORDINAL_POSITION, IS_NULLABLE, NULL AS SCOPE_CATALOG, NULL AS SCOPE_SCHEMA, NULL AS SCOPE_TABLE, NULL AS SOURCE_DATA_TYPE,"); + sqlBuf.append("IF (EXTRA LIKE '%auto_increment%','YES','NO') AS IS_AUTOINCREMENT, "); + sqlBuf.append("IF (EXTRA LIKE '%GENERATED%','YES','NO') AS IS_GENERATEDCOLUMN "); + + sqlBuf.append("FROM INFORMATION_SCHEMA.COLUMNS"); + + StringBuilder conditionBuf = new StringBuilder(); + + if (catalog != null) { + conditionBuf.append( + "information_schema".equalsIgnoreCase(catalog) || "performance_schema".equalsIgnoreCase(catalog) || !StringUtils.hasWildcards(catalog) + ? " TABLE_SCHEMA = ?" : " TABLE_SCHEMA LIKE ?"); + } + if (tableName != null) { + if (conditionBuf.length() > 0) { + conditionBuf.append(" AND"); + } + conditionBuf.append(StringUtils.hasWildcards(tableName) ? " TABLE_NAME LIKE ?" : " TABLE_NAME = ?"); + } + if (columnNamePattern != null) { + if (conditionBuf.length() > 0) { + conditionBuf.append(" AND"); + } + conditionBuf.append(StringUtils.hasWildcards(columnNamePattern) ? " COLUMN_NAME LIKE ?" : " COLUMN_NAME = ?"); + } + + if (conditionBuf.length() > 0) { + sqlBuf.append(" WHERE"); + } + sqlBuf.append(conditionBuf); + sqlBuf.append(" ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + + int nextId = 1; + if (catalog != null) { + pStmt.setString(nextId++, catalog); + } + if (tableName != null) { + pStmt.setString(nextId++, tableName); + } + if (columnNamePattern != null) { + pStmt.setString(nextId, columnNamePattern); + } + + ResultSet rs = executeMetadataQuery(pStmt); + + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).getColumnDefinition().setFields(createColumnsFields()); + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + @Override + public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, + String foreignTable) throws SQLException { + if (primaryTable == null) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + if (primaryCatalog == null && this.nullCatalogMeansCurrent) { + primaryCatalog = this.database; + } + + if (foreignCatalog == null && this.nullCatalogMeansCurrent) { + foreignCatalog = this.database; + } + + primaryCatalog = this.pedantic ? primaryCatalog : StringUtils.unQuoteIdentifier(primaryCatalog, this.quotedId); + foreignCatalog = this.pedantic ? foreignCatalog : StringUtils.unQuoteIdentifier(foreignCatalog, this.quotedId); + + StringBuilder sqlBuf = new StringBuilder( + "SELECT A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT,NULL AS PKTABLE_SCHEM, A.REFERENCED_TABLE_NAME AS PKTABLE_NAME,"); + sqlBuf.append(" A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME, A.TABLE_SCHEMA AS FKTABLE_CAT, NULL AS FKTABLE_SCHEM,"); + sqlBuf.append(" A.TABLE_NAME AS FKTABLE_NAME, A.COLUMN_NAME AS FKCOLUMN_NAME, A.ORDINAL_POSITION AS KEY_SEQ,"); + sqlBuf.append(generateUpdateRuleClause()); + sqlBuf.append(" AS UPDATE_RULE,"); + sqlBuf.append(generateDeleteRuleClause()); + sqlBuf.append(" AS DELETE_RULE, A.CONSTRAINT_NAME AS FK_NAME,"); + sqlBuf.append(" (SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = A.REFERENCED_TABLE_SCHEMA"); + sqlBuf.append(" AND TABLE_NAME = A.REFERENCED_TABLE_NAME AND CONSTRAINT_TYPE IN ('UNIQUE','PRIMARY KEY') LIMIT 1) AS PK_NAME, "); + sqlBuf.append(importedKeyNotDeferrable); + sqlBuf.append(" AS DEFERRABILITY FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE A"); + sqlBuf.append(" JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS B USING (TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_NAME) "); + sqlBuf.append(generateOptionalRefContraintsJoin()); + sqlBuf.append("WHERE B.CONSTRAINT_TYPE = 'FOREIGN KEY'"); + if (primaryCatalog != null) { + sqlBuf.append(" AND A.REFERENCED_TABLE_SCHEMA LIKE ?"); + } + sqlBuf.append(" AND A.REFERENCED_TABLE_NAME=?"); + if (foreignCatalog != null) { + sqlBuf.append(" AND A.TABLE_SCHEMA LIKE ?"); + } + sqlBuf.append(" AND A.TABLE_NAME=?"); + sqlBuf.append(" ORDER BY A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + int nextId = 1; + if (primaryCatalog != null) { + pStmt.setString(nextId++, primaryCatalog); + } + pStmt.setString(nextId++, primaryTable); + if (foreignCatalog != null) { + pStmt.setString(nextId++, foreignCatalog); + } + pStmt.setString(nextId, foreignTable); + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).getColumnDefinition().setFields(createFkMetadataFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + @Override + public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException { + // TODO: Can't determine actions using INFORMATION_SCHEMA yet... + + if (table == null) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + catalog = this.pedantic ? catalog : StringUtils.unQuoteIdentifier(catalog, this.quotedId); + + //CASCADE, SET NULL, SET DEFAULT, RESTRICT, NO ACTION + + StringBuilder sqlBuf = new StringBuilder( + "SELECT A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT, NULL AS PKTABLE_SCHEM, A.REFERENCED_TABLE_NAME AS PKTABLE_NAME,"); + sqlBuf.append(" A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME, A.TABLE_SCHEMA AS FKTABLE_CAT, NULL AS FKTABLE_SCHEM, A.TABLE_NAME AS FKTABLE_NAME,"); + sqlBuf.append(" A.COLUMN_NAME AS FKCOLUMN_NAME, A.ORDINAL_POSITION AS KEY_SEQ,"); + sqlBuf.append(generateUpdateRuleClause()); + sqlBuf.append(" AS UPDATE_RULE,"); + sqlBuf.append(generateDeleteRuleClause()); + sqlBuf.append(" AS DELETE_RULE, A.CONSTRAINT_NAME AS FK_NAME, (SELECT CONSTRAINT_NAME FROM"); + sqlBuf.append(" INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = A.REFERENCED_TABLE_SCHEMA AND"); + sqlBuf.append(" TABLE_NAME = A.REFERENCED_TABLE_NAME AND CONSTRAINT_TYPE IN ('UNIQUE','PRIMARY KEY') LIMIT 1) AS PK_NAME,"); + sqlBuf.append(importedKeyNotDeferrable); + sqlBuf.append(" AS DEFERRABILITY FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE A"); + sqlBuf.append(" JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS B USING (TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_NAME) "); + sqlBuf.append(generateOptionalRefContraintsJoin()); + sqlBuf.append(" WHERE B.CONSTRAINT_TYPE = 'FOREIGN KEY'"); + if (catalog != null) { + sqlBuf.append(" AND A.REFERENCED_TABLE_SCHEMA LIKE ?"); + } + sqlBuf.append(" AND A.REFERENCED_TABLE_NAME=?"); + sqlBuf.append(" ORDER BY A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + int nextId = 1; + + if (catalog != null) { + pStmt.setString(nextId++, catalog); + } + pStmt.setString(nextId, table); + + ResultSet rs = executeMetadataQuery(pStmt); + + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).getColumnDefinition().setFields(createFkMetadataFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + + } + + private String generateOptionalRefContraintsJoin() { + return ("JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R ON (R.CONSTRAINT_NAME = B.CONSTRAINT_NAME " + + "AND R.TABLE_NAME = B.TABLE_NAME AND R.CONSTRAINT_SCHEMA = B.TABLE_SCHEMA) "); + } + + private String generateDeleteRuleClause() { + return ("CASE WHEN R.DELETE_RULE='CASCADE' THEN " + String.valueOf(importedKeyCascade) + " WHEN R.DELETE_RULE='SET NULL' THEN " + + String.valueOf(importedKeySetNull) + " WHEN R.DELETE_RULE='SET DEFAULT' THEN " + String.valueOf(importedKeySetDefault) + + " WHEN R.DELETE_RULE='RESTRICT' THEN " + String.valueOf(importedKeyRestrict) + " WHEN R.DELETE_RULE='NO ACTION' THEN " + + String.valueOf(importedKeyNoAction) + " ELSE " + String.valueOf(importedKeyNoAction) + " END "); + } + + private String generateUpdateRuleClause() { + return ("CASE WHEN R.UPDATE_RULE='CASCADE' THEN " + String.valueOf(importedKeyCascade) + " WHEN R.UPDATE_RULE='SET NULL' THEN " + + String.valueOf(importedKeySetNull) + " WHEN R.UPDATE_RULE='SET DEFAULT' THEN " + String.valueOf(importedKeySetDefault) + + " WHEN R.UPDATE_RULE='RESTRICT' THEN " + String.valueOf(importedKeyRestrict) + " WHEN R.UPDATE_RULE='NO ACTION' THEN " + + String.valueOf(importedKeyNoAction) + " ELSE " + String.valueOf(importedKeyNoAction) + " END "); + } + + @Override + public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException { + if (table == null) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + catalog = this.pedantic ? catalog : StringUtils.unQuoteIdentifier(catalog, this.quotedId); + + StringBuilder sqlBuf = new StringBuilder("SELECT A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT, NULL AS PKTABLE_SCHEM,"); + sqlBuf.append(" A.REFERENCED_TABLE_NAME AS PKTABLE_NAME, A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME, A.TABLE_SCHEMA AS FKTABLE_CAT,"); + sqlBuf.append(" NULL AS FKTABLE_SCHEM, A.TABLE_NAME AS FKTABLE_NAME, A.COLUMN_NAME AS FKCOLUMN_NAME, A.ORDINAL_POSITION AS KEY_SEQ,"); + sqlBuf.append(generateUpdateRuleClause()); + sqlBuf.append(" AS UPDATE_RULE,"); + sqlBuf.append(generateDeleteRuleClause()); + sqlBuf.append(" AS DELETE_RULE, A.CONSTRAINT_NAME AS FK_NAME, (SELECT CONSTRAINT_NAME FROM"); + sqlBuf.append(" INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = A.REFERENCED_TABLE_SCHEMA AND"); + sqlBuf.append(" TABLE_NAME = A.REFERENCED_TABLE_NAME AND CONSTRAINT_TYPE IN ('UNIQUE','PRIMARY KEY') LIMIT 1) AS PK_NAME,"); + sqlBuf.append(importedKeyNotDeferrable); + sqlBuf.append(" AS DEFERRABILITY FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE A"); + sqlBuf.append(" JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS B USING (CONSTRAINT_NAME, TABLE_NAME) "); + sqlBuf.append(generateOptionalRefContraintsJoin()); + sqlBuf.append("WHERE B.CONSTRAINT_TYPE = 'FOREIGN KEY'"); + if (catalog != null) { + sqlBuf.append(" AND A.TABLE_SCHEMA LIKE ?"); + } + sqlBuf.append(" AND A.TABLE_NAME=?"); + sqlBuf.append(" AND A.REFERENCED_TABLE_SCHEMA IS NOT NULL"); + sqlBuf.append(" ORDER BY A.REFERENCED_TABLE_SCHEMA, A.REFERENCED_TABLE_NAME, A.ORDINAL_POSITION"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + int nextId = 1; + if (catalog != null) { + pStmt.setString(nextId++, catalog); + } + pStmt.setString(nextId, table); + + ResultSet rs = executeMetadataQuery(pStmt); + + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).getColumnDefinition().setFields(createFkMetadataFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + @Override + public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException { + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + catalog = this.pedantic ? catalog : StringUtils.unQuoteIdentifier(catalog, this.quotedId); + + StringBuilder sqlBuf = new StringBuilder("SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, NON_UNIQUE,"); + sqlBuf.append("TABLE_SCHEMA AS INDEX_QUALIFIER, INDEX_NAME,"); + sqlBuf.append(tableIndexOther); + sqlBuf.append(" AS TYPE, SEQ_IN_INDEX AS ORDINAL_POSITION, COLUMN_NAME,"); + sqlBuf.append("COLLATION AS ASC_OR_DESC, CARDINALITY, NULL AS PAGES, NULL AS FILTER_CONDITION FROM INFORMATION_SCHEMA.STATISTICS WHERE"); + if (catalog != null) { + sqlBuf.append(" TABLE_SCHEMA LIKE ? AND"); + } + sqlBuf.append(" TABLE_NAME LIKE ?"); + + if (unique) { + sqlBuf.append(" AND NON_UNIQUE=0 "); + } + sqlBuf.append("ORDER BY NON_UNIQUE, INDEX_NAME, SEQ_IN_INDEX"); + + java.sql.PreparedStatement pStmt = null; + + try { + + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + int nextId = 1; + if (catalog != null) { + pStmt.setString(nextId++, catalog); + } + pStmt.setString(nextId, table); + + ResultSet rs = executeMetadataQuery(pStmt); + + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).getColumnDefinition().setFields(createIndexInfoFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + @Override + public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException { + + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + if (table == null) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + catalog = this.pedantic ? catalog : StringUtils.unQuoteIdentifier(catalog, this.quotedId); + + StringBuilder sqlBuf = new StringBuilder("SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME,"); + sqlBuf.append(" COLUMN_NAME, SEQ_IN_INDEX AS KEY_SEQ, 'PRIMARY' AS PK_NAME FROM INFORMATION_SCHEMA.STATISTICS WHERE"); + if (catalog != null) { + sqlBuf.append(" TABLE_SCHEMA LIKE ? AND"); + } + sqlBuf.append(" TABLE_NAME LIKE ?"); + sqlBuf.append(" AND INDEX_NAME='PRIMARY' ORDER BY TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + int nextId = 1; + if (catalog != null) { + pStmt.setString(nextId++, catalog); + } + pStmt.setString(nextId, table); + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).getColumnDefinition() + .setFields(new Field[] { new Field("", "TABLE_CAT", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 255), + new Field("", "TABLE_SCHEM", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 0), + new Field("", "TABLE_NAME", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 255), + new Field("", "COLUMN_NAME", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 32), + new Field("", "KEY_SEQ", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.SMALLINT, 5), + new Field("", "PK_NAME", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 32) }); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + @Override + public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { + + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + catalog = this.pedantic ? catalog : StringUtils.unQuoteIdentifier(catalog, this.quotedId); + + StringBuilder sqlBuf = new StringBuilder( + "SELECT ROUTINE_SCHEMA AS PROCEDURE_CAT, NULL AS PROCEDURE_SCHEM, ROUTINE_NAME AS PROCEDURE_NAME, NULL AS RESERVED_1,"); + sqlBuf.append(" NULL AS RESERVED_2, NULL AS RESERVED_3, ROUTINE_COMMENT AS REMARKS, CASE WHEN ROUTINE_TYPE = 'PROCEDURE' THEN "); + sqlBuf.append(procedureNoResult); + sqlBuf.append(" WHEN ROUTINE_TYPE='FUNCTION' THEN "); + sqlBuf.append(procedureReturnsResult); + sqlBuf.append(" ELSE "); + sqlBuf.append(procedureResultUnknown); + sqlBuf.append(" END AS PROCEDURE_TYPE, ROUTINE_NAME AS SPECIFIC_NAME FROM INFORMATION_SCHEMA.ROUTINES"); + + StringBuilder conditionBuf = new StringBuilder(); + if (!this.conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_getProceduresReturnsFunctions).getValue()) { + conditionBuf.append(" ROUTINE_TYPE = 'PROCEDURE'"); + } + if (catalog != null) { + if (conditionBuf.length() > 0) { + conditionBuf.append(" AND"); + } + conditionBuf.append(" ROUTINE_SCHEMA LIKE ?"); + } + if (procedureNamePattern != null) { + if (conditionBuf.length() > 0) { + conditionBuf.append(" AND"); + } + conditionBuf.append(" ROUTINE_NAME LIKE ?"); + } + + if (conditionBuf.length() > 0) { + sqlBuf.append(" WHERE"); + sqlBuf.append(conditionBuf); + } + sqlBuf.append(" ORDER BY ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + int nextId = 1; + if (catalog != null) { + pStmt.setString(nextId++, catalog); + } + if (procedureNamePattern != null) { + pStmt.setString(nextId, procedureNamePattern); + } + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).getColumnDefinition().setFields(createFieldMetadataForGetProcedures()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + @Override + public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException { + + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + catalog = this.pedantic ? catalog : StringUtils.unQuoteIdentifier(catalog, this.quotedId); + + // Here's what we get from MySQL ... + // SPECIFIC_CATALOG NULL + // SPECIFIC_SCHEMA db17 + // SPECIFIC_NAME p + // ORDINAL_POSITION 1 + // PARAMETER_MODE OUT + // PARAMETER_NAME a + // DATA_TYPE int + // CHARACTER_MAXIMUM_LENGTH NULL + // CHARACTER_OCTET_LENGTH NULL + // CHARACTER_SET_NAME NULL + // COLLATION_NAME NULL + // NUMERIC_PRECISION 10 + // NUMERIC_SCALE 0 + // DTD_IDENTIFIER int(11) + StringBuilder sqlBuf = new StringBuilder("SELECT SPECIFIC_SCHEMA AS PROCEDURE_CAT, NULL AS `PROCEDURE_SCHEM`, "); + sqlBuf.append(" SPECIFIC_NAME AS `PROCEDURE_NAME`,"); + sqlBuf.append(" IFNULL(PARAMETER_NAME, '') AS `COLUMN_NAME`,"); + sqlBuf.append(" CASE WHEN PARAMETER_MODE = 'IN' THEN "); + sqlBuf.append(procedureColumnIn); + sqlBuf.append(" WHEN PARAMETER_MODE = 'OUT' THEN "); + sqlBuf.append(procedureColumnOut); + sqlBuf.append(" WHEN PARAMETER_MODE = 'INOUT' THEN "); + sqlBuf.append(procedureColumnInOut); + sqlBuf.append(" WHEN ORDINAL_POSITION = 0 THEN "); + sqlBuf.append(procedureColumnReturn); + sqlBuf.append(" ELSE "); + sqlBuf.append(procedureColumnUnknown); + sqlBuf.append(" END AS `COLUMN_TYPE`, "); + + //DATA_TYPE + appendJdbcTypeMappingQuery(sqlBuf, "DATA_TYPE", "DTD_IDENTIFIER"); + + sqlBuf.append(" AS `DATA_TYPE`, "); + + // TYPE_NAME + sqlBuf.append(" UPPER(CASE WHEN LOCATE('UNSIGNED', UPPER(DATA_TYPE)) != 0 AND LOCATE('UNSIGNED', UPPER(DATA_TYPE)) = 0"); + sqlBuf.append(" THEN CONCAT(DATA_TYPE, ' UNSIGNED') ELSE DATA_TYPE END) AS `TYPE_NAME`,"); + + // PRECISION int => precision + sqlBuf.append(" NUMERIC_PRECISION AS `PRECISION`,"); + + // LENGTH int => length in bytes of data + sqlBuf.append(" CASE WHEN LCASE(DATA_TYPE)='date' THEN 10 WHEN LCASE(DATA_TYPE)='time' THEN 8 WHEN LCASE(DATA_TYPE)='datetime' THEN 19"); + sqlBuf.append(" WHEN LCASE(DATA_TYPE)='timestamp' THEN 19 WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN NUMERIC_PRECISION"); + sqlBuf.append(" WHEN CHARACTER_MAXIMUM_LENGTH > "); + sqlBuf.append(Integer.MAX_VALUE); + sqlBuf.append(" THEN "); + sqlBuf.append(Integer.MAX_VALUE); + sqlBuf.append(" ELSE CHARACTER_MAXIMUM_LENGTH END AS LENGTH,"); + + // SCALE short => scale + sqlBuf.append("NUMERIC_SCALE AS `SCALE`, "); + // RADIX short => radix + sqlBuf.append("10 AS RADIX,"); + + sqlBuf.append(procedureNullable); + sqlBuf.append(" AS `NULLABLE`, NULL AS `REMARKS`, NULL AS `COLUMN_DEF`, NULL AS `SQL_DATA_TYPE`, NULL AS `SQL_DATETIME_SUB`,"); + sqlBuf.append(" CHARACTER_OCTET_LENGTH AS `CHAR_OCTET_LENGTH`, ORDINAL_POSITION, 'YES' AS `IS_NULLABLE`, SPECIFIC_NAME"); + sqlBuf.append(" FROM INFORMATION_SCHEMA.PARAMETERS"); + + StringBuilder conditionBuf = new StringBuilder(); + if (!this.conn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_getProceduresReturnsFunctions).getValue()) { + conditionBuf.append(" ROUTINE_TYPE = 'PROCEDURE'"); + } + if (catalog != null) { + if (conditionBuf.length() > 0) { + conditionBuf.append(" AND"); + } + conditionBuf.append(" SPECIFIC_SCHEMA LIKE ?"); + } + if (procedureNamePattern != null) { + if (conditionBuf.length() > 0) { + conditionBuf.append(" AND"); + } + conditionBuf.append(" SPECIFIC_NAME LIKE ?"); + } + if (columnNamePattern != null) { + if (conditionBuf.length() > 0) { + conditionBuf.append(" AND"); + } + conditionBuf.append(" (PARAMETER_NAME LIKE ? OR PARAMETER_NAME IS NULL)"); + } + + if (conditionBuf.length() > 0) { + sqlBuf.append(" WHERE"); + sqlBuf.append(conditionBuf); + } + sqlBuf.append(" ORDER BY SPECIFIC_SCHEMA, SPECIFIC_NAME, ROUTINE_TYPE, ORDINAL_POSITION"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + + int nextId = 1; + if (catalog != null) { + pStmt.setString(nextId++, catalog); + } + if (procedureNamePattern != null) { + pStmt.setString(nextId++, procedureNamePattern); + } + if (columnNamePattern != null) { + pStmt.setString(nextId, columnNamePattern); + } + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).getColumnDefinition().setFields(createProcedureColumnsFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + @Override + public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) throws SQLException { + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + catalog = this.pedantic ? catalog : StringUtils.unQuoteIdentifier(catalog, this.quotedId); + + if (tableNamePattern != null) { + List parseList = StringUtils.splitDBdotName(tableNamePattern, catalog, this.quotedId, + this.session.getServerSession().isNoBackslashEscapesSet()); + //There *should* be 2 rows, if any. + if (parseList.size() == 2) { + tableNamePattern = parseList.get(1); + } + } + + java.sql.PreparedStatement pStmt = null; + + StringBuilder sqlBuf = new StringBuilder("SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, "); + sqlBuf.append("CASE WHEN TABLE_TYPE='BASE TABLE' THEN CASE WHEN TABLE_SCHEMA = 'mysql' OR TABLE_SCHEMA = 'performance_schema' THEN 'SYSTEM TABLE' "); + sqlBuf.append("ELSE 'TABLE' END WHEN TABLE_TYPE='TEMPORARY' THEN 'LOCAL_TEMPORARY' ELSE TABLE_TYPE END AS TABLE_TYPE, "); + sqlBuf.append("TABLE_COMMENT AS REMARKS, NULL AS TYPE_CAT, NULL AS TYPE_SCHEM, NULL AS TYPE_NAME, NULL AS SELF_REFERENCING_COL_NAME, "); + sqlBuf.append("NULL AS REF_GENERATION FROM INFORMATION_SCHEMA.TABLES WHERE"); + + if (catalog != null) { + sqlBuf.append("information_schema".equalsIgnoreCase(catalog) || "performance_schema".equalsIgnoreCase(catalog) || !StringUtils.hasWildcards(catalog) + ? " TABLE_SCHEMA = ?" : " TABLE_SCHEMA LIKE ?"); + } + + if (tableNamePattern != null) { + if (catalog != null) { + sqlBuf.append(" AND"); + } + sqlBuf.append(StringUtils.hasWildcards(tableNamePattern) ? " TABLE_NAME LIKE ?" : " TABLE_NAME = ?"); + } + if (types != null && types.length > 0) { + sqlBuf.append(" HAVING TABLE_TYPE IN (?,?,?,?,?)"); + } + sqlBuf.append(" ORDER BY TABLE_TYPE, TABLE_SCHEMA, TABLE_NAME"); + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + + int nextId = 1; + if (catalog != null) { + pStmt.setString(nextId++, catalog != null ? catalog : "%"); + } + if (tableNamePattern != null) { + pStmt.setString(nextId++, tableNamePattern); + } + + if (types != null && types.length > 0) { + for (int i = 0; i < 5; i++) { + pStmt.setNull(nextId + i, MysqlType.VARCHAR.getJdbcType()); + } + for (int i = 0; i < types.length; i++) { + TableType tableType = TableType.getTableTypeEqualTo(types[i]); + if (tableType != TableType.UNKNOWN) { + pStmt.setString(nextId++, tableType.getName()); + } + } + } + + ResultSet rs = executeMetadataQuery(pStmt); + + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).setColumnDefinition(createTablesFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + @Override + public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException { + + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + if (table == null) { + throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.2"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + catalog = this.pedantic ? catalog : StringUtils.unQuoteIdentifier(catalog, this.quotedId); + + StringBuilder sqlBuf = new StringBuilder("SELECT NULL AS SCOPE, COLUMN_NAME, "); + appendJdbcTypeMappingQuery(sqlBuf, "DATA_TYPE", "COLUMN_TYPE"); + sqlBuf.append(" AS DATA_TYPE, UPPER(COLUMN_TYPE) AS TYPE_NAME,"); + sqlBuf.append(" CASE WHEN LCASE(DATA_TYPE)='date' THEN 10 WHEN LCASE(DATA_TYPE)='time' THEN 8"); + sqlBuf.append(" WHEN LCASE(DATA_TYPE)='datetime' THEN 19 WHEN LCASE(DATA_TYPE)='timestamp' THEN 19"); + sqlBuf.append(" WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN NUMERIC_PRECISION WHEN CHARACTER_MAXIMUM_LENGTH > "); + sqlBuf.append(Integer.MAX_VALUE); + sqlBuf.append(" THEN "); + sqlBuf.append(Integer.MAX_VALUE); + sqlBuf.append(" ELSE CHARACTER_MAXIMUM_LENGTH END AS COLUMN_SIZE, "); + sqlBuf.append(maxBufferSize); + sqlBuf.append(" AS BUFFER_LENGTH,NUMERIC_SCALE AS DECIMAL_DIGITS, "); + sqlBuf.append(Integer.toString(java.sql.DatabaseMetaData.versionColumnNotPseudo)); + sqlBuf.append(" AS PSEUDO_COLUMN FROM INFORMATION_SCHEMA.COLUMNS WHERE"); + if (catalog != null) { + sqlBuf.append(" TABLE_SCHEMA LIKE ? AND"); + } + sqlBuf.append(" TABLE_NAME LIKE ?"); + sqlBuf.append(" AND EXTRA LIKE '%on update CURRENT_TIMESTAMP%'"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + int nextId = 1; + if (catalog != null) { + pStmt.setString(nextId++, catalog); + } + pStmt.setString(nextId, table); + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).getColumnDefinition() + .setFields(new Field[] { new Field("", "SCOPE", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.SMALLINT, 5), + new Field("", "COLUMN_NAME", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 32), + new Field("", "DATA_TYPE", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.INT, 5), + new Field("", "TYPE_NAME", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 16), + new Field("", "COLUMN_SIZE", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.INT, 16), + new Field("", "BUFFER_LENGTH", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.INT, 16), + new Field("", "DECIMAL_DIGITS", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.SMALLINT, 16), + new Field("", "PSEUDO_COLUMN", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.SMALLINT, 5) }); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + @Override + public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) throws SQLException { + + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + catalog = this.pedantic ? catalog : StringUtils.unQuoteIdentifier(catalog, this.quotedId); + + // FUNCTION_CAT + // FUNCTION_SCHEM + // FUNCTION_NAME + // COLUMN_NAME + // COLUMN_TYPE + StringBuilder sqlBuf = new StringBuilder("SELECT SPECIFIC_SCHEMA AS FUNCTION_CAT, NULL AS `FUNCTION_SCHEM`, SPECIFIC_NAME AS `FUNCTION_NAME`, "); + sqlBuf.append("IFNULL(PARAMETER_NAME, '') AS `COLUMN_NAME`, CASE WHEN PARAMETER_MODE = 'IN' THEN "); + sqlBuf.append(getFunctionConstant(FunctionConstant.FUNCTION_COLUMN_IN)); + sqlBuf.append(" WHEN PARAMETER_MODE = 'OUT' THEN "); + sqlBuf.append(getFunctionConstant(FunctionConstant.FUNCTION_COLUMN_OUT)); + sqlBuf.append(" WHEN PARAMETER_MODE = 'INOUT' THEN "); + sqlBuf.append(getFunctionConstant(FunctionConstant.FUNCTION_COLUMN_INOUT)); + sqlBuf.append(" WHEN ORDINAL_POSITION = 0 THEN "); + sqlBuf.append(getFunctionConstant(FunctionConstant.FUNCTION_COLUMN_RETURN)); + sqlBuf.append(" ELSE "); + sqlBuf.append(getFunctionConstant(FunctionConstant.FUNCTION_COLUMN_UNKNOWN)); + sqlBuf.append(" END AS `COLUMN_TYPE`, "); + + //DATA_TYPE + appendJdbcTypeMappingQuery(sqlBuf, "DATA_TYPE", "DTD_IDENTIFIER"); + + sqlBuf.append(" AS `DATA_TYPE`, "); + + // TYPE_NAME + sqlBuf.append( + "UPPER(CASE WHEN LOCATE('UNSIGNED', UPPER(DATA_TYPE)) != 0 AND LOCATE('UNSIGNED', UPPER(DATA_TYPE)) = 0 THEN CONCAT(DATA_TYPE, ' UNSIGNED') " + + "ELSE DATA_TYPE END) AS `TYPE_NAME`,"); + + // PRECISION int => precision + sqlBuf.append("NUMERIC_PRECISION AS `PRECISION`, "); + // LENGTH int => length in bytes of data + sqlBuf.append("CASE WHEN LCASE(DATA_TYPE)='date' THEN 10 WHEN LCASE(DATA_TYPE)='time' THEN 8 WHEN LCASE(DATA_TYPE)='datetime' THEN 19 WHEN " + + "LCASE(DATA_TYPE)='timestamp' THEN 19 WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN NUMERIC_PRECISION WHEN CHARACTER_MAXIMUM_LENGTH > " + + Integer.MAX_VALUE + " THEN " + Integer.MAX_VALUE + " ELSE CHARACTER_MAXIMUM_LENGTH END AS LENGTH, "); + + // SCALE short => scale + sqlBuf.append("NUMERIC_SCALE AS `SCALE`, "); + // RADIX short => radix + sqlBuf.append("10 AS RADIX,"); + // NULLABLE + // REMARKS + // CHAR_OCTET_LENGTH * + // ORDINAL_POSITION * + // IS_NULLABLE * + sqlBuf.append(getFunctionConstant(FunctionConstant.FUNCTION_NULLABLE)); + sqlBuf.append(" AS `NULLABLE`, NULL AS `REMARKS`, CHARACTER_OCTET_LENGTH AS `CHAR_OCTET_LENGTH`, ORDINAL_POSITION, 'YES' AS `IS_NULLABLE`,"); + // SPECIFIC_NAME * + sqlBuf.append(" SPECIFIC_NAME FROM INFORMATION_SCHEMA.PARAMETERS WHERE"); + + StringBuilder conditionBuf = new StringBuilder(); + if (catalog != null) { + conditionBuf.append(" SPECIFIC_SCHEMA LIKE ?"); + } + + if (functionNamePattern != null) { + if (conditionBuf.length() > 0) { + conditionBuf.append(" AND"); + } + conditionBuf.append(" SPECIFIC_NAME LIKE ?"); + } + + if (columnNamePattern != null) { + if (conditionBuf.length() > 0) { + conditionBuf.append(" AND"); + } + conditionBuf.append(" (PARAMETER_NAME LIKE ? OR PARAMETER_NAME IS NULL)"); + } + + if (conditionBuf.length() > 0) { + conditionBuf.append(" AND"); + } + conditionBuf.append(" ROUTINE_TYPE='FUNCTION'"); + + sqlBuf.append(conditionBuf); + sqlBuf.append(" ORDER BY SPECIFIC_SCHEMA, SPECIFIC_NAME, ORDINAL_POSITION"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + int nextId = 1; + if (catalog != null) { + pStmt.setString(nextId++, catalog); + } + if (functionNamePattern != null) { + pStmt.setString(nextId++, functionNamePattern); + } + if (columnNamePattern != null) { + pStmt.setString(nextId, columnNamePattern); + } + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).getColumnDefinition().setFields(createFunctionColumnsFields()); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + /** + * Getter to DatabaseMetaData.function* constants. + * + * @param constant + * the constant id from DatabaseMetaData fields to return. + * + * @return one of the java.sql.DatabaseMetaData#function* fields. + */ + protected int getFunctionConstant(FunctionConstant constant) { + switch (constant) { + case FUNCTION_COLUMN_IN: + return functionColumnIn; + case FUNCTION_COLUMN_INOUT: + return functionColumnInOut; + case FUNCTION_COLUMN_OUT: + return functionColumnOut; + case FUNCTION_COLUMN_RETURN: + return functionReturn; + case FUNCTION_COLUMN_RESULT: + return functionColumnResult; + case FUNCTION_COLUMN_UNKNOWN: + return functionColumnUnknown; + case FUNCTION_NO_NULLS: + return functionNoNulls; + case FUNCTION_NULLABLE: + return functionNullable; + case FUNCTION_NULLABLE_UNKNOWN: + return functionNullableUnknown; + default: + return -1; + } + } + + @Override + public java.sql.ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException { + + if (catalog == null && this.nullCatalogMeansCurrent) { + catalog = this.database; + } + + catalog = this.pedantic ? catalog : StringUtils.unQuoteIdentifier(catalog, this.quotedId); + + StringBuilder sqlBuf = new StringBuilder( + "SELECT ROUTINE_SCHEMA AS FUNCTION_CAT, NULL AS FUNCTION_SCHEM, ROUTINE_NAME AS FUNCTION_NAME, ROUTINE_COMMENT AS REMARKS, "); + sqlBuf.append(getFunctionNoTableConstant()); + sqlBuf.append(" AS FUNCTION_TYPE, ROUTINE_NAME AS SPECIFIC_NAME FROM INFORMATION_SCHEMA.ROUTINES"); + sqlBuf.append(" WHERE ROUTINE_TYPE LIKE 'FUNCTION'"); + if (catalog != null) { + sqlBuf.append(" AND ROUTINE_SCHEMA LIKE ?"); + } + if (functionNamePattern != null) { + sqlBuf.append(" AND ROUTINE_NAME LIKE ?"); + } + + sqlBuf.append(" ORDER BY FUNCTION_CAT, FUNCTION_SCHEM, FUNCTION_NAME, SPECIFIC_NAME"); + + java.sql.PreparedStatement pStmt = null; + + try { + pStmt = prepareMetaDataSafeStatement(sqlBuf.toString()); + int nextId = 1; + if (catalog != null) { + pStmt.setString(nextId++, catalog); + } + if (functionNamePattern != null) { + pStmt.setString(nextId, functionNamePattern); + } + + ResultSet rs = executeMetadataQuery(pStmt); + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).getColumnDefinition() + .setFields(new Field[] { new Field("", "FUNCTION_CAT", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 255), + new Field("", "FUNCTION_SCHEM", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 255), + new Field("", "FUNCTION_NAME", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 255), + new Field("", "REMARKS", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 255), + new Field("", "FUNCTION_TYPE", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.SMALLINT, 6), + new Field("", "SPECIFIC_NAME", this.getMetadataCollationIndex(), this.getMetadataEncoding(), MysqlType.CHAR, 255) }); + + return rs; + } finally { + if (pStmt != null) { + pStmt.close(); + } + } + } + + private final void appendJdbcTypeMappingQuery(StringBuilder buf, String mysqlTypeColumnName, String fullMysqlTypeColumnName) { + + buf.append("CASE "); + for (MysqlType mysqlType : MysqlType.values()) { + + buf.append(" WHEN UPPER("); + buf.append(mysqlTypeColumnName); + buf.append(")='"); + buf.append(mysqlType.getName()); + buf.append("' THEN "); + + switch (mysqlType) { + case TINYINT: + case TINYINT_UNSIGNED: + if (this.tinyInt1isBit) { + buf.append("CASE"); + if (this.transformedBitIsBoolean) { + buf.append(" WHEN LOCATE('(1)', "); + buf.append(fullMysqlTypeColumnName); + buf.append(") != 0 THEN 16"); + } else { + buf.append(" WHEN LOCATE('(1)', "); + buf.append(fullMysqlTypeColumnName); + buf.append(") != 0 THEN -7"); + } + buf.append(" ELSE -6 END "); + } else { + buf.append(mysqlType.getJdbcType()); + } + break; + + default: + buf.append(mysqlType.getJdbcType()); + } + } + + buf.append(" WHEN UPPER(DATA_TYPE)='POINT' THEN -2"); + buf.append(" WHEN UPPER(DATA_TYPE)='LINESTRING' THEN -2"); + buf.append(" WHEN UPPER(DATA_TYPE)='POLYGON' THEN -2"); + buf.append(" WHEN UPPER(DATA_TYPE)='MULTIPOINT' THEN -2"); + buf.append(" WHEN UPPER(DATA_TYPE)='MULTILINESTRING' THEN -2"); + buf.append(" WHEN UPPER(DATA_TYPE)='MULTIPOLYGON' THEN -2"); + buf.append(" WHEN UPPER(DATA_TYPE)='GEOMETRYCOLLECTION' THEN -2"); + buf.append(" WHEN UPPER(DATA_TYPE)='GEOMCOLLECTION' THEN -2"); + + buf.append(" ELSE 1111"); + buf.append(" END "); + + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/DocsConnectionPropsHelper.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/DocsConnectionPropsHelper.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/DocsConnectionPropsHelper.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import com.mysql.cj.conf.PropertyDefinitions; + +public class DocsConnectionPropsHelper { + + public static void main(String[] args) throws Exception { + System.out.println(PropertyDefinitions.exposeAsXml()); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/Driver.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/Driver.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/Driver.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.SQLException; + +/** + * The Java SQL framework allows for multiple database drivers. Each driver should supply a class that implements the Driver interface + * + *

+ * The DriverManager will try to load as many drivers as it can find and then for any given connection request, it will ask each driver in turn to try to + * connect to the target URL. + * + *

+ * It is strongly recommended that each Driver class should be small and standalone so that the Driver class can be loaded and queried without bringing in vast + * quantities of supporting code. + * + *

+ * When a Driver class is loaded, it should create an instance of itself and register it with the DriverManager. This means that a user can load and register a + * driver by doing Class.forName("foo.bah.Driver") + */ +public class Driver extends NonRegisteringDriver implements java.sql.Driver { + // + // Register ourselves with the DriverManager + // + static { + try { + java.sql.DriverManager.registerDriver(new Driver()); + } catch (SQLException E) { + throw new RuntimeException("Can't register driver!"); + } + } + + /** + * Construct a new driver and register it with DriverManager + * + * @throws SQLException + * if a database error occurs. + */ + public Driver() throws SQLException { + // Required for Class.forName().newInstance() + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/EscapeProcessor.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/EscapeProcessor.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/EscapeProcessor.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.SQLException; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.TimeZone; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.util.EscapeTokenizer; +import com.mysql.cj.util.StringUtils; +import com.mysql.cj.util.TimeUtil; + +/** + * EscapeProcessor performs all escape code processing as outlined in the JDBC spec by JavaSoft. + */ +class EscapeProcessor { + private static Map JDBC_CONVERT_TO_MYSQL_TYPE_MAP; + + static { + Map tempMap = new HashMap<>(); + + tempMap.put("BIGINT", "0 + ?"); + tempMap.put("BINARY", "BINARY"); + tempMap.put("BIT", "0 + ?"); + tempMap.put("CHAR", "CHAR"); + tempMap.put("DATE", "DATE"); + tempMap.put("DECIMAL", "0.0 + ?"); + tempMap.put("DOUBLE", "0.0 + ?"); + tempMap.put("FLOAT", "0.0 + ?"); + tempMap.put("INTEGER", "0 + ?"); + tempMap.put("LONGVARBINARY", "BINARY"); + tempMap.put("LONGVARCHAR", "CONCAT(?)"); + tempMap.put("REAL", "0.0 + ?"); + tempMap.put("SMALLINT", "CONCAT(?)"); + tempMap.put("TIME", "TIME"); + tempMap.put("TIMESTAMP", "DATETIME"); + tempMap.put("TINYINT", "CONCAT(?)"); + tempMap.put("VARBINARY", "BINARY"); + tempMap.put("VARCHAR", "CONCAT(?)"); + + JDBC_CONVERT_TO_MYSQL_TYPE_MAP = Collections.unmodifiableMap(tempMap); + + } + + /** + * Escape process one string + * + * @param sql + * the SQL to escape process. + * + * @return the SQL after it has been escape processed. + * + * @throws java.sql.SQLException + * @throws SQLException + */ + public static final Object escapeSQL(String sql, TimeZone defaultTimeZone, boolean serverSupportsFractionalSecond, + ExceptionInterceptor exceptionInterceptor) throws java.sql.SQLException { + boolean replaceEscapeSequence = false; + String escapeSequence = null; + + if (sql == null) { + return null; + } + + /* + * Short circuit this code if we don't have a matching pair of "{}". - Suggested by Ryan Gustafason + */ + int beginBrace = sql.indexOf('{'); + int nextEndBrace = (beginBrace == -1) ? (-1) : sql.indexOf('}', beginBrace); + + if (nextEndBrace == -1) { + return sql; + } + + StringBuilder newSql = new StringBuilder(); + + EscapeTokenizer escapeTokenizer = new EscapeTokenizer(sql); + + byte usesVariables = StatementImpl.USES_VARIABLES_FALSE; + boolean callingStoredFunction = false; + + while (escapeTokenizer.hasMoreTokens()) { + String token = escapeTokenizer.nextToken(); + + if (token.length() != 0) { + if (token.charAt(0) == '{') { // It's an escape code + + if (!token.endsWith("}")) { + throw SQLError.createSQLException(Messages.getString("EscapeProcessor.0", new Object[] { token }), exceptionInterceptor); + } + + if (token.length() > 2) { + int nestedBrace = token.indexOf('{', 2); + + if (nestedBrace != -1) { + StringBuilder buf = new StringBuilder(token.substring(0, 1)); + + Object remainingResults = escapeSQL(token.substring(1, token.length() - 1), defaultTimeZone, serverSupportsFractionalSecond, + exceptionInterceptor); + + String remaining = null; + + if (remainingResults instanceof String) { + remaining = (String) remainingResults; + } else { + remaining = ((EscapeProcessorResult) remainingResults).escapedSql; + + if (usesVariables != StatementImpl.USES_VARIABLES_TRUE) { + usesVariables = ((EscapeProcessorResult) remainingResults).usesVariables; + } + } + + buf.append(remaining); + + buf.append('}'); + + token = buf.toString(); + } + } + + // nested escape code + // Compare to tokens with _no_ whitespace + String collapsedToken = removeWhitespace(token); + + /* + * Process the escape code + */ + if (StringUtils.startsWithIgnoreCase(collapsedToken, "{escape")) { + try { + StringTokenizer st = new StringTokenizer(token, " '"); + st.nextToken(); // eat the "escape" token + escapeSequence = st.nextToken(); + + if (escapeSequence.length() < 3) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } else { + + escapeSequence = escapeSequence.substring(1, escapeSequence.length() - 1); + replaceEscapeSequence = true; + } + } catch (java.util.NoSuchElementException e) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{fn")) { + int startPos = token.toLowerCase().indexOf("fn ") + 3; + int endPos = token.length() - 1; // no } + + String fnToken = token.substring(startPos, endPos); + + // We need to handle 'convert' by ourselves + + if (StringUtils.startsWithIgnoreCaseAndWs(fnToken, "convert")) { + newSql.append(processConvertToken(fnToken, exceptionInterceptor)); + } else { + // just pass functions right to the DB + newSql.append(fnToken); + } + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{d")) { + int startPos = token.indexOf('\'') + 1; + int endPos = token.lastIndexOf('\''); // no } + + if ((startPos == -1) || (endPos == -1)) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } else { + + String argument = token.substring(startPos, endPos); + + try { + StringTokenizer st = new StringTokenizer(argument, " -"); + String year4 = st.nextToken(); + String month2 = st.nextToken(); + String day2 = st.nextToken(); + String dateString = "'" + year4 + "-" + month2 + "-" + day2 + "'"; + newSql.append(dateString); + } catch (java.util.NoSuchElementException e) { + throw SQLError.createSQLException(Messages.getString("EscapeProcessor.1", new Object[] { argument }), + MysqlErrorNumbers.SQL_STATE_SYNTAX_ERROR, exceptionInterceptor); + } + } + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{ts")) { + processTimestampToken(defaultTimeZone, newSql, token, serverSupportsFractionalSecond, exceptionInterceptor); + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{t")) { + processTimeToken(newSql, token, serverSupportsFractionalSecond, exceptionInterceptor); + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{call") || StringUtils.startsWithIgnoreCase(collapsedToken, "{?=call")) { + + int startPos = StringUtils.indexOfIgnoreCase(token, "CALL") + 5; + int endPos = token.length() - 1; + + if (StringUtils.startsWithIgnoreCase(collapsedToken, "{?=call")) { + callingStoredFunction = true; + newSql.append("SELECT "); + newSql.append(token.substring(startPos, endPos)); + } else { + callingStoredFunction = false; + newSql.append("CALL "); + newSql.append(token.substring(startPos, endPos)); + } + + for (int i = endPos - 1; i >= startPos; i--) { + char c = token.charAt(i); + + if (Character.isWhitespace(c)) { + continue; + } + + if (c != ')') { + newSql.append("()"); // handle no-parenthesis no-arg call not supported by MySQL parser + } + + break; + } + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, "{oj")) { + // MySQL already handles this escape sequence because of ODBC. Cool. + newSql.append(token); + } else { + // not an escape code, just part of the query + newSql.append(token); + } + } else { + newSql.append(token); // it's just part of the query + } + } + } + + String escapedSql = newSql.toString(); + + // + // FIXME: Let MySQL do this, however requires lightweight parsing of statement + // + if (replaceEscapeSequence) { + String currentSql = escapedSql; + + while (currentSql.indexOf(escapeSequence) != -1) { + int escapePos = currentSql.indexOf(escapeSequence); + String lhs = currentSql.substring(0, escapePos); + String rhs = currentSql.substring(escapePos + 1, currentSql.length()); + currentSql = lhs + "\\" + rhs; + } + + escapedSql = currentSql; + } + + EscapeProcessorResult epr = new EscapeProcessorResult(); + epr.escapedSql = escapedSql; + epr.callingStoredFunction = callingStoredFunction; + + if (usesVariables != StatementImpl.USES_VARIABLES_TRUE) { + if (escapeTokenizer.sawVariableUse()) { + epr.usesVariables = StatementImpl.USES_VARIABLES_TRUE; + } else { + epr.usesVariables = StatementImpl.USES_VARIABLES_FALSE; + } + } + + return epr; + } + + private static void processTimeToken(StringBuilder newSql, String token, boolean serverSupportsFractionalSecond, ExceptionInterceptor exceptionInterceptor) + throws SQLException { + int startPos = token.indexOf('\'') + 1; + int endPos = token.lastIndexOf('\''); // no } + + if ((startPos == -1) || (endPos == -1)) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } else { + + String argument = token.substring(startPos, endPos); + + try { + StringTokenizer st = new StringTokenizer(argument, " :."); + String hour = st.nextToken(); + String minute = st.nextToken(); + String second = st.nextToken(); + + String fractionalSecond = ""; + + if (serverSupportsFractionalSecond && st.hasMoreTokens()) { + fractionalSecond = "." + st.nextToken(); + } + + newSql.append("'"); + newSql.append(hour); + newSql.append(":"); + newSql.append(minute); + newSql.append(":"); + newSql.append(second); + if (serverSupportsFractionalSecond) { + newSql.append(fractionalSecond); + } + newSql.append("'"); + } catch (java.util.NoSuchElementException e) { + throw SQLError.createSQLException(Messages.getString("EscapeProcessor.3", new Object[] { argument }), MysqlErrorNumbers.SQL_STATE_SYNTAX_ERROR, + exceptionInterceptor); + } + } + } + + private static void processTimestampToken(TimeZone tz, StringBuilder newSql, String token, boolean serverSupportsFractionalSecond, + ExceptionInterceptor exceptionInterceptor) throws SQLException { + int startPos = token.indexOf('\'') + 1; + int endPos = token.lastIndexOf('\''); // no } + + if ((startPos == -1) || (endPos == -1)) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } else { + + String argument = token.substring(startPos, endPos); + + try { + Timestamp ts = Timestamp.valueOf(argument); + SimpleDateFormat tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss", Locale.US); + + tsdf.setTimeZone(tz); + + newSql.append(tsdf.format(ts)); + + if (serverSupportsFractionalSecond && ts.getNanos() > 0) { + newSql.append('.'); + newSql.append(TimeUtil.formatNanos(ts.getNanos(), true)); + } + + newSql.append('\''); + } catch (IllegalArgumentException illegalArgumentException) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("EscapeProcessor.2", new Object[] { argument }), + MysqlErrorNumbers.SQL_STATE_SYNTAX_ERROR, exceptionInterceptor); + sqlEx.initCause(illegalArgumentException); + + throw sqlEx; + } + } + } + + /** + * Re-writes {fn convert (expr, type)} as cast(expr AS type) + * + * @param functionToken + * @throws SQLException + */ + private static String processConvertToken(String functionToken, ExceptionInterceptor exceptionInterceptor) throws SQLException { + // The JDBC spec requires these types: + // + // BIGINT + // BINARY + // BIT + // CHAR + // DATE + // DECIMAL + // DOUBLE + // FLOAT + // INTEGER + // LONGVARBINARY + // LONGVARCHAR + // REAL + // SMALLINT + // TIME + // TIMESTAMP + // TINYINT + // VARBINARY + // VARCHAR + + // MySQL supports these types: + // + // BINARY + // CHAR + // DATE + // DATETIME + // SIGNED (integer) + // UNSIGNED (integer) + // TIME + + int firstIndexOfParen = functionToken.indexOf("("); + + if (firstIndexOfParen == -1) { + throw SQLError.createSQLException(Messages.getString("EscapeProcessor.4", new Object[] { functionToken }), MysqlErrorNumbers.SQL_STATE_SYNTAX_ERROR, + exceptionInterceptor); + } + + int indexOfComma = functionToken.lastIndexOf(","); + + if (indexOfComma == -1) { + throw SQLError.createSQLException(Messages.getString("EscapeProcessor.5", new Object[] { functionToken }), MysqlErrorNumbers.SQL_STATE_SYNTAX_ERROR, + exceptionInterceptor); + } + + int indexOfCloseParen = functionToken.indexOf(')', indexOfComma); + + if (indexOfCloseParen == -1) { + throw SQLError.createSQLException(Messages.getString("EscapeProcessor.6", new Object[] { functionToken }), MysqlErrorNumbers.SQL_STATE_SYNTAX_ERROR, + exceptionInterceptor); + + } + + String expression = functionToken.substring(firstIndexOfParen + 1, indexOfComma); + String type = functionToken.substring(indexOfComma + 1, indexOfCloseParen); + + String newType = null; + + String trimmedType = type.trim(); + + if (StringUtils.startsWithIgnoreCase(trimmedType, "SQL_")) { + trimmedType = trimmedType.substring(4, trimmedType.length()); + } + + newType = JDBC_CONVERT_TO_MYSQL_TYPE_MAP.get(trimmedType.toUpperCase(Locale.ENGLISH)); + + if (newType == null) { + throw SQLError.createSQLException(Messages.getString("EscapeProcessor.7", new Object[] { type.trim() }), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + exceptionInterceptor); + } + + int replaceIndex = newType.indexOf("?"); + + if (replaceIndex != -1) { + StringBuilder convertRewrite = new StringBuilder(newType.substring(0, replaceIndex)); + convertRewrite.append(expression); + convertRewrite.append(newType.substring(replaceIndex + 1, newType.length())); + + return convertRewrite.toString(); + } + + StringBuilder castRewrite = new StringBuilder("CAST("); + castRewrite.append(expression); + castRewrite.append(" AS "); + castRewrite.append(newType); + castRewrite.append(")"); + + return castRewrite.toString(); + + } + + /** + * Removes all whitespace from the given String. We use this to make escape + * token comparison white-space ignorant. + * + * @param toCollapse + * the string to remove the whitespace from + * + * @return a string with _no_ whitespace. + */ + private static String removeWhitespace(String toCollapse) { + if (toCollapse == null) { + return null; + } + + int length = toCollapse.length(); + + StringBuilder collapsed = new StringBuilder(length); + + for (int i = 0; i < length; i++) { + char c = toCollapse.charAt(i); + + if (!Character.isWhitespace(c)) { + collapsed.append(c); + } + } + + return collapsed.toString(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/EscapeProcessorResult.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/EscapeProcessorResult.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/EscapeProcessorResult.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +/** + * Wraps output from EscapeProcessor, to help prevent multiple passes over the query string, to detect characters such as '@' (defining/using a variable), + * which are used further up the call stack to handle failover. + */ +class EscapeProcessorResult { + boolean callingStoredFunction = false; + + String escapedSql; + + byte usesVariables = StatementImpl.USES_VARIABLES_FALSE; +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/IterateBlock.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/IterateBlock.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/IterateBlock.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.SQLException; +import java.util.Iterator; + +import com.mysql.cj.jdbc.DatabaseMetaData.IteratorWithCleanup; + +public abstract class IterateBlock { + IteratorWithCleanup iteratorWithCleanup; + Iterator javaIterator; + boolean stopIterating = false; + + IterateBlock(IteratorWithCleanup i) { + this.iteratorWithCleanup = i; + this.javaIterator = null; + } + + IterateBlock(Iterator i) { + this.javaIterator = i; + this.iteratorWithCleanup = null; + } + + public void doForAll() throws SQLException { + if (this.iteratorWithCleanup != null) { + try { + while (this.iteratorWithCleanup.hasNext()) { + forEach(this.iteratorWithCleanup.next()); + + if (this.stopIterating) { + break; + } + } + } finally { + this.iteratorWithCleanup.close(); + } + } else { + while (this.javaIterator.hasNext()) { + forEach(this.javaIterator.next()); + + if (this.stopIterating) { + break; + } + } + } + } + + abstract void forEach(T each) throws SQLException; + + public final boolean fullIteration() { + return !this.stopIterating; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcConnection.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcConnection.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcConnection.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,501 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.SQLException; +import java.util.List; + +import com.mysql.cj.MysqlConnection; +import com.mysql.cj.ServerVersion; +import com.mysql.cj.TransactionEventHandler; +import com.mysql.cj.interceptors.QueryInterceptor; +import com.mysql.cj.jdbc.result.CachedResultSetMetaData; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; + +/** + * This interface contains methods that are considered the "vendor extension" to the JDBC API for MySQL's implementation of java.sql.Connection. + * + * For those looking further into the driver implementation, it is not an API that is used for plugability of implementations inside our driver + * (which is why there are still references to ConnectionImpl throughout the code). + */ +public interface JdbcConnection extends java.sql.Connection, MysqlConnection, TransactionEventHandler { + + public JdbcPropertySet getPropertySet(); + + /** + * Changes the user on this connection by performing a re-authentication. If + * authentication fails, the connection is failed. + * + * @param userName + * the username to authenticate with + * @param newPassword + * the password to authenticate with + * @throws SQLException + * if authentication fails, or some other error occurs while + * performing the command. + */ + void changeUser(String userName, String newPassword) throws SQLException; + + @Deprecated + void clearHasTriedMaster(); + + /** + * Prepares a statement on the client, using client-side emulation + * (irregardless of the configuration property 'useServerPrepStmts') + * with the same semantics as the java.sql.Connection.prepareStatement() + * method with the same argument types. + * + * @param sql + * statement + * @return prepared statement + * @throws SQLException + * if an error occurs + * @see java.sql.Connection#prepareStatement(String) + */ + java.sql.PreparedStatement clientPrepareStatement(String sql) throws SQLException; + + /** + * Prepares a statement on the client, using client-side emulation + * (irregardless of the configuration property 'useServerPrepStmts') + * with the same semantics as the java.sql.Connection.prepareStatement() + * method with the same argument types. + * + * @param sql + * statement + * @param autoGenKeyIndex + * autoGenKeyIndex + * @return prepared statement + * @throws SQLException + * if an error occurs + * @see java.sql.Connection#prepareStatement(String, int) + */ + java.sql.PreparedStatement clientPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException; + + /** + * Prepares a statement on the client, using client-side emulation + * (irregardless of the configuration property 'useServerPrepStmts') + * with the same semantics as the java.sql.Connection.prepareStatement() + * method with the same argument types. + * + * @param sql + * statement + * @param resultSetType + * resultSetType + * @param resultSetConcurrency + * resultSetConcurrency + * @return prepared statement + * @throws SQLException + * if an error occurs + * + * @see java.sql.Connection#prepareStatement(String, int, int) + */ + java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException; + + /** + * Prepares a statement on the client, using client-side emulation + * (irregardless of the configuration property 'useServerPrepStmts') + * with the same semantics as the java.sql.Connection.prepareStatement() + * method with the same argument types. + * + * @param sql + * statement + * @param autoGenKeyIndexes + * autoGenKeyIndexes + * @return prepared statement + * @throws SQLException + * if an error occurs + * + * @see java.sql.Connection#prepareStatement(String, int[]) + */ + java.sql.PreparedStatement clientPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException; + + /** + * Prepares a statement on the client, using client-side emulation + * (irregardless of the configuration property 'useServerPrepStmts') + * with the same semantics as the java.sql.Connection.prepareStatement() + * method with the same argument types. + * + * @param sql + * statement + * @param resultSetType + * resultSetType + * @param resultSetConcurrency + * resultSetConcurrency + * @param resultSetHoldability + * resultSetHoldability + * @return prepared statement + * @throws SQLException + * if an error occurs + * + * @see java.sql.Connection#prepareStatement(String, int, int, int) + */ + java.sql.PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException; + + /** + * Prepares a statement on the client, using client-side emulation + * (irregardless of the configuration property 'useServerPrepStmts') + * with the same semantics as the java.sql.Connection.prepareStatement() + * method with the same argument types. + * + * @param sql + * statement + * @param autoGenKeyColNames + * autoGenKeyColNames + * @return prepared statement + * @throws SQLException + * if an error occurs + * + * @see java.sql.Connection#prepareStatement(String, String[]) + */ + java.sql.PreparedStatement clientPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException; + + /** + * Returns the number of statements active on this connection, which + * haven't been .close()d. + * + * @return the number of active statements + */ + int getActiveStatementCount(); + + /** + * Reports how long this connection has been idle. + * This time (reported in milliseconds) is updated once a query has + * completed. + * + * @return number of ms that this connection has been idle, 0 if the driver + * is busy retrieving results. + */ + long getIdleFor(); + + /** + * Returns the comment that will be prepended to all statements + * sent to the server. + * + * @return the comment that will be prepended to all statements + * sent to the server. + */ + String getStatementComment(); + + /** + * Has this connection tried to execute a query on the "master" + * server (first host in a multiple host list). + * + * @return true if it has tried + */ + @Deprecated + boolean hasTriedMaster(); + + /** + * Is this connection currently a participant in an XA transaction? + * + * @return true if this connection currently a participant in an XA transaction + */ + boolean isInGlobalTx(); + + /** + * Set the state of being in a global (XA) transaction. + * + * @param flag + * the state flag + */ + void setInGlobalTx(boolean flag); + + // TODO this and other multi-host connection specific methods should be moved to special interface + /** + * Is this connection connected to the first host in the list if + * there is a list of servers in the URL? + * + * @return true if this connection is connected to the first in + * the list. + */ + boolean isMasterConnection(); + + /** + * Does this connection have the same resource name as the given + * connection (for XA)? + * + * @param c + * connection + * @return true if it is the same one + */ + boolean isSameResource(JdbcConnection c); + + /** + * Is the server configured to use lower-case table names only? + * + * @return true if lower_case_table_names is 'on' + */ + boolean lowerCaseTableNames(); + + /** + * Detect if the connection is still good by sending a ping command + * to the server. + * + * @throws SQLException + * if the ping fails + */ + void ping() throws SQLException; + + /** + * Resets the server-side state of this connection. Doesn't work for MySQL + * versions older than 4.0.6 or if isParanoid() is set (it will become a + * no-op in these cases). Usually only used from connection pooling code. + * + * @throws SQLException + * if the operation fails while resetting server state. + */ + void resetServerState() throws SQLException; + + /** + * Prepares a statement on the server (irregardless of the + * configuration property 'useServerPrepStmts') with the same semantics + * as the java.sql.Connection.prepareStatement() method with the + * same argument types. + * + * @param sql + * statement + * @return prepared statement + * @throws SQLException + * if an error occurs + * @see java.sql.Connection#prepareStatement(String) + */ + java.sql.PreparedStatement serverPrepareStatement(String sql) throws SQLException; + + /** + * Prepares a statement on the server (irregardless of the + * configuration property 'useServerPrepStmts') with the same semantics + * as the java.sql.Connection.prepareStatement() method with the + * same argument types. + * + * @param sql + * statement + * @param autoGenKeyIndex + * autoGenKeyIndex + * @return prepared statement + * @throws SQLException + * if an error occurs + * @see java.sql.Connection#prepareStatement(String, int) + */ + java.sql.PreparedStatement serverPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException; + + /** + * Prepares a statement on the server (irregardless of the + * configuration property 'useServerPrepStmts') with the same semantics + * as the java.sql.Connection.prepareStatement() method with the + * same argument types. + * + * @param sql + * statement + * @param resultSetType + * resultSetType + * @param resultSetConcurrency + * resultSetConcurrency + * @return prepared statement + * @throws SQLException + * if an error occurs + * + * @see java.sql.Connection#prepareStatement(String, int, int) + */ + java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException; + + /** + * Prepares a statement on the server (irregardless of the + * configuration property 'useServerPrepStmts') with the same semantics + * as the java.sql.Connection.prepareStatement() method with the + * same argument types. + * + * @param sql + * statement + * @param resultSetType + * resultSetType + * @param resultSetConcurrency + * resultSetConcurrency + * @param resultSetHoldability + * resultSetHoldability + * @return prepared statement + * @throws SQLException + * if an error occurs + * + * @see java.sql.Connection#prepareStatement(String, int, int, int) + */ + java.sql.PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException; + + /** + * Prepares a statement on the server (irregardless of the + * configuration property 'useServerPrepStmts') with the same semantics + * as the java.sql.Connection.prepareStatement() method with the + * same argument types. + * + * @param sql + * statement + * @param autoGenKeyIndexes + * autoGenKeyIndexes + * @return prepared statement + * @throws SQLException + * if an error occurs + * @see java.sql.Connection#prepareStatement(String, int[]) + */ + java.sql.PreparedStatement serverPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException; + + /** + * Prepares a statement on the server (irregardless of the + * configuration property 'useServerPrepStmts') with the same semantics + * as the java.sql.Connection.prepareStatement() method with the + * same argument types. + * + * @param sql + * statement + * @param autoGenKeyColNames + * autoGenKeyColNames + * @return prepared statement + * @throws SQLException + * if an error occurs + * + * @see java.sql.Connection#prepareStatement(String, String[]) + */ + java.sql.PreparedStatement serverPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException; + + /** + * @param flag + * The failedOver flag to set. + */ + void setFailedOver(boolean flag); + + /** + * Sets the comment that will be prepended to all statements + * sent to the server. Do not use slash-star or star-slash tokens + * in the comment as these will be added by the driver itself. + * + * @param comment + * the comment that will be prepended to all statements + * sent to the server. + */ + void setStatementComment(String comment); + + /** + * Used by MiniAdmin to shutdown a MySQL server + * + * @throws SQLException + * if the command can not be issued. + */ + void shutdownServer() throws SQLException; + + /** + * Returns the -session- value of 'auto_increment_increment' from the server if it exists, + * or '1' if not. + * + * @return the -session- value of 'auto_increment_increment' + */ + int getAutoIncrementIncrement(); + + /** + * Does this connection have the same properties as another? + * + * @param c + * connection + * @return true if has the same properties + */ + boolean hasSameProperties(JdbcConnection c); + + String getHost(); + + String getHostPortPair(); + + void setProxy(JdbcConnection proxy); + + /** + * Is the server this connection is connected to "local" (i.e. same host) as the application? + * + * @return true if the server is "local" + * @throws SQLException + * if an error occurs + */ + boolean isServerLocal() throws SQLException; + + int getSessionMaxRows(); + + void setSessionMaxRows(int max) throws SQLException; + + // until we flip catalog/schema, this is a no-op + void setSchema(String schema) throws SQLException; + + // ************************** + // moved from MysqlJdbcConnection + // ************************** + + void abortInternal() throws SQLException; + + boolean isProxySet(); + + CachedResultSetMetaData getCachedMetaData(String sql); + + String getCharacterSetMetadata(); + + java.sql.Statement getMetadataSafeStatement() throws SQLException; + + ServerVersion getServerVersion(); + + List getQueryInterceptorsInstances(); + + void initializeResultsMetadataFromCache(String sql, CachedResultSetMetaData cachedMetaData, ResultSetInternalMethods resultSet) throws SQLException; + + void initializeSafeQueryInterceptors() throws SQLException; + + boolean isReadOnly(boolean useSessionStatus) throws SQLException; + + void pingInternal(boolean checkForClosedConnection, int timeoutMillis) throws SQLException; + + void realClose(boolean calledExplicitly, boolean issueRollback, boolean skipLocalTeardown, Throwable reason) throws SQLException; + + void recachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException; + + void decachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException; + + void registerStatement(JdbcStatement stmt); + + void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException; + + boolean storesLowerCaseTableName(); + + void throwConnectionClosedException() throws SQLException; + + void unregisterStatement(JdbcStatement stmt); + + void unSafeQueryInterceptors() throws SQLException; + + JdbcConnection getMultiHostSafeProxy(); + + JdbcConnection getActiveMySQLConnection(); + + /* + * Non standard methods: + */ + ClientInfoProvider getClientInfoProviderImpl() throws SQLException; +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcPreparedStatement.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcPreparedStatement.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcPreparedStatement.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.math.BigInteger; +import java.sql.SQLException; + +import com.mysql.cj.MysqlType; +import com.mysql.cj.ParseInfo; +import com.mysql.cj.QueryBindings; + +public interface JdbcPreparedStatement extends java.sql.PreparedStatement, JdbcStatement { + + void realClose(boolean calledExplicitly, boolean closeOpenResults) throws SQLException; + + QueryBindings getQueryBindings(); + + byte[] getBytesRepresentation(int parameterIndex) throws SQLException; + + ParseInfo getParseInfo(); + + boolean isNull(int paramIndex) throws SQLException; + + String getPreparedSql(); + + void setBytes(int parameterIndex, byte[] x, boolean checkForIntroducer, boolean escapeForMBChars) throws SQLException; + + /** + * Used by updatable result sets for refreshRow() because the parameter has + * already been escaped for updater or inserter prepared statements. + * + * @param parameterIndex + * the parameter to set. + * @param parameterAsBytes + * the parameter as a string. + * + * @throws SQLException + * if an error occurs + */ + void setBytesNoEscape(int parameterIndex, byte[] parameterAsBytes) throws SQLException; + + void setBytesNoEscapeNoQuotes(int parameterIndex, byte[] parameterAsBytes) throws SQLException; + + void setBigInteger(int parameterIndex, BigInteger x) throws SQLException; + + void setNull(int parameterIndex, MysqlType mysqlType) throws SQLException; + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcPropertySet.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcPropertySet.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcPropertySet.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; +import java.util.Properties; + +import com.mysql.cj.conf.ModifiableProperty; +import com.mysql.cj.conf.PropertySet; + +public interface JdbcPropertySet extends PropertySet { + + ModifiableProperty getJdbcModifiableProperty(String name) throws SQLException; + + /** + * Exposes all ConnectionPropertyInfo instances as DriverPropertyInfo + * + * @param info + * the properties to load into these ConnectionPropertyInfo + * instances + * @param slotsToReserve + * the number of DPI slots to reserve for 'standard' DPI + * properties (user, host, password, etc) + * + * @return a list of all ConnectionPropertyInfo instances, as DriverPropertyInfo + * + * @throws SQLException + * if an error occurs + */ + DriverPropertyInfo[] exposeAsDriverPropertyInfo(Properties info, int slotsToReserve) throws SQLException; +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcPropertySetImpl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcPropertySetImpl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcPropertySetImpl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; +import java.util.Properties; + +import com.mysql.cj.conf.DefaultPropertySet; +import com.mysql.cj.conf.ModifiableProperty; +import com.mysql.cj.conf.PropertyDefinition; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.ReadableProperty; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping; +import com.mysql.cj.util.StringUtils; + +public class JdbcPropertySetImpl extends DefaultPropertySet implements JdbcPropertySet { + + private static final long serialVersionUID = -8223499903182568260L; + + @Override + public ModifiableProperty getJdbcModifiableProperty(String name) throws SQLException { + try { + return getModifiableProperty(name); + } catch (CJException ex) { + throw SQLExceptionsMapping.translateException(ex); + } + } + + @Override + public void postInitialization() { + + // Adjust max rows + if (getIntegerReadableProperty(PropertyDefinitions.PNAME_maxRows).getValue() == 0) { + // adjust so that it will become MysqlDefs.MAX_ROWS in execSQL() + super. getModifiableProperty(PropertyDefinitions.PNAME_maxRows).setValue(Integer.valueOf(-1), null); + } + + // + // Check character encoding + // + String testEncoding = getStringReadableProperty(PropertyDefinitions.PNAME_characterEncoding).getValue(); + + if (testEncoding != null) { + // Attempt to use the encoding, and bail out if it can't be used + String testString = "abc"; + StringUtils.getBytes(testString, testEncoding); + } + + if (getBooleanReadableProperty(PropertyDefinitions.PNAME_useCursorFetch).getValue()) { + // assume server-side prepared statements are wanted because they're required for this functionality + super. getModifiableProperty(PropertyDefinitions.PNAME_useServerPrepStmts).setValue(true); + } + } + + public DriverPropertyInfo[] exposeAsDriverPropertyInfo(Properties info, int slotsToReserve) throws SQLException { + initializeProperties(info); + + int numProperties = PropertyDefinitions.PROPERTY_NAME_TO_PROPERTY_DEFINITION.size(); + + int listSize = numProperties + slotsToReserve; + + DriverPropertyInfo[] driverProperties = new DriverPropertyInfo[listSize]; + + int i = slotsToReserve; + + for (String propName : PropertyDefinitions.PROPERTY_NAME_TO_PROPERTY_DEFINITION.keySet()) { + driverProperties[i++] = getAsDriverPropertyInfo(getReadableProperty(propName)); + } + + return driverProperties; + } + + private DriverPropertyInfo getAsDriverPropertyInfo(ReadableProperty pr) { + PropertyDefinition pdef = pr.getPropertyDefinition(); + + DriverPropertyInfo dpi = new DriverPropertyInfo(pdef.getName(), null); + dpi.choices = pdef.getAllowableValues(); + dpi.value = (pr.getStringValue() != null) ? pr.getStringValue() : null; + dpi.required = false; + dpi.description = pdef.getDescription(); + + return dpi; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcStatement.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcStatement.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/JdbcStatement.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.InputStream; +import java.sql.SQLException; + +import com.mysql.cj.PingTarget; +import com.mysql.cj.Query; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; + +/** + * This interface contains methods that are considered the "vendor extension" to the JDBC API for MySQL's implementation of java.sql.Statement. + * + * For those looking further into the driver implementation, it is not an API that is used for plugability of implementations inside our driver + * (which is why there are still references to StatementImpl throughout the code). + */ +public interface JdbcStatement extends java.sql.Statement, Query { + + public static final int MAX_ROWS = 50000000; // From the MySQL FAQ + + /** + * Workaround for containers that 'check' for sane values of + * Statement.setFetchSize() so that applications can use + * the Java variant of libmysql's mysql_use_result() behavior. + * + * @throws SQLException + * if an error occurs + */ + void enableStreamingResults() throws SQLException; + + /** + * Resets this statements fetch size and result set type to the values + * they had before enableStreamingResults() was called. + * + * @throws SQLException + * if an error occurs + */ + void disableStreamingResults() throws SQLException; + + /** + * Sets an InputStream instance that will be used to send data + * to the MySQL server for a "LOAD DATA LOCAL INFILE" statement + * rather than a FileInputStream or URLInputStream that represents + * the path given as an argument to the statement. + * + * This stream will be read to completion upon execution of a + * "LOAD DATA LOCAL INFILE" statement, and will automatically + * be closed by the driver, so it needs to be reset + * before each call to execute*() that would cause the MySQL + * server to request data to fulfill the request for + * "LOAD DATA LOCAL INFILE". + * + * If this value is set to NULL, the driver will revert to using + * a FileInputStream or URLInputStream as required. + * + * @param stream + * input stream + */ + void setLocalInfileInputStream(InputStream stream); + + /** + * Returns the InputStream instance that will be used to send + * data in response to a "LOAD DATA LOCAL INFILE" statement. + * + * This method returns NULL if no such stream has been set + * via setLocalInfileInputStream(). + * + * @return + * input stream + */ + InputStream getLocalInfileInputStream(); + + void setPingTarget(PingTarget pingTarget); + + ExceptionInterceptor getExceptionInterceptor(); + + /** + * Callback for result set instances to remove them from the Set that + * tracks them per-statement + * + * @param rs + * result set + */ + + void removeOpenResultSet(ResultSetInternalMethods rs); + + /** + * Returns the number of open result sets for this statement. + * + * @return the number of open result sets for this statement + */ + int getOpenResultSetCount(); + + void setHoldResultsOpenOverClose(boolean holdResultsOpenOverClose); + + Query getQuery(); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlConnectionPoolDataSource.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlConnectionPoolDataSource.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlConnectionPoolDataSource.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.Connection; +import java.sql.SQLException; + +import javax.sql.ConnectionPoolDataSource; +import javax.sql.PooledConnection; + +/** + * This class is used to obtain a physical connection and instantiate and return a MysqlPooledConnection. J2EE application servers map client calls to + * dataSource.getConnection to this class based upon mapping set within deployment descriptor. This class extends MysqlDataSource. + */ +public class MysqlConnectionPoolDataSource extends MysqlDataSource implements ConnectionPoolDataSource { + + static final long serialVersionUID = -7767325445592304961L; + + /** + * Returns a pooled connection. + * + * @exception SQLException + * if an error occurs + * @return a PooledConnection + */ + public synchronized PooledConnection getPooledConnection() throws SQLException { + Connection connection = getConnection(); + MysqlPooledConnection mysqlPooledConnection = MysqlPooledConnection.getInstance((JdbcConnection) connection); + + return mysqlPooledConnection; + } + + /** + * This method is invoked by the container. Obtains physical connection + * using mySql.Driver class and returns a mysqlPooledConnection object. + * + * @param s + * user name + * @param s1 + * password + * @exception SQLException + * if an error occurs + * @return a PooledConnection + */ + public synchronized PooledConnection getPooledConnection(String s, String s1) throws SQLException { + Connection connection = getConnection(s, s1); + MysqlPooledConnection mysqlPooledConnection = MysqlPooledConnection.getInstance((JdbcConnection) connection); + + return mysqlPooledConnection; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlDataSource.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlDataSource.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlDataSource.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,663 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.PrintWriter; +import java.io.Serializable; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.Properties; +import java.util.logging.Logger; + +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.Referenceable; +import javax.naming.StringRefAddr; +import javax.sql.DataSource; + +import com.mysql.cj.Messages; +import com.mysql.cj.conf.AbstractRuntimeProperty; +import com.mysql.cj.conf.ConnectionUrl; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.ReadableProperty; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; + +/** + * A JNDI DataSource for a Mysql JDBC connection + */ +public class MysqlDataSource extends JdbcPropertySetImpl implements DataSource, Referenceable, Serializable, JdbcPropertySet { + + static final long serialVersionUID = -5515846944416881264L; + + /** The driver to create connections with */ + protected final static NonRegisteringDriver mysqlDriver; + + static { + try { + mysqlDriver = new NonRegisteringDriver(); + } catch (Exception E) { + throw new RuntimeException(Messages.getString("MysqlDataSource.0")); + } + } + + /** Log stream */ + protected transient PrintWriter logWriter = null; + + /** Database Name */ + protected String databaseName = null; + + /** Character Encoding */ + protected String encoding = null; + + /** Hostname */ + protected String hostName = null; + + /** Password */ + protected String password = null; + + /** The profileSQL property */ + protected String profileSQLString = "false"; + + /** The JDBC URL */ + protected String url = null; + + /** User name */ + protected String user = null; + + /** Should we construct the URL, or has it been set explicitly */ + protected boolean explicitUrl = false; + + /** Port number */ + protected int port = 3306; + + protected String description = "MySQL Connector/J Data Source"; + + /** + * Default no-arg constructor for Serialization + */ + public MysqlDataSource() { + } + + /** + * Creates a new connection using the already configured username and + * password. + * + * @return a connection to the database + * + * @throws SQLException + * if an error occurs + */ + public java.sql.Connection getConnection() throws SQLException { + return getConnection(this.user, this.password); + } + + /** + * Creates a new connection with the given username and password + * + * @param userID + * the user id to connect with + * @param password + * the password to connect with + * + * @return a connection to the database + * + * @throws SQLException + * if an error occurs + */ + public java.sql.Connection getConnection(String userID, String pass) throws SQLException { + Properties props = exposeAsProperties(); + + if (userID != null) { + props.setProperty(PropertyDefinitions.PNAME_user, userID); + } + + if (pass != null) { + props.setProperty(PropertyDefinitions.PNAME_password, pass); + } + + return getConnection(props); + } + + public String getDescription() { + return this.description; + } + + public void setDescription(String value) { + this.description = value; + } + + /** + * Sets the database name. + * + * @param dbName + * the name of the database + */ + public void setDatabaseName(String dbName) { + this.databaseName = dbName; + } + + /** + * Gets the name of the database + * + * @return the name of the database for this data source + */ + public String getDatabaseName() { + return (this.databaseName != null) ? this.databaseName : ""; + } + + /** + * Sets the log writer for this data source. + * + * @see javax.sql.DataSource#setLogWriter(PrintWriter) + */ + public void setLogWriter(PrintWriter output) throws SQLException { + this.logWriter = output; + } + + /** + * Returns the log writer for this data source + * + * @return the log writer for this data source + */ + public java.io.PrintWriter getLogWriter() { + return this.logWriter; + } + + /** + * @param seconds + * + * @throws SQLException + */ + public void setLoginTimeout(int seconds) throws SQLException { + } + + /** + * Returns the login timeout + * + * @return the login timeout + */ + public int getLoginTimeout() { + return 0; + } + + /** + * Sets the password + * + * @param pass + * the password + */ + public void setPassword(String pass) { + this.password = pass; + } + + /** + * Sets the database port. + * + * @param p + * the port + */ + public void setPort(int p) { + this.port = p; + } + + /** + * Returns the port number + * + * @return the port number + */ + public int getPort() { + return this.port; + } + + /** + * Sets the port number + * + * @param p + * the port + * + * @see #setPort + */ + public void setPortNumber(int p) { + setPort(p); + } + + /** + * Returns the port number + * + * @return the port number + */ + public int getPortNumber() { + return getPort(); + } + + /** + * Initializes driver properties that come from a JNDI reference (in the + * case of a javax.sql.DataSource bound into some name service that doesn't + * handle Java objects directly). + * + * @param ref + * The JNDI Reference that holds RefAddrs for all properties + */ + public void setPropertiesViaRef(Reference ref) throws SQLException { + for (String propName : PropertyDefinitions.PROPERTY_NAME_TO_PROPERTY_DEFINITION.keySet()) { + ReadableProperty propToSet = getReadableProperty(propName); + + if (ref != null) { + propToSet.initializeFrom(ref, null); + } + } + + postInitialization(); + } + + /** + * Required method to support this class as a Referenceable. + * + * @return a Reference to this data source + * + * @throws NamingException + * if a JNDI error occurs + */ + public Reference getReference() throws NamingException { + String factoryName = MysqlDataSourceFactory.class.getName(); + Reference ref = new Reference(getClass().getName(), factoryName, null); + ref.add(new StringRefAddr(PropertyDefinitions.PNAME_user, getUser())); + ref.add(new StringRefAddr(PropertyDefinitions.PNAME_password, this.password)); + ref.add(new StringRefAddr("serverName", getServerName())); + ref.add(new StringRefAddr("port", "" + getPort())); + ref.add(new StringRefAddr("databaseName", getDatabaseName())); + ref.add(new StringRefAddr("url", getUrl())); + ref.add(new StringRefAddr("explicitUrl", String.valueOf(this.explicitUrl))); + + // + // Now store all of the 'non-standard' properties... + // + for (String propName : PropertyDefinitions.PROPERTY_NAME_TO_PROPERTY_DEFINITION.keySet()) { + ReadableProperty propToStore = getReadableProperty(propName); + + String val = propToStore.getStringValue(); + if (val != null) { + ref.add(new StringRefAddr(propToStore.getPropertyDefinition().getName(), val)); + } + + } + + return ref; + } + + /** + * Sets the server name. + * + * @param serverName + * the server name + */ + public void setServerName(String serverName) { + this.hostName = serverName; + } + + /** + * Returns the name of the database server + * + * @return the name of the database server + */ + public String getServerName() { + return (this.hostName != null) ? this.hostName : ""; + } + + // + // I've seen application servers use both formats, URL or url (doh) + // + + /** + * Sets the URL for this connection + * + * @param url + * the URL for this connection + */ + public void setURL(String url) { + setUrl(url); + } + + /** + * Returns the URL for this connection + * + * @return the URL for this connection + */ + public String getURL() { + return getUrl(); + } + + /** + * This method is used by the app server to set the url string specified + * within the datasource deployment descriptor. It is discovered using + * introspection and matches if property name in descriptor is "url". + * + * @param url + * url to be used within driver.connect + */ + public void setUrl(String url) { + this.url = url; + this.explicitUrl = true; + } + + /** + * Returns the JDBC URL that will be used to create the database connection. + * + * @return the URL for this connection + */ + public String getUrl() { + if (!this.explicitUrl) { + StringBuilder sbUrl = new StringBuilder(ConnectionUrl.Type.SINGLE_CONNECTION.getProtocol()); + sbUrl.append("//").append(getServerName()).append(":").append(getPort()).append("/").append(getDatabaseName()); + return sbUrl.toString(); + } + return this.url; + } + + /** + * Sets the user ID. + * + * @param userID + * the User ID + */ + public void setUser(String userID) { + this.user = userID; + } + + /** + * Returns the configured user for this connection + * + * @return the user for this connection + */ + public String getUser() { + return this.user; + } + + /** + * Creates a connection using the specified properties. + * + * @param props + * the properties to connect with + * + * @return a connection to the database + * + * @throws SQLException + * if an error occurs + */ + protected java.sql.Connection getConnection(Properties props) throws SQLException { + String jdbcUrlToUse = null; + + if (!this.explicitUrl) { + jdbcUrlToUse = getUrl(); + } else { + jdbcUrlToUse = this.url; + } + + // + // URL should take precedence over properties + // + ConnectionUrl connUrl = ConnectionUrl.getConnectionUrlInstance(jdbcUrlToUse, null); + if (connUrl.getType() == null) { + throw SQLError.createSQLException(Messages.getString("MysqlDataSource.BadUrl", new Object[] { jdbcUrlToUse }), + MysqlErrorNumbers.SQL_STATE_CONNECTION_FAILURE, null); + } + Properties urlProps = connUrl.getConnectionArgumentsAsProperties(); + urlProps.remove(PropertyDefinitions.DBNAME_PROPERTY_KEY); + urlProps.remove(PropertyDefinitions.HOST_PROPERTY_KEY); + urlProps.remove(PropertyDefinitions.PORT_PROPERTY_KEY); + urlProps.stringPropertyNames().stream().forEach(k -> props.setProperty(k, urlProps.getProperty(k))); + + return mysqlDriver.connect(jdbcUrlToUse, props); + } + + // + // public boolean isWrapperFor(Class iface) throws SQLException { + // throw SQLError.createSQLFeatureNotSupportedException(); + // } + // + // public T unwrap(Class iface) throws SQLException { + // throw SQLError.createSQLFeatureNotSupportedException(); + // } + + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return null; + } + + public T unwrap(Class iface) throws SQLException { + return null; + } + + public boolean isWrapperFor(Class iface) throws SQLException { + return false; + } + + /** + * Used in properties getters added by instrumentation. + * + * @param name + * property name property name + * @return + * @throws SQLException + */ + protected String getStringProperty(String name) throws SQLException { + return getStringReadableProperty(name).getValue(); + } + + /** + * Used in properties setters added by instrumentation. + * + * @param name + * property name + * @param value + * value + * @throws SQLException + */ + @SuppressWarnings("unchecked") + protected void setStringProperty(String name, String value) throws SQLException { + ReadableProperty prop = getStringReadableProperty(name); + if (prop.getPropertyDefinition().isRuntimeModifiable()) { + getStringModifiableProperty(name).setValue(value); + } else { + ((AbstractRuntimeProperty) prop).setFromString(value, null); + } + } + + /** + * Used in properties getters added by instrumentation. + * + * @param name + * property name + * @return + * @throws SQLException + */ + protected boolean getBooleanProperty(String name) throws SQLException { + return getBooleanReadableProperty(name).getValue(); + } + + /** + * Used in properties setters added by instrumentation. + * + * @param name + * property name + * @param value + * value + * @throws SQLException + */ + @SuppressWarnings("unchecked") + protected void setBooleanProperty(String name, boolean value) throws SQLException { + ReadableProperty prop = getBooleanReadableProperty(name); + if (prop.getPropertyDefinition().isRuntimeModifiable()) { + getBooleanModifiableProperty(name).setValue(value); + } else { + ((AbstractRuntimeProperty) prop).setFromString("" + value, null); + } + } + + /** + * Used in properties getters added by instrumentation. + * + * @param name + * property name + * @return + * @throws SQLException + */ + protected int getIntegerProperty(String name) throws SQLException { + return getIntegerReadableProperty(name).getValue(); + } + + /** + * Used in properties setters added by instrumentation. + * + * @param name + * property name + * @param value + * value + * @throws SQLException + */ + @SuppressWarnings("unchecked") + protected void setIntegerProperty(String name, int value) throws SQLException { + ReadableProperty prop = getIntegerReadableProperty(name); + if (prop.getPropertyDefinition().isRuntimeModifiable()) { + getIntegerModifiableProperty(name).setValue(value); + } else { + ((AbstractRuntimeProperty) prop).setFromString("" + value, null); + } + } + + /** + * Used in properties getters added by instrumentation. + * + * @param name + * property name + * @return + * @throws SQLException + */ + protected long getLongProperty(String name) throws SQLException { + return getLongReadableProperty(name).getValue(); + } + + /** + * Used in properties setters added by instrumentation. + * + * @param name + * property name + * @param value + * value + * @throws SQLException + */ + @SuppressWarnings("unchecked") + protected void setLongProperty(String name, long value) throws SQLException { + ReadableProperty prop = getLongReadableProperty(name); + if (prop.getPropertyDefinition().isRuntimeModifiable()) { + getLongModifiableProperty(name).setValue(value); + } else { + ((AbstractRuntimeProperty) prop).setFromString("" + value, null); + } + } + + /** + * Used in properties getters added by instrumentation. + * + * @param name + * property name + * @return + * @throws SQLException + */ + protected int getMemorySizeProperty(String name) throws SQLException { + return getMemorySizeReadableProperty(name).getValue(); + } + + /** + * Used in properties setters added by instrumentation. + * + * @param name + * property name + * @param value + * value + * @throws SQLException + */ + @SuppressWarnings("unchecked") + protected void setMemorySizeProperty(String name, int value) throws SQLException { + ReadableProperty prop = getIntegerReadableProperty(name); + if (prop.getPropertyDefinition().isRuntimeModifiable()) { + getMemorySizeModifiableProperty(name).setValue(value); + } else { + ((AbstractRuntimeProperty) prop).setFromString("" + value, null); + } + } + + /** + * Used in properties getters added by instrumentation. + * + * @param name + * property name + * @return + * @throws SQLException + */ + protected String getEnumProperty(String name) throws SQLException { + return getEnumReadableProperty(name).getStringValue(); + } + + /** + * Used in properties setters added by instrumentation. + * + * @param name + * property name + * @param value + * value + * @throws SQLException + */ + protected void setEnumProperty(String name, String value) throws SQLException { + ReadableProperty prop = getEnumReadableProperty(name); + if (prop.getPropertyDefinition().isRuntimeModifiable()) { + getEnumModifiableProperty(name).setFromString(value, null); + } else { + ((AbstractRuntimeProperty) prop).setFromString("" + value, null); + } + } + + @Override + public Properties exposeAsProperties() { + Properties props = new Properties(); + + for (String propName : PropertyDefinitions.PROPERTY_NAME_TO_PROPERTY_DEFINITION.keySet()) { + ReadableProperty propToGet = getReadableProperty(propName); + + String propValue = propToGet.getStringValue(); + + if (propValue != null && propToGet.isExplicitlySet()) { + props.setProperty(propToGet.getPropertyDefinition().getName(), propValue); + } + } + + return props; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlDataSourceFactory.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlDataSourceFactory.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlDataSourceFactory.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.spi.ObjectFactory; + +import com.mysql.cj.Messages; +import com.mysql.cj.conf.PropertyDefinitions; + +/** + * Factory class for MysqlDataSource objects + */ +public class MysqlDataSourceFactory implements ObjectFactory { + /** + * The class name for a standard MySQL DataSource. + */ + protected final static String DATA_SOURCE_CLASS_NAME = MysqlDataSource.class.getName(); + + /** + * The class name for a poolable MySQL DataSource. + */ + protected final static String POOL_DATA_SOURCE_CLASS_NAME = MysqlConnectionPoolDataSource.class.getName(); + + /** + * The class name for a MysqlXADataSource + */ + + protected final static String XA_DATA_SOURCE_CLASS_NAME = MysqlXADataSource.class.getName(); + + /** + * @param refObj + * @param nm + * @param ctx + * @param env + * @throws Exception + */ + public Object getObjectInstance(Object refObj, Name nm, Context ctx, Hashtable env) throws Exception { + Reference ref = (Reference) refObj; + String className = ref.getClassName(); + + if ((className != null) + && (className.equals(DATA_SOURCE_CLASS_NAME) || className.equals(POOL_DATA_SOURCE_CLASS_NAME) || className.equals(XA_DATA_SOURCE_CLASS_NAME))) { + MysqlDataSource dataSource = null; + + try { + dataSource = (MysqlDataSource) Class.forName(className).newInstance(); + } catch (Exception ex) { + throw new RuntimeException(Messages.getString("MysqlDataSourceFactory.0", new Object[] { className, ex.toString() })); + } + + int portNumber = 3306; + + String portNumberAsString = nullSafeRefAddrStringGet("port", ref); + + if (portNumberAsString != null) { + portNumber = Integer.parseInt(portNumberAsString); + } + + dataSource.setPort(portNumber); + + String user = nullSafeRefAddrStringGet(PropertyDefinitions.PNAME_user, ref); + + if (user != null) { + dataSource.setUser(user); + } + + String password = nullSafeRefAddrStringGet(PropertyDefinitions.PNAME_password, ref); + + if (password != null) { + dataSource.setPassword(password); + } + + String serverName = nullSafeRefAddrStringGet("serverName", ref); + + if (serverName != null) { + dataSource.setServerName(serverName); + } + + String databaseName = nullSafeRefAddrStringGet("databaseName", ref); + + if (databaseName != null) { + dataSource.setDatabaseName(databaseName); + } + + String explicitUrlAsString = nullSafeRefAddrStringGet("explicitUrl", ref); + + if (explicitUrlAsString != null) { + if (Boolean.valueOf(explicitUrlAsString).booleanValue()) { + dataSource.setUrl(nullSafeRefAddrStringGet("url", ref)); + } + } + + dataSource.setPropertiesViaRef(ref); + + return dataSource; + } + + // We can't create an instance of the reference + return null; + } + + private String nullSafeRefAddrStringGet(String referenceName, Reference ref) { + RefAddr refAddr = ref.get(referenceName); + + String asString = refAddr != null ? (String) refAddr.getContent() : null; + + return asString; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlParameterMetadata.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlParameterMetadata.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlParameterMetadata.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.ParameterMetaData; +import java.sql.SQLException; + +import com.mysql.cj.Messages; +import com.mysql.cj.MysqlType; +import com.mysql.cj.Session; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.result.ResultSetMetaData; +import com.mysql.cj.result.Field; + +public class MysqlParameterMetadata implements ParameterMetaData { + boolean returnSimpleMetadata = false; + + ResultSetMetaData metadata = null; + + int parameterCount = 0; + + private ExceptionInterceptor exceptionInterceptor; + + MysqlParameterMetadata(Session session, Field[] fieldInfo, int parameterCount, ExceptionInterceptor exceptionInterceptor) { + this.metadata = new ResultSetMetaData(session, fieldInfo, false, true, exceptionInterceptor); + + this.parameterCount = parameterCount; + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * Used for "fake" basic metadata for client-side prepared statements when + * we don't know the parameter types. + * + * @param parameterCount + */ + MysqlParameterMetadata(int count) { + this.parameterCount = count; + this.returnSimpleMetadata = true; + } + + public int getParameterCount() throws SQLException { + return this.parameterCount; + } + + public int isNullable(int arg0) throws SQLException { + checkAvailable(); + + return this.metadata.isNullable(arg0); + } + + private void checkAvailable() throws SQLException { + if (this.metadata == null || this.metadata.getFields() == null) { + throw SQLError.createSQLException(Messages.getString("MysqlParameterMetadata.0"), MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, + this.exceptionInterceptor); + } + } + + public boolean isSigned(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return false; + } + + checkAvailable(); + + return (this.metadata.isSigned(arg0)); + } + + public int getPrecision(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return 0; + } + + checkAvailable(); + + return (this.metadata.getPrecision(arg0)); + } + + public int getScale(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return 0; + } + + checkAvailable(); + + return (this.metadata.getScale(arg0)); + } + + public int getParameterType(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return MysqlType.VARCHAR.getJdbcType(); + } + + checkAvailable(); + + return (this.metadata.getColumnType(arg0)); + } + + public String getParameterTypeName(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return MysqlType.VARCHAR.getName(); + } + + checkAvailable(); + + return (this.metadata.getColumnTypeName(arg0)); + } + + public String getParameterClassName(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return "java.lang.String"; + } + + checkAvailable(); + + return (this.metadata.getColumnClassName(arg0)); + } + + public int getParameterMode(int arg0) throws SQLException { + return parameterModeIn; + } + + private void checkBounds(int paramNumber) throws SQLException { + if (paramNumber < 1) { + throw SQLError.createSQLException(Messages.getString("MysqlParameterMetadata.1", new Object[] { paramNumber }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + if (paramNumber > this.parameterCount) { + throw SQLError.createSQLException(Messages.getString("MysqlParameterMetadata.2", new Object[] { paramNumber, this.parameterCount }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + + } + } + + /** + * @see java.sql.Wrapper#isWrapperFor(Class) + */ + public boolean isWrapperFor(Class iface) throws SQLException { + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + /** + * @see java.sql.Wrapper#unwrap(Class) + */ + public T unwrap(Class iface) throws SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("Common.UnableToUnwrap", new Object[] { iface.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlPooledConnection.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlPooledConnection.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlPooledConnection.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.sql.ConnectionEvent; +import javax.sql.ConnectionEventListener; +import javax.sql.PooledConnection; +import javax.sql.StatementEvent; +import javax.sql.StatementEventListener; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.jdbc.exceptions.SQLError; + +/** + * This class is used to wrap and return a physical connection within a logical handle. It also registers and notifies ConnectionEventListeners of any + * ConnectionEvents + */ +public class MysqlPooledConnection implements PooledConnection { + + protected static MysqlPooledConnection getInstance(com.mysql.cj.jdbc.JdbcConnection connection) throws SQLException { + return new MysqlPooledConnection(connection); + } + + /** + * The flag for an exception being thrown. + */ + public static final int CONNECTION_ERROR_EVENT = 1; + + /** + * The flag for a connection being closed. + */ + public static final int CONNECTION_CLOSED_EVENT = 2; + + private Map connectionEventListeners; + + private Connection logicalHandle; + + private com.mysql.cj.jdbc.JdbcConnection physicalConn; + + private ExceptionInterceptor exceptionInterceptor; + + private final Map statementEventListeners = new HashMap<>(); + + /** + * Construct a new MysqlPooledConnection and set instance variables + * + * @param connection + * physical connection to db + */ + public MysqlPooledConnection(com.mysql.cj.jdbc.JdbcConnection connection) { + this.logicalHandle = null; + this.physicalConn = connection; + this.connectionEventListeners = new HashMap<>(); + this.exceptionInterceptor = this.physicalConn.getExceptionInterceptor(); + } + + /** + * Adds ConnectionEventListeners to a hash table to be used for notification + * of ConnectionEvents + * + * @param connectioneventlistener + * listener to be notified with ConnectionEvents + */ + public synchronized void addConnectionEventListener(ConnectionEventListener connectioneventlistener) { + + if (this.connectionEventListeners != null) { + this.connectionEventListeners.put(connectioneventlistener, connectioneventlistener); + } + } + + /** + * Removes ConnectionEventListeners from hash table used for notification of + * ConnectionEvents + * + * @param connectioneventlistener + * listener to be removed + */ + public synchronized void removeConnectionEventListener(ConnectionEventListener connectioneventlistener) { + + if (this.connectionEventListeners != null) { + this.connectionEventListeners.remove(connectioneventlistener); + } + } + + /** + * Invoked by the container. Return a logicalHandle object that wraps a + * physical connection. + * + * @see javax.sql.DataSource#getConnection() + */ + public synchronized Connection getConnection() throws SQLException { + return getConnection(true, false); + + } + + protected synchronized Connection getConnection(boolean resetServerState, boolean forXa) throws SQLException { + if (this.physicalConn == null) { + + SQLException sqlException = SQLError.createSQLException(Messages.getString("MysqlPooledConnection.0"), this.exceptionInterceptor); + callConnectionEventListeners(CONNECTION_ERROR_EVENT, sqlException); + + throw sqlException; + } + + try { + + if (this.logicalHandle != null) { + ((ConnectionWrapper) this.logicalHandle).close(false); + } + + if (resetServerState) { + this.physicalConn.resetServerState(); + } + + this.logicalHandle = ConnectionWrapper.getInstance(this, this.physicalConn, forXa); + } catch (SQLException sqlException) { + callConnectionEventListeners(CONNECTION_ERROR_EVENT, sqlException); + + throw sqlException; + } + + return this.logicalHandle; + } + + /** + * Invoked by the container (not the client), and should close the physical + * connection. This will be called if the pool is destroyed or the + * connectionEventListener receives a connectionErrorOccurred event. + * + * @see javax.sql.PooledConnection#close() + */ + public synchronized void close() throws SQLException { + if (this.physicalConn != null) { + this.physicalConn.close(); + + this.physicalConn = null; + } + + if (this.connectionEventListeners != null) { + this.connectionEventListeners.clear(); + + this.connectionEventListeners = null; + } + + this.statementEventListeners.clear(); + } + + /** + * Notifies all registered ConnectionEventListeners of ConnectionEvents. + * Instantiates a new ConnectionEvent which wraps sqlException and invokes + * either connectionClose or connectionErrorOccurred on listener as + * appropriate. + * + * @param eventType + * value indicating whether connectionClosed or + * connectionErrorOccurred called + * @param sqlException + * the exception being thrown + */ + protected synchronized void callConnectionEventListeners(int eventType, SQLException sqlException) { + + if (this.connectionEventListeners == null) { + + return; + } + + Iterator> iterator = this.connectionEventListeners.entrySet().iterator(); + + ConnectionEvent connectionevent = new ConnectionEvent(this, sqlException); + + while (iterator.hasNext()) { + + ConnectionEventListener connectioneventlistener = iterator.next().getValue(); + + if (eventType == CONNECTION_CLOSED_EVENT) { + connectioneventlistener.connectionClosed(connectionevent); + } else if (eventType == CONNECTION_ERROR_EVENT) { + connectioneventlistener.connectionErrorOccurred(connectionevent); + } + } + } + + protected ExceptionInterceptor getExceptionInterceptor() { + return this.exceptionInterceptor; + } + + /** + * Registers a StatementEventListener with this PooledConnection object. Components that + * wish to be notified when PreparedStatements created by the + * connection are closed or are detected to be invalid may use this method + * to register a StatementEventListener with this PooledConnection object. + * + * @param listener + * an component which implements the StatementEventListener interface that is to be registered with this + * PooledConnection object + * + * @since 1.6 + */ + public void addStatementEventListener(StatementEventListener listener) { + synchronized (this.statementEventListeners) { + this.statementEventListeners.put(listener, listener); + } + } + + /** + * Removes the specified StatementEventListener from the list of + * components that will be notified when the driver detects that a PreparedStatement has been closed or is invalid. + * + * @param listener + * the component which implements the StatementEventListener interface that was previously + * registered with this PooledConnection object + * + * @since 1.6 + */ + public void removeStatementEventListener(StatementEventListener listener) { + synchronized (this.statementEventListeners) { + this.statementEventListeners.remove(listener); + } + } + + void fireStatementEvent(StatementEvent event) throws SQLException { + synchronized (this.statementEventListeners) { + for (StatementEventListener listener : this.statementEventListeners.keySet()) { + listener.statementClosed(event); + } + } + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlSQLXML.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlSQLXML.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlSQLXML.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,610 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.sql.SQLException; +import java.sql.SQLXML; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stax.StAXResult; +import javax.xml.transform.stax.StAXSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; + +public class MysqlSQLXML implements SQLXML { + + private XMLInputFactory inputFactory; + + private XMLOutputFactory outputFactory; + + private String stringRep; + + private ResultSetInternalMethods owningResultSet; + + private int columnIndexOfXml; + + private boolean fromResultSet; + + private boolean isClosed = false; + + private boolean workingWithResult; + + private DOMResult asDOMResult; + + private SAXResult asSAXResult; + + private SimpleSaxToReader saxToReaderConverter; + + private StringWriter asStringWriter; + + private ByteArrayOutputStream asByteArrayOutputStream; + + private ExceptionInterceptor exceptionInterceptor; + + public MysqlSQLXML(ResultSetInternalMethods owner, int index, ExceptionInterceptor exceptionInterceptor) { + this.owningResultSet = owner; + this.columnIndexOfXml = index; + this.fromResultSet = true; + this.exceptionInterceptor = exceptionInterceptor; + } + + public MysqlSQLXML(ExceptionInterceptor exceptionInterceptor) { + this.fromResultSet = false; + this.exceptionInterceptor = exceptionInterceptor; + } + + public synchronized void free() throws SQLException { + this.stringRep = null; + this.asDOMResult = null; + this.asSAXResult = null; + this.inputFactory = null; + this.outputFactory = null; + this.owningResultSet = null; + this.workingWithResult = false; + this.isClosed = true; + + } + + public synchronized String getString() throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + if (this.fromResultSet) { + return this.owningResultSet.getString(this.columnIndexOfXml); + } + + return this.stringRep; + } + + private synchronized void checkClosed() throws SQLException { + if (this.isClosed) { + throw SQLError.createSQLException(Messages.getString("MysqlSQLXML.0"), this.exceptionInterceptor); + } + } + + private synchronized void checkWorkingWithResult() throws SQLException { + if (this.workingWithResult) { + throw SQLError.createSQLException(Messages.getString("MysqlSQLXML.1"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } + + public synchronized void setString(String str) throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + this.stringRep = str; + this.fromResultSet = false; + } + + public synchronized boolean isEmpty() throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + if (!this.fromResultSet) { + return this.stringRep == null || this.stringRep.length() == 0; + } + + return false; + } + + public synchronized InputStream getBinaryStream() throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + return this.owningResultSet.getBinaryStream(this.columnIndexOfXml); + } + + public synchronized Reader getCharacterStream() throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + return this.owningResultSet.getCharacterStream(this.columnIndexOfXml); + } + + @SuppressWarnings("unchecked") + public T getSource(Class clazz) throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + // Note that we try and use streams here wherever possible for the day that the server actually supports streaming from server -> client + // (futureproofing) + + if (clazz == null || clazz.equals(SAXSource.class)) { + + InputSource inputSource = null; + + if (this.fromResultSet) { + inputSource = new InputSource(this.owningResultSet.getCharacterStream(this.columnIndexOfXml)); + } else { + inputSource = new InputSource(new StringReader(this.stringRep)); + } + + return (T) new SAXSource(inputSource); + } else if (clazz.equals(DOMSource.class)) { + try { + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + builderFactory.setNamespaceAware(true); + DocumentBuilder builder = builderFactory.newDocumentBuilder(); + + InputSource inputSource = null; + + if (this.fromResultSet) { + inputSource = new InputSource(this.owningResultSet.getCharacterStream(this.columnIndexOfXml)); + } else { + inputSource = new InputSource(new StringReader(this.stringRep)); + } + + return (T) new DOMSource(builder.parse(inputSource)); + } catch (Throwable t) { + SQLException sqlEx = SQLError.createSQLException(t.getMessage(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, t, this.exceptionInterceptor); + throw sqlEx; + } + + } else if (clazz.equals(StreamSource.class)) { + Reader reader = null; + + if (this.fromResultSet) { + reader = this.owningResultSet.getCharacterStream(this.columnIndexOfXml); + } else { + reader = new StringReader(this.stringRep); + } + + return (T) new StreamSource(reader); + } else if (clazz.equals(StAXSource.class)) { + try { + Reader reader = null; + + if (this.fromResultSet) { + reader = this.owningResultSet.getCharacterStream(this.columnIndexOfXml); + } else { + reader = new StringReader(this.stringRep); + } + + return (T) new StAXSource(this.inputFactory.createXMLStreamReader(reader)); + } catch (XMLStreamException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, ex, this.exceptionInterceptor); + throw sqlEx; + } + } else { + throw SQLError.createSQLException(Messages.getString("MysqlSQLXML.2", new Object[] { clazz.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } + + public synchronized OutputStream setBinaryStream() throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + this.workingWithResult = true; + + return setBinaryStreamInternal(); + } + + private synchronized OutputStream setBinaryStreamInternal() throws SQLException { + this.asByteArrayOutputStream = new ByteArrayOutputStream(); + + return this.asByteArrayOutputStream; + } + + public synchronized Writer setCharacterStream() throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + this.workingWithResult = true; + + return setCharacterStreamInternal(); + } + + private synchronized Writer setCharacterStreamInternal() throws SQLException { + this.asStringWriter = new StringWriter(); + + return this.asStringWriter; + } + + @SuppressWarnings("unchecked") + public synchronized T setResult(Class clazz) throws SQLException { + checkClosed(); + checkWorkingWithResult(); + + this.workingWithResult = true; + this.asDOMResult = null; + this.asSAXResult = null; + this.saxToReaderConverter = null; + this.stringRep = null; + this.asStringWriter = null; + this.asByteArrayOutputStream = null; + + if (clazz == null || clazz.equals(SAXResult.class)) { + this.saxToReaderConverter = new SimpleSaxToReader(); + + this.asSAXResult = new SAXResult(this.saxToReaderConverter); + + return (T) this.asSAXResult; + } else if (clazz.equals(DOMResult.class)) { + + this.asDOMResult = new DOMResult(); + return (T) this.asDOMResult; + + } else if (clazz.equals(StreamResult.class)) { + return (T) new StreamResult(setCharacterStreamInternal()); + } else if (clazz.equals(StAXResult.class)) { + try { + if (this.outputFactory == null) { + this.outputFactory = XMLOutputFactory.newInstance(); + } + + return (T) new StAXResult(this.outputFactory.createXMLEventWriter(setCharacterStreamInternal())); + } catch (XMLStreamException ex) { + SQLException sqlEx = SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, ex, this.exceptionInterceptor); + throw sqlEx; + } + } else { + throw SQLError.createSQLException(Messages.getString("MysqlSQLXML.3", new Object[] { clazz.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } + + private Reader binaryInputStreamStreamToReader(ByteArrayOutputStream out) { + + try { + // There's got to be an easier way to do this, but I don't feel like coding up Appendix F of the XML Spec myself, when there's a reusable way to do + // it, and we can warn folks away from BINARY xml streams that have to be parsed to determine the character encoding :P + + String encoding = "UTF-8"; + + try { + ByteArrayInputStream bIn = new ByteArrayInputStream(out.toByteArray()); + XMLStreamReader reader = this.inputFactory.createXMLStreamReader(bIn); + + int eventType = 0; + + while ((eventType = reader.next()) != XMLStreamConstants.END_DOCUMENT) { + if (eventType == XMLStreamConstants.START_DOCUMENT) { + String possibleEncoding = reader.getEncoding(); + + if (possibleEncoding != null) { + encoding = possibleEncoding; + } + + break; + } + } + } catch (Throwable t) { + // ignore, dealt with later when the string can't be parsed into valid XML + } + + return new StringReader(new String(out.toByteArray(), encoding)); + } catch (UnsupportedEncodingException badEnc) { + throw new RuntimeException(badEnc); + } + } + + protected String readerToString(Reader reader) throws SQLException { + StringBuilder buf = new StringBuilder(); + + int charsRead = 0; + + char[] charBuf = new char[512]; + + try { + while ((charsRead = reader.read(charBuf)) != -1) { + buf.append(charBuf, 0, charsRead); + } + } catch (IOException ioEx) { + SQLException sqlEx = SQLError.createSQLException(ioEx.getMessage(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, ioEx, this.exceptionInterceptor); + throw sqlEx; + } + + return buf.toString(); + } + + protected synchronized Reader serializeAsCharacterStream() throws SQLException { + checkClosed(); + if (this.workingWithResult) { + // figure out what kind of result + if (this.stringRep != null) { + return new StringReader(this.stringRep); + } + + if (this.asDOMResult != null) { + return new StringReader(domSourceToString()); + } + + if (this.asStringWriter != null) { // stax result + return new StringReader(this.asStringWriter.toString()); + } + + if (this.asSAXResult != null) { + return this.saxToReaderConverter.toReader(); + } + + if (this.asByteArrayOutputStream != null) { + return binaryInputStreamStreamToReader(this.asByteArrayOutputStream); + } + } + + return this.owningResultSet.getCharacterStream(this.columnIndexOfXml); + } + + protected String domSourceToString() throws SQLException { + try { + DOMSource source = new DOMSource(this.asDOMResult.getNode()); + Transformer identity = TransformerFactory.newInstance().newTransformer(); + StringWriter stringOut = new StringWriter(); + Result result = new StreamResult(stringOut); + identity.transform(source, result); + + return stringOut.toString(); + } catch (Throwable t) { + SQLException sqlEx = SQLError.createSQLException(t.getMessage(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, t, this.exceptionInterceptor); + throw sqlEx; + } + } + + protected synchronized String serializeAsString() throws SQLException { + checkClosed(); + if (this.workingWithResult) { + // figure out what kind of result + if (this.stringRep != null) { + return this.stringRep; + } + + if (this.asDOMResult != null) { + return domSourceToString(); + } + + if (this.asStringWriter != null) { // stax result + return this.asStringWriter.toString(); + } + + if (this.asSAXResult != null) { + return readerToString(this.saxToReaderConverter.toReader()); + } + + if (this.asByteArrayOutputStream != null) { + return readerToString(binaryInputStreamStreamToReader(this.asByteArrayOutputStream)); + } + } + + return this.owningResultSet.getString(this.columnIndexOfXml); + } + + /* + * The SimpleSaxToReader class is an adaptation of the SAX "Writer" + * example from the Apache XercesJ-2 Project. The license for this + * code is as follows: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + class SimpleSaxToReader extends DefaultHandler { + StringBuilder buf = new StringBuilder(); + + @Override + public void startDocument() throws SAXException { + this.buf.append(""); + } + + @Override + public void endDocument() throws SAXException { + // Do we need to override this? + } + + @Override + public void startElement(String namespaceURI, String sName, String qName, Attributes attrs) throws SAXException { + + this.buf.append("<"); + this.buf.append(qName); + + if (attrs != null) { + for (int i = 0; i < attrs.getLength(); i++) { + this.buf.append(" "); + this.buf.append(attrs.getQName(i)).append("=\""); + escapeCharsForXml(attrs.getValue(i), true); + this.buf.append("\""); + } + } + + this.buf.append(">"); + } + + @Override + public void characters(char buffer[], int offset, int len) throws SAXException { + if (!this.inCDATA) { + escapeCharsForXml(buffer, offset, len, false); + } else { + this.buf.append(buffer, offset, len); + } + } + + @Override + public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { + characters(ch, start, length); + } + + private boolean inCDATA = false; + + public void startCDATA() throws SAXException { + this.buf.append(""); + } + + public void comment(char ch[], int start, int length) throws SAXException { + // if (!fCanonical && fElementDepth > 0) { + this.buf.append(""); + // } + } + + Reader toReader() { + return new StringReader(this.buf.toString()); + } + + private void escapeCharsForXml(String str, boolean isAttributeData) { + if (str == null) { + return; + } + + int strLen = str.length(); + + for (int i = 0; i < strLen; i++) { + escapeCharsForXml(str.charAt(i), isAttributeData); + } + } + + private void escapeCharsForXml(char[] buffer, int offset, int len, boolean isAttributeData) { + + if (buffer == null) { + return; + } + + for (int i = 0; i < len; i++) { + escapeCharsForXml(buffer[offset + i], isAttributeData); + } + } + + private void escapeCharsForXml(char c, boolean isAttributeData) { + switch (c) { + case '<': + this.buf.append("<"); + break; + + case '>': + this.buf.append(">"); + break; + + case '&': + this.buf.append("&"); + break; + + case '"': + + if (!isAttributeData) { + this.buf.append("\""); + } else { + this.buf.append("""); + } + + break; + + case '\r': + this.buf.append(" "); + break; + + default: + + if (((c >= 0x01 && c <= 0x1F && c != 0x09 && c != 0x0A) || (c >= 0x7F && c <= 0x9F) || c == 0x2028) + || isAttributeData && (c == 0x09 || c == 0x0A)) { + this.buf.append("&#x"); + this.buf.append(Integer.toHexString(c).toUpperCase()); + this.buf.append(";"); + } else { + this.buf.append(c); + } + } + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlSavepoint.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlSavepoint.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlSavepoint.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.SQLException; +import java.sql.Savepoint; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.util.StringUtils; + +/** + * Represents SQL SAVEPOINTS in MySQL. + */ +public class MysqlSavepoint implements Savepoint { + + private String savepointName; + + private ExceptionInterceptor exceptionInterceptor; + + /** + * Creates an unnamed savepoint. + * + * @param conn + * + * @throws SQLException + * if an error occurs + */ + MysqlSavepoint(ExceptionInterceptor exceptionInterceptor) throws SQLException { + this(StringUtils.getUniqueSavepointId(), exceptionInterceptor); + } + + /** + * Creates a named savepoint + * + * @param name + * the name of the savepoint. + * + * @throws SQLException + * if name == null or is empty. + */ + MysqlSavepoint(String name, ExceptionInterceptor exceptionInterceptor) throws SQLException { + if (name == null || name.length() == 0) { + throw SQLError.createSQLException(Messages.getString("MysqlSavepoint.0"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, exceptionInterceptor); + } + + this.savepointName = name; + + this.exceptionInterceptor = exceptionInterceptor; + } + + /** + * @see java.sql.Savepoint#getSavepointId() + */ + public int getSavepointId() throws SQLException { + throw SQLError.createSQLException(Messages.getString("MysqlSavepoint.1"), MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, this.exceptionInterceptor); + } + + /** + * @see java.sql.Savepoint#getSavepointName() + */ + public String getSavepointName() throws SQLException { + return this.savepointName; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlXAConnection.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlXAConnection.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlXAConnection.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.sql.XAConnection; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import com.mysql.cj.Messages; +import com.mysql.cj.log.Log; +import com.mysql.cj.util.StringUtils; + +public class MysqlXAConnection extends MysqlPooledConnection implements XAConnection, XAResource { + + private static final int MAX_COMMAND_LENGTH = 300; + + private com.mysql.cj.jdbc.JdbcConnection underlyingConnection; + + private final static Map MYSQL_ERROR_CODES_TO_XA_ERROR_CODES; + + private Log log; + + protected boolean logXaCommands; + + static { + HashMap temp = new HashMap<>(); + + temp.put(1397, XAException.XAER_NOTA); + temp.put(1398, XAException.XAER_INVAL); + temp.put(1399, XAException.XAER_RMFAIL); + temp.put(1400, XAException.XAER_OUTSIDE); + temp.put(1401, XAException.XAER_RMERR); + temp.put(1402, XAException.XA_RBROLLBACK); + temp.put(1440, XAException.XAER_DUPID); + temp.put(1613, XAException.XA_RBTIMEOUT); + temp.put(1614, XAException.XA_RBDEADLOCK); + + MYSQL_ERROR_CODES_TO_XA_ERROR_CODES = Collections.unmodifiableMap(temp); + } + + protected static MysqlXAConnection getInstance(JdbcConnection mysqlConnection, boolean logXaCommands) throws SQLException { + return new MysqlXAConnection(mysqlConnection, logXaCommands); + } + + public MysqlXAConnection(JdbcConnection connection, boolean logXaCommands) { + super(connection); + this.underlyingConnection = connection; + this.log = connection.getSession().getLog(); + this.logXaCommands = logXaCommands; + } + + public XAResource getXAResource() throws SQLException { + return this; + } + + public int getTransactionTimeout() throws XAException { + return 0; + } + + public boolean setTransactionTimeout(int arg0) throws XAException { + return false; + } + + public boolean isSameRM(XAResource xares) throws XAException { + + if (xares instanceof MysqlXAConnection) { + return this.underlyingConnection.isSameResource(((MysqlXAConnection) xares).underlyingConnection); + } + + return false; + } + + public Xid[] recover(int flag) throws XAException { + return recover(this.underlyingConnection, flag); + } + + protected static Xid[] recover(Connection c, int flag) throws XAException { + /* + * The XA RECOVER statement returns information for those XA transactions on the MySQL server that are in the PREPARED state. (See Section 13.4.7.2, �XA + * Transaction States�.) The output includes a row for each such XA transaction on the server, regardless of which client started it. + * + * XA RECOVER output rows look like this (for an example xid value consisting of the parts 'abc', 'def', and 7): + * + * mysql> XA RECOVER; + * +----------+--------------+--------------+--------+ + * | formatID | gtrid_length | bqual_length | data | + * +----------+--------------+--------------+--------+ + * | 7 | 3 | 3 | abcdef | + * +----------+--------------+--------------+--------+ + * + * The output columns have the following meanings: + * + * formatID is the formatID part of the transaction xid + * gtrid_length is the length in bytes of the gtrid part of the xid + * bqual_length is the length in bytes of the bqual part of the xid + * data is the concatenation of the gtrid and bqual parts of the xid + */ + + boolean startRscan = ((flag & TMSTARTRSCAN) > 0); + boolean endRscan = ((flag & TMENDRSCAN) > 0); + + if (!startRscan && !endRscan && flag != TMNOFLAGS) { + throw new MysqlXAException(XAException.XAER_INVAL, Messages.getString("MysqlXAConnection.001"), null); + } + + // + // We return all recovered XIDs at once, so if not TMSTARTRSCAN, return no new XIDs + // + // We don't attempt to maintain state to check for TMNOFLAGS "outside" of a scan + // + + if (!startRscan) { + return new Xid[0]; + } + + ResultSet rs = null; + Statement stmt = null; + + List recoveredXidList = new ArrayList<>(); + + try { + // TODO: Cache this for lifetime of XAConnection + stmt = c.createStatement(); + + rs = stmt.executeQuery("XA RECOVER"); + + while (rs.next()) { + final int formatId = rs.getInt(1); + int gtridLength = rs.getInt(2); + int bqualLength = rs.getInt(3); + byte[] gtridAndBqual = rs.getBytes(4); + + final byte[] gtrid = new byte[gtridLength]; + final byte[] bqual = new byte[bqualLength]; + + if (gtridAndBqual.length != (gtridLength + bqualLength)) { + throw new MysqlXAException(XAException.XA_RBPROTO, Messages.getString("MysqlXAConnection.002"), null); + } + + System.arraycopy(gtridAndBqual, 0, gtrid, 0, gtridLength); + System.arraycopy(gtridAndBqual, gtridLength, bqual, 0, bqualLength); + + recoveredXidList.add(new MysqlXid(gtrid, bqual, formatId)); + } + } catch (SQLException sqlEx) { + throw mapXAExceptionFromSQLException(sqlEx); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException sqlEx) { + throw mapXAExceptionFromSQLException(sqlEx); + } + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { + throw mapXAExceptionFromSQLException(sqlEx); + } + } + } + + int numXids = recoveredXidList.size(); + + Xid[] asXids = new Xid[numXids]; + Object[] asObjects = recoveredXidList.toArray(); + + for (int i = 0; i < numXids; i++) { + asXids[i] = (Xid) asObjects[i]; + } + + return asXids; + } + + public int prepare(Xid xid) throws XAException { + StringBuilder commandBuf = new StringBuilder(MAX_COMMAND_LENGTH); + commandBuf.append("XA PREPARE "); + appendXid(commandBuf, xid); + + dispatchCommand(commandBuf.toString()); + + return XA_OK; // TODO: Check for read-only + } + + public void forget(Xid xid) throws XAException { + // mysql doesn't support this + } + + public void rollback(Xid xid) throws XAException { + StringBuilder commandBuf = new StringBuilder(MAX_COMMAND_LENGTH); + commandBuf.append("XA ROLLBACK "); + appendXid(commandBuf, xid); + + try { + dispatchCommand(commandBuf.toString()); + } finally { + this.underlyingConnection.setInGlobalTx(false); + } + } + + public void end(Xid xid, int flags) throws XAException { + StringBuilder commandBuf = new StringBuilder(MAX_COMMAND_LENGTH); + commandBuf.append("XA END "); + appendXid(commandBuf, xid); + + switch (flags) { + case TMSUCCESS: + break; // no-op + case TMSUSPEND: + commandBuf.append(" SUSPEND"); + break; + case TMFAIL: + break; // no-op + default: + throw new XAException(XAException.XAER_INVAL); + } + + dispatchCommand(commandBuf.toString()); + } + + public void start(Xid xid, int flags) throws XAException { + StringBuilder commandBuf = new StringBuilder(MAX_COMMAND_LENGTH); + commandBuf.append("XA START "); + appendXid(commandBuf, xid); + + switch (flags) { + case TMJOIN: + commandBuf.append(" JOIN"); + break; + case TMRESUME: + commandBuf.append(" RESUME"); + break; + case TMNOFLAGS: + // no-op + break; + default: + throw new XAException(XAException.XAER_INVAL); + } + + dispatchCommand(commandBuf.toString()); + + this.underlyingConnection.setInGlobalTx(true); + } + + public void commit(Xid xid, boolean onePhase) throws XAException { + StringBuilder commandBuf = new StringBuilder(MAX_COMMAND_LENGTH); + commandBuf.append("XA COMMIT "); + appendXid(commandBuf, xid); + + if (onePhase) { + commandBuf.append(" ONE PHASE"); + } + + try { + dispatchCommand(commandBuf.toString()); + } finally { + this.underlyingConnection.setInGlobalTx(false); + } + } + + private ResultSet dispatchCommand(String command) throws XAException { + Statement stmt = null; + + try { + if (this.logXaCommands) { + this.log.logDebug("Executing XA statement: " + command); + } + + // TODO: Cache this for lifetime of XAConnection + stmt = this.underlyingConnection.createStatement(); + + stmt.execute(command); + + ResultSet rs = stmt.getResultSet(); + + return rs; + } catch (SQLException sqlEx) { + throw mapXAExceptionFromSQLException(sqlEx); + } finally { + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { + } + } + } + } + + protected static XAException mapXAExceptionFromSQLException(SQLException sqlEx) { + Integer xaCode = MYSQL_ERROR_CODES_TO_XA_ERROR_CODES.get(sqlEx.getErrorCode()); + + if (xaCode != null) { + return (XAException) new MysqlXAException(xaCode.intValue(), sqlEx.getMessage(), null).initCause(sqlEx); + } + + return (XAException) new MysqlXAException(XAException.XAER_RMFAIL, Messages.getString("MysqlXAConnection.003"), null).initCause(sqlEx); + } + + private static void appendXid(StringBuilder builder, Xid xid) { + byte[] gtrid = xid.getGlobalTransactionId(); + byte[] btrid = xid.getBranchQualifier(); + + if (gtrid != null) { + StringUtils.appendAsHex(builder, gtrid); + } + + builder.append(','); + if (btrid != null) { + StringUtils.appendAsHex(builder, btrid); + } + + builder.append(','); + StringUtils.appendAsHex(builder, xid.getFormatId()); + } + + @Override + public synchronized Connection getConnection() throws SQLException { + Connection connToWrap = getConnection(false, true); + + return connToWrap; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlXADataSource.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlXADataSource.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlXADataSource.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.Connection; +import java.sql.SQLException; + +import javax.sql.XAConnection; + +import com.mysql.cj.conf.PropertyDefinitions; + +public class MysqlXADataSource extends MysqlDataSource implements javax.sql.XADataSource { + + static final long serialVersionUID = 7911390333152247455L; + + /** + * Default no-arg constructor is required by specification. + */ + public MysqlXADataSource() { + } + + /** + * @see javax.sql.XADataSource#getXAConnection() + */ + public XAConnection getXAConnection() throws SQLException { + + Connection conn = getConnection(); + + return wrapConnection(conn); + } + + /** + * @see javax.sql.XADataSource#getXAConnection(String, String) + */ + public XAConnection getXAConnection(String u, String p) throws SQLException { + + Connection conn = getConnection(u, p); + + return wrapConnection(conn); + } + + /** + * Wraps a connection as a 'fake' XAConnection + */ + + private XAConnection wrapConnection(Connection conn) throws SQLException { + if (getBooleanReadableProperty(PropertyDefinitions.PNAME_pinGlobalTxToPhysicalConnection).getValue() + || ((JdbcConnection) conn).getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_pinGlobalTxToPhysicalConnection).getValue()) { + return SuspendableXAConnection.getInstance((JdbcConnection) conn); + } + + return MysqlXAConnection.getInstance((JdbcConnection) conn, getBooleanReadableProperty(PropertyDefinitions.PNAME_logXaCommands).getValue()); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlXAException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlXAException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlXAException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import javax.transaction.xa.XAException; + +/** + * The stock XAException class isn't too friendly (i.e. no error messages), so we extend it a bit. + */ +class MysqlXAException extends XAException { + private static final long serialVersionUID = -9075817535836563004L; + + private String message; + protected String xidAsString; + + public MysqlXAException(int errorCode, String message, String xidAsString) { + super(errorCode); + this.message = message; + this.xidAsString = xidAsString; + } + + public MysqlXAException(String message, String xidAsString) { + super(); + + this.message = message; + this.xidAsString = xidAsString; + } + + @Override + public String getMessage() { + String superMessage = super.getMessage(); + StringBuilder returnedMessage = new StringBuilder(); + + if (superMessage != null) { + returnedMessage.append(superMessage); + returnedMessage.append(":"); + } + + if (this.message != null) { + returnedMessage.append(this.message); + } + + return returnedMessage.toString(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlXid.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlXid.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/MysqlXid.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import javax.transaction.xa.Xid; + +/** + * Implementation of the XID interface for MySQL XA + */ +public class MysqlXid implements Xid { + + int hash = 0; + + byte[] myBqual; + + int myFormatId; + + byte[] myGtrid; + + public MysqlXid(byte[] gtrid, byte[] bqual, int formatId) { + this.myGtrid = gtrid; + this.myBqual = bqual; + this.myFormatId = formatId; + } + + @Override + public boolean equals(Object another) { + + if (another instanceof Xid) { + Xid anotherAsXid = (Xid) another; + + if (this.myFormatId != anotherAsXid.getFormatId()) { + return false; + } + + byte[] otherBqual = anotherAsXid.getBranchQualifier(); + byte[] otherGtrid = anotherAsXid.getGlobalTransactionId(); + + if (otherGtrid != null && otherGtrid.length == this.myGtrid.length) { + int length = otherGtrid.length; + + for (int i = 0; i < length; i++) { + if (otherGtrid[i] != this.myGtrid[i]) { + return false; + } + } + + if (otherBqual != null && otherBqual.length == this.myBqual.length) { + length = otherBqual.length; + + for (int i = 0; i < length; i++) { + if (otherBqual[i] != this.myBqual[i]) { + return false; + } + } + } else { + return false; + } + + return true; + } + } + + return false; + } + + public byte[] getBranchQualifier() { + return this.myBqual; + } + + public int getFormatId() { + return this.myFormatId; + }; + + public byte[] getGlobalTransactionId() { + return this.myGtrid; + } + + @Override + public synchronized int hashCode() { + if (this.hash == 0) { + for (int i = 0; i < this.myGtrid.length; i++) { + this.hash = 33 * this.hash + this.myGtrid[i]; + } + } + + return this.hash; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/NClob.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/NClob.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/NClob.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import com.mysql.cj.exceptions.ExceptionInterceptor; + +/** + * Simplistic implementation of java.sql.NClob for MySQL Connector/J + */ +public class NClob extends Clob implements java.sql.NClob { + + NClob(ExceptionInterceptor exceptionInterceptor) { + super(exceptionInterceptor); + } + + public NClob(String charDataInit, ExceptionInterceptor exceptionInterceptor) { + super(charDataInit, exceptionInterceptor); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/NonRegisteringDriver.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/NonRegisteringDriver.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/NonRegisteringDriver.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import static com.mysql.cj.util.StringUtils.isNullOrEmpty; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +import com.mysql.cj.Constants; +import com.mysql.cj.Messages; +import com.mysql.cj.conf.ConnectionUrl; +import com.mysql.cj.conf.ConnectionUrl.Type; +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.url.LoadbalanceConnectionUrl; +import com.mysql.cj.conf.url.ReplicationConnectionUrl; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.UnableToConnectException; +import com.mysql.cj.jdbc.ha.FailoverConnectionProxy; +import com.mysql.cj.jdbc.ha.LoadBalancedConnectionProxy; +import com.mysql.cj.jdbc.ha.ReplicationConnectionProxy; +import com.mysql.cj.protocol.NetworkResources; +import com.mysql.cj.util.StringUtils; + +/** + * The Java SQL framework allows for multiple database drivers. Each driver should supply a class that implements the Driver interface + * + *

+ * The DriverManager will try to load as many drivers as it can find and then for any given connection request, it will ask each driver in turn to try to + * connect to the target URL. + *

+ * + *

+ * It is strongly recommended that each Driver class should be small and standalone so that the Driver class can be loaded and queried without bringing in vast + * quantities of supporting code. + *

+ * + *

+ * When a Driver class is loaded, it should create an instance of itself and register it with the DriverManager. This means that a user can load and register a + * driver by doing Class.forName("foo.bah.Driver") + *

+ */ +public class NonRegisteringDriver implements java.sql.Driver { + + protected static final ConcurrentHashMap connectionPhantomRefs = new ConcurrentHashMap<>(); + + protected static final ReferenceQueue refQueue = new ReferenceQueue<>(); + + /* + * Standardizes OS name information to align with other drivers/clients + * for MySQL connection attributes + * + * @return the transformed, standardized OS name + */ + public static String getOSName() { + return Constants.OS_NAME; + } + + /* + * Standardizes platform information to align with other drivers/clients + * for MySQL connection attributes + * + * @return the transformed, standardized platform details + */ + public static String getPlatform() { + return Constants.OS_ARCH; + } + + static { + try { + Class.forName(AbandonedConnectionCleanupThread.class.getName()); + } catch (ClassNotFoundException e) { + // ignore + } + } + + /** + * Gets the drivers major version number + * + * @return the drivers major version number + */ + static int getMajorVersionInternal() { + return StringUtils.safeIntParse(Constants.CJ_MAJOR_VERSION); + } + + /** + * Get the drivers minor version number + * + * @return the drivers minor version number + */ + static int getMinorVersionInternal() { + return StringUtils.safeIntParse(Constants.CJ_MINOR_VERSION); + } + + /** + * Construct a new driver and register it with DriverManager + * + * @throws SQLException + * if a database error occurs. + */ + public NonRegisteringDriver() throws SQLException { + // Required for Class.forName().newInstance() + } + + /** + * Typically, drivers will return true if they understand the subprotocol + * specified in the URL and false if they don't. This driver's protocols + * start with jdbc:mysql: + * + * @param url + * the URL of the driver + * + * @return true if this driver accepts the given URL + * + * @exception SQLException + * if a database access error occurs or the url is null + * + * @see java.sql.Driver#acceptsURL + */ + public boolean acceptsURL(String url) throws SQLException { + return (ConnectionUrl.acceptsUrl(url)); + } + + // + // return the database name property + // + + /** + * Try to make a database connection to the given URL. The driver should return "null" if it realizes it is the wrong kind of driver to connect to the given + * URL. This will be common, as when the JDBC driverManager is asked to connect to a given URL, it passes the URL to each loaded driver in turn. + * + *

+ * The driver should raise an SQLException if the URL is null or if it is the right driver to connect to the given URL, but has trouble connecting to the + * database. + *

+ * + *

+ * The java.util.Properties argument can be used to pass arbitrary string tag/value pairs as connection arguments. These properties take precedence over any + * properties sent in the URL. + *

+ * + *

+ * MySQL protocol takes the form: + * + *

+     * jdbc:mysql://host:port/database
+     * 
+ * + *

+ * + * @param url + * the URL of the database to connect to + * @param info + * a list of arbitrary tag/value pairs as connection arguments + * + * @return a connection to the URL or null if it isn't us + * + * @exception SQLException + * if a database access error occurs or the url is {@code null} + * + * @see java.sql.Driver#connect + */ + public java.sql.Connection connect(String url, Properties info) throws SQLException { + + try { + ConnectionUrl conStr = ConnectionUrl.getConnectionUrlInstance(url, info); + if (conStr.getType() == null) { + /* + * According to JDBC spec: + * The driver should return "null" if it realizes it is the wrong kind of driver to connect to the given URL. This will be common, as when the + * JDBC driver manager is asked to connect to a given URL it passes the URL to each loaded driver in turn. + */ + return null; + } + + switch (conStr.getType()) { + case LOADBALANCE_CONNECTION: + return LoadBalancedConnectionProxy.createProxyInstance((LoadbalanceConnectionUrl) conStr); + + case FAILOVER_CONNECTION: + return FailoverConnectionProxy.createProxyInstance(conStr); + + case REPLICATION_CONNECTION: + return ReplicationConnectionProxy.createProxyInstance((ReplicationConnectionUrl) conStr); + + case XDEVAPI_SESSION: + // TODO test it + //return new XJdbcConnection(conStr.getProperties()); + + default: + return com.mysql.cj.jdbc.ConnectionImpl.getInstance(conStr.getMainHost()); + + } + + } catch (CJException ex) { + throw ExceptionFactory.createException(UnableToConnectException.class, + Messages.getString("NonRegisteringDriver.17", new Object[] { ex.toString() }), ex); + } + } + + protected static void trackConnection(JdbcConnection newConn) { + + ConnectionPhantomReference phantomRef = new ConnectionPhantomReference((ConnectionImpl) newConn, refQueue); + connectionPhantomRefs.put(phantomRef, phantomRef); + } + + /** + * Gets the drivers major version number + * + * @return the drivers major version number + */ + public int getMajorVersion() { + return getMajorVersionInternal(); + } + + /** + * Get the drivers minor version number + * + * @return the drivers minor version number + */ + public int getMinorVersion() { + return getMinorVersionInternal(); + } + + /** + * The getPropertyInfo method is intended to allow a generic GUI tool to + * discover what properties it should prompt a human for in order to get + * enough information to connect to a database. + * + *

+ * Note that depending on the values the human has supplied so far, additional values may become necessary, so it may be necessary to iterate through + * several calls to getPropertyInfo + *

+ * + * @param url + * the Url of the database to connect to + * @param info + * a proposed list of tag/value pairs that will be sent on + * connect open. + * + * @return An array of DriverPropertyInfo objects describing possible + * properties. This array may be an empty array if no properties are + * required + * + * @exception SQLException + * if a database-access error occurs + * + * @see java.sql.Driver#getPropertyInfo + */ + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { + String host = ""; + String port = ""; + String database = ""; + String user = ""; + String password = ""; + + if (!isNullOrEmpty(url)) { + ConnectionUrl connStr = ConnectionUrl.getConnectionUrlInstance(url, info); + if (connStr.getType() == Type.SINGLE_CONNECTION) { + HostInfo hostInfo = connStr.getMainHost(); + info = hostInfo.exposeAsProperties(); + } + } + + if (info != null) { + host = info.getProperty(PropertyDefinitions.HOST_PROPERTY_KEY); + port = info.getProperty(PropertyDefinitions.PORT_PROPERTY_KEY); + database = info.getProperty(PropertyDefinitions.DBNAME_PROPERTY_KEY); + user = info.getProperty(PropertyDefinitions.PNAME_user); + password = info.getProperty(PropertyDefinitions.PNAME_password); + } + + DriverPropertyInfo hostProp = new DriverPropertyInfo(PropertyDefinitions.HOST_PROPERTY_KEY, host); + hostProp.required = true; + hostProp.description = Messages.getString("NonRegisteringDriver.3"); + + DriverPropertyInfo portProp = new DriverPropertyInfo(PropertyDefinitions.PORT_PROPERTY_KEY, port); + portProp.required = false; + portProp.description = Messages.getString("NonRegisteringDriver.7"); + + DriverPropertyInfo dbProp = new DriverPropertyInfo(PropertyDefinitions.DBNAME_PROPERTY_KEY, database); + dbProp.required = false; + dbProp.description = Messages.getString("NonRegisteringDriver.10"); + + DriverPropertyInfo userProp = new DriverPropertyInfo(PropertyDefinitions.PNAME_user, user); + userProp.required = true; + userProp.description = Messages.getString("NonRegisteringDriver.13"); + + DriverPropertyInfo passwordProp = new DriverPropertyInfo(PropertyDefinitions.PNAME_password, password); + passwordProp.required = true; + passwordProp.description = Messages.getString("NonRegisteringDriver.16"); + + DriverPropertyInfo[] dpi; + dpi = new JdbcPropertySetImpl().exposeAsDriverPropertyInfo(info, 5); + + dpi[0] = hostProp; + dpi[1] = portProp; + dpi[2] = dbProp; + dpi[3] = userProp; + dpi[4] = passwordProp; + + return dpi; + } + + /** + * Report whether the driver is a genuine JDBC compliant driver. A driver + * may only report "true" here if it passes the JDBC compliance tests, + * otherwise it is required to return false. JDBC compliance requires full + * support for the JDBC API and full support for SQL 92 Entry Level. + * + *

+ * MySQL is not SQL92 compliant + *

+ * + * @return is this driver JDBC compliant? + */ + public boolean jdbcCompliant() { + return false; + } + + static class ConnectionPhantomReference extends PhantomReference { + private NetworkResources io; + + ConnectionPhantomReference(ConnectionImpl connectionImpl, ReferenceQueue q) { + super(connectionImpl, q); + + this.io = connectionImpl.getSession().getNetworkResources(); + } + + void cleanup() { + if (this.io != null) { + try { + this.io.forceClose(); + } finally { + this.io = null; + } + } + } + } + + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + throw new SQLFeatureNotSupportedException(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ParameterBindings.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ParameterBindings.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ParameterBindings.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URL; +import java.sql.Array; +import java.sql.Clob; +import java.sql.Date; +import java.sql.Ref; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; + +/** + * Interface to allow PreparedStatement implementations to expose their parameter bindings to QueryInterceptors. + */ +public interface ParameterBindings { + + Array getArray(int parameterIndex) throws SQLException; + + InputStream getAsciiStream(int parameterIndex) throws SQLException; + + BigDecimal getBigDecimal(int parameterIndex) throws SQLException; + + InputStream getBinaryStream(int parameterIndex) throws SQLException; + + java.sql.Blob getBlob(int parameterIndex) throws SQLException; + + boolean getBoolean(int parameterIndex) throws SQLException; + + byte getByte(int parameterIndex) throws SQLException; + + byte[] getBytes(int parameterIndex) throws SQLException; + + Reader getCharacterStream(int parameterIndex) throws SQLException; + + Clob getClob(int parameterIndex) throws SQLException; + + Date getDate(int parameterIndex) throws SQLException; + + double getDouble(int parameterIndex) throws SQLException; + + float getFloat(int parameterIndex) throws SQLException; + + int getInt(int parameterIndex) throws SQLException; + + BigInteger getBigInteger(int parameterIndex) throws SQLException; + + long getLong(int parameterIndex) throws SQLException; + + Reader getNCharacterStream(int parameterIndex) throws SQLException; + + Reader getNClob(int parameterIndex) throws SQLException; + + Object getObject(int parameterIndex) throws SQLException; + + Ref getRef(int parameterIndex) throws SQLException; + + short getShort(int parameterIndex) throws SQLException; + + String getString(int parameterIndex) throws SQLException; + + Time getTime(int parameterIndex) throws SQLException; + + Timestamp getTimestamp(int parameterIndex) throws SQLException; + + URL getURL(int parameterIndex) throws SQLException; + + boolean isNull(int parameterIndex) throws SQLException; +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/PreparedStatementWrapper.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/PreparedStatementWrapper.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/PreparedStatementWrapper.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,996 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.InputStream; +import java.io.Reader; +import java.lang.reflect.Proxy; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLType; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.HashMap; + +import javax.sql.StatementEvent; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; + +/** + * Wraps prepared statements so that errors can be reported correctly to ConnectionEventListeners. + */ +public class PreparedStatementWrapper extends StatementWrapper implements PreparedStatement { + + protected static PreparedStatementWrapper getInstance(ConnectionWrapper c, MysqlPooledConnection conn, PreparedStatement toWrap) throws SQLException { + return new PreparedStatementWrapper(c, conn, toWrap); + } + + PreparedStatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn, PreparedStatement toWrap) { + super(c, conn, toWrap); + } + + public void setArray(int parameterIndex, Array x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setArray(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setAsciiStream(parameterIndex, x, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBigDecimal(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBinaryStream(parameterIndex, x, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBlob(int parameterIndex, Blob x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBoolean(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setByte(int parameterIndex, byte x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setByte(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBytes(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setCharacterStream(parameterIndex, reader, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setClob(int parameterIndex, Clob x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setClob(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setDate(int parameterIndex, Date x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setDate(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setDate(parameterIndex, x, cal); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setDouble(int parameterIndex, double x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setDouble(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setFloat(int parameterIndex, float x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setFloat(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setInt(int parameterIndex, int x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setInt(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setLong(int parameterIndex, long x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setLong(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public ResultSetMetaData getMetaData() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((PreparedStatement) this.wrappedStmt).getMetaData(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public void setNull(int parameterIndex, int sqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNull(parameterIndex, sqlType); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNull(parameterIndex, sqlType, typeName); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setObject(int parameterIndex, Object x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setObject(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setObject(parameterIndex, x, targetSqlType); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setObject(parameterIndex, x, targetSqlType, scale); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public ParameterMetaData getParameterMetaData() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((PreparedStatement) this.wrappedStmt).getParameterMetaData(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public void setRef(int parameterIndex, Ref x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setRef(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setShort(int parameterIndex, short x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setShort(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setString(int parameterIndex, String x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setString(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setTime(int parameterIndex, Time x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setTime(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setTime(parameterIndex, x, cal); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setTimestamp(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setTimestamp(parameterIndex, x, cal); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setURL(int parameterIndex, URL x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setURL(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * @param parameterIndex + * @param x + * @param length + * + * @throws SQLException + * + * @see java.sql.PreparedStatement#setUnicodeStream(int, java.io.InputStream, int) + * @deprecated + */ + @Deprecated + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setUnicodeStream(parameterIndex, x, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void addBatch() throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).addBatch(); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void clearParameters() throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).clearParameters(); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public boolean execute() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((PreparedStatement) this.wrappedStmt).execute(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // we actually never get here, but the compiler can't figure that out + } + + public ResultSet executeQuery() throws SQLException { + ResultSet rs = null; + try { + if (this.wrappedStmt == null) { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + + rs = ((PreparedStatement) this.wrappedStmt).executeQuery(); + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).setWrapperStatement(this); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return rs; + } + + public int executeUpdate() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((PreparedStatement) this.wrappedStmt).executeUpdate(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(super.toString()); + + if (this.wrappedStmt != null) { + buf.append(": "); + try { + buf.append(((ClientPreparedStatement) this.wrappedStmt).asSql()); + } catch (SQLException sqlEx) { + buf.append("EXCEPTION: " + sqlEx.toString()); + } + } + + return buf.toString(); + } + + public void setRowId(int parameterIndex, RowId x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setRowId(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNString(int parameterIndex, String value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNString(parameterIndex, value); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNCharacterStream(parameterIndex, value, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(int parameterIndex, NClob value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex, value); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setClob(parameterIndex, reader, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex, inputStream, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex, reader, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setSQLXML(parameterIndex, xmlObject); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setAsciiStream(parameterIndex, x, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBinaryStream(parameterIndex, x, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setCharacterStream(parameterIndex, reader, length); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setAsciiStream(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBinaryStream(parameterIndex, x); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setCharacterStream(parameterIndex, reader); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + } + + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNCharacterStream(parameterIndex, value); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + } + + public void setClob(int parameterIndex, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setClob(parameterIndex, reader); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + } + + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex, inputStream); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex, reader); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Returns true if this either implements the interface argument or is + * directly or indirectly a wrapper for an object that does. Returns false + * otherwise. If this implements the interface then return true, else if + * this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped object. If this does not + * implement the interface and is not a wrapper, return false. This method + * should be implemented as a low-cost operation compared to unwrap so that callers can use this method to avoid + * expensive unwrap calls that may fail. If this method returns + * true then calling unwrap with the same argument should + * succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly + * wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a + * wrapper for an object with the given interface. + * @since 1.6 + */ + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + + boolean isInstance = iface.isInstance(this); + + if (isInstance) { + return true; + } + + String interfaceClassName = iface.getName(); + + return (interfaceClassName.equals("com.mysql.cj.jdbc.Statement") || interfaceClassName.equals("java.sql.Statement") + || interfaceClassName.equals("java.sql.Wrapper") || interfaceClassName.equals("java.sql.PreparedStatement")); // TODO check other interfaces + } + + /** + * Returns an object that implements the given interface to allow access to + * non-standard methods, or standard methods not exposed by the proxy. The + * result may be either the object found to implement the interface or a + * proxy for that object. If the receiver implements the interface then that + * is the object. If the receiver is a wrapper and the wrapped object + * implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped + * object. If the receiver is not a wrapper and does not implement the + * interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the + * actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + @Override + public synchronized T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + if ("java.sql.Statement".equals(iface.getName()) || "java.sql.PreparedStatement".equals(iface.getName()) + || "java.sql.Wrapper.class".equals(iface.getName())) { + return iface.cast(this); + } + + if (this.unwrappedInterfaces == null) { + this.unwrappedInterfaces = new HashMap<>(); + } + + Object cachedUnwrapped = this.unwrappedInterfaces.get(iface); + + if (cachedUnwrapped == null) { + cachedUnwrapped = Proxy.newProxyInstance(this.wrappedStmt.getClass().getClassLoader(), new Class[] { iface }, + new ConnectionErrorFiringInvocationHandler(this.wrappedStmt)); + this.unwrappedInterfaces.put(iface, cachedUnwrapped); + } + + return iface.cast(cachedUnwrapped); + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("Common.UnableToUnwrap", new Object[] { iface.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } + + @Override + public synchronized void close() throws SQLException { + if (this.pooledConnection == null) { + // no-op + return; + } + + MysqlPooledConnection con = this.pooledConnection; // we need this later... + + try { + super.close(); + } finally { + try { + StatementEvent e = new StatementEvent(con, this); + con.fireStatementEvent(e); + } finally { + this.unwrappedInterfaces = null; + } + } + } + + /** + * JDBC 4.2 + * Same as PreparedStatement.executeUpdate() but returns long instead of int. + */ + public long executeLargeUpdate() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((ClientPreparedStatement) this.wrappedStmt).executeLargeUpdate(); + } + + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @throws SQLException + */ + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setObject(parameterIndex, x, targetSqlType); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Support for java.sql.JDBCType/java.sql.SQLType. + * + * @param parameterIndex + * @param x + * @param targetSqlType + * @param scaleOrLength + * @throws SQLException + */ + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((PreparedStatement) this.wrappedStmt).setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } else { + throw SQLError.createSQLException("No operations allowed after statement closed", MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ServerPreparedStatement.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ServerPreparedStatement.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ServerPreparedStatement.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,801 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.sql.Date; +import java.sql.ParameterMetaData; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Wrapper; +import java.util.ArrayList; + +import com.mysql.cj.CancelQueryTask; +import com.mysql.cj.Messages; +import com.mysql.cj.MysqlType; +import com.mysql.cj.ParseInfo; +import com.mysql.cj.PreparedQuery; +import com.mysql.cj.ServerPreparedQuery; +import com.mysql.cj.ServerPreparedQueryBindValue; +import com.mysql.cj.ServerPreparedQueryBindings; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.jdbc.exceptions.MySQLStatementCancelledException; +import com.mysql.cj.jdbc.exceptions.MySQLTimeoutException; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; +import com.mysql.cj.jdbc.result.ResultSetMetaData; +import com.mysql.cj.protocol.ColumnDefinition; +import com.mysql.cj.protocol.Message; + +/** + * JDBC Interface for MySQL-4.1 and newer server-side PreparedStatements. + */ +public class ServerPreparedStatement extends ClientPreparedStatement { + + private boolean hasOnDuplicateKeyUpdate = false; + + /** Has this prepared statement been marked invalid? */ + private boolean invalid = false; + + /** If this statement has been marked invalid, what was the reason? */ + private CJException invalidationException; + + protected boolean isCached = false; + + /** + * Creates a prepared statement instance + */ + + protected static ServerPreparedStatement getInstance(JdbcConnection conn, String sql, String catalog, int resultSetType, int resultSetConcurrency) + throws SQLException { + return new ServerPreparedStatement(conn, sql, catalog, resultSetType, resultSetConcurrency); + } + + /** + * Creates a new ServerPreparedStatement object. + * + * @param conn + * the connection creating us. + * @param sql + * the SQL containing the statement to prepare. + * @param catalog + * the catalog in use when we were created. + * + * @throws SQLException + * If an error occurs + */ + protected ServerPreparedStatement(JdbcConnection conn, String sql, String catalog, int resultSetType, int resultSetConcurrency) throws SQLException { + super(conn, catalog); + + checkNullOrEmptyQuery(sql); + String statementComment = this.session.getProtocol().getQueryComment(); + ((PreparedQuery) this.query).setOriginalSql(statementComment == null ? sql : "/* " + statementComment + " */ " + sql); + ((PreparedQuery) this.query).setParseInfo(new ParseInfo(((PreparedQuery) this.query).getOriginalSql(), this.session, this.charEncoding)); + + this.hasOnDuplicateKeyUpdate = ((PreparedQuery) this.query).getParseInfo().getFirstStmtChar() == 'I' && containsOnDuplicateKeyInString(sql); + + try { + serverPrepare(sql); + } catch (CJException | SQLException sqlEx) { + realClose(false, true); + + throw SQLExceptionsMapping.translateException(sqlEx, this.exceptionInterceptor); + } + + setResultSetType(resultSetType); + setResultSetConcurrency(resultSetConcurrency); + + } + + @Override + protected void initQuery() { + this.query = ServerPreparedQuery.getInstance(this.session); + } + + @Override + public String toString() { + StringBuilder toStringBuf = new StringBuilder(); + + toStringBuf.append(this.getClass().getName() + "["); + toStringBuf.append(((ServerPreparedQuery) this.query).getServerStatementId()); + toStringBuf.append("]: "); + + try { + toStringBuf.append(asSql()); + } catch (SQLException sqlEx) { + toStringBuf.append(Messages.getString("ServerPreparedStatement.6")); + toStringBuf.append(sqlEx); + } + + return toStringBuf.toString(); + } + + @Override + public void addBatch() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.query.addBatch(((PreparedQuery) this.query).getQueryBindings().clone()); + } + } + + @Override + public String asSql(boolean quoteStreamsAndUnknowns) throws SQLException { + + synchronized (checkClosed().getConnectionMutex()) { + + ClientPreparedStatement pStmtForSub = null; + + try { + pStmtForSub = ClientPreparedStatement.getInstance(this.connection, ((PreparedQuery) this.query).getOriginalSql(), this.getCurrentCatalog()); + + int numParameters = ((PreparedQuery) pStmtForSub.query).getParameterCount(); + int ourNumParameters = ((PreparedQuery) this.query).getParameterCount(); + + ServerPreparedQueryBindValue[] parameterBindings = ((ServerPreparedQuery) this.query).getQueryBindings().getBindValues(); + + for (int i = 0; (i < numParameters) && (i < ourNumParameters); i++) { + if (parameterBindings[i] != null) { + if (parameterBindings[i].isNull()) { + pStmtForSub.setNull(i + 1, MysqlType.NULL); + } else { + ServerPreparedQueryBindValue bindValue = parameterBindings[i]; + + // + // Handle primitives first + // + switch (bindValue.bufferType) { + + case MysqlType.FIELD_TYPE_TINY: + pStmtForSub.setByte(i + 1, (byte) bindValue.longBinding); + break; + case MysqlType.FIELD_TYPE_SHORT: + pStmtForSub.setShort(i + 1, (short) bindValue.longBinding); + break; + case MysqlType.FIELD_TYPE_LONG: + pStmtForSub.setInt(i + 1, (int) bindValue.longBinding); + break; + case MysqlType.FIELD_TYPE_LONGLONG: + pStmtForSub.setLong(i + 1, bindValue.longBinding); + break; + case MysqlType.FIELD_TYPE_FLOAT: + pStmtForSub.setFloat(i + 1, bindValue.floatBinding); + break; + case MysqlType.FIELD_TYPE_DOUBLE: + pStmtForSub.setDouble(i + 1, bindValue.doubleBinding); + break; + default: + pStmtForSub.setObject(i + 1, parameterBindings[i].value); + break; + } + } + } + } + + return pStmtForSub.asSql(quoteStreamsAndUnknowns); + } finally { + if (pStmtForSub != null) { + try { + pStmtForSub.close(); + } catch (SQLException sqlEx) { + // ignore + } + } + } + } + } + + @Override + protected JdbcConnection checkClosed() { + if (this.invalid) { + throw this.invalidationException; + } + + return super.checkClosed(); + } + + @Override + public void clearParameters() { + synchronized (checkClosed().getConnectionMutex()) { + ((ServerPreparedQuery) this.query).clearParameters(true); + } + } + + protected void setClosed(boolean flag) { + this.isClosed = flag; + } + + @Override + public void close() throws SQLException { + JdbcConnection locallyScopedConn = this.connection; + + if (locallyScopedConn == null) { + return; // already closed + } + + synchronized (locallyScopedConn.getConnectionMutex()) { + + if (this.isCached && isPoolable() && !this.isClosed) { + clearParameters(); + + this.isClosed = true; + + this.connection.recachePreparedStatement(this); + return; + } + + this.isClosed = false; + realClose(true, true); + } + } + + @Override + protected long[] executeBatchSerially(int batchTimeout) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + JdbcConnection locallyScopedConn = this.connection; + + if (locallyScopedConn.isReadOnly()) { + throw SQLError.createSQLException(Messages.getString("ServerPreparedStatement.2") + Messages.getString("ServerPreparedStatement.3"), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + + clearWarnings(); + + // Store this for later, we're going to 'swap' them out + // as we execute each batched statement... + ServerPreparedQueryBindValue[] oldBindValues = ((ServerPreparedQuery) this.query).getQueryBindings().getBindValues(); + + try { + long[] updateCounts = null; + + if (this.query.getBatchedArgs() != null) { + int nbrCommands = this.query.getBatchedArgs().size(); + updateCounts = new long[nbrCommands]; + + if (this.retrieveGeneratedKeys) { + this.batchedGeneratedKeys = new ArrayList<>(nbrCommands); + } + + for (int i = 0; i < nbrCommands; i++) { + updateCounts[i] = -3; + } + + SQLException sqlEx = null; + + int commandIndex = 0; + + ServerPreparedQueryBindValue[] previousBindValuesForBatch = null; + + CancelQueryTask timeoutTask = null; + + try { + timeoutTask = startQueryTimer(this, batchTimeout); + + for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) { + Object arg = this.query.getBatchedArgs().get(commandIndex); + + try { + if (arg instanceof String) { + updateCounts[commandIndex] = executeUpdateInternal((String) arg, true, this.retrieveGeneratedKeys); + + // limit one generated key per OnDuplicateKey statement + getBatchedGeneratedKeys(this.results.getFirstCharOfQuery() == 'I' && containsOnDuplicateKeyInString((String) arg) ? 1 : 0); + } else { + ((ServerPreparedQuery) this.query).setQueryBindings((ServerPreparedQueryBindings) arg); + ServerPreparedQueryBindValue[] parameterBindings = ((ServerPreparedQuery) this.query).getQueryBindings().getBindValues(); + + // We need to check types each time, as the user might have bound different types in each addBatch() + + if (previousBindValuesForBatch != null) { + for (int j = 0; j < parameterBindings.length; j++) { + if (parameterBindings[j].bufferType != previousBindValuesForBatch[j].bufferType) { + ((ServerPreparedQuery) this.query).getQueryBindings().getSendTypesToServer().set(true); + + break; + } + } + } + + try { + updateCounts[commandIndex] = executeUpdateInternal(false, true); + } finally { + previousBindValuesForBatch = parameterBindings; + } + + // limit one generated key per OnDuplicateKey statement + getBatchedGeneratedKeys(containsOnDuplicateKeyUpdateInSQL() ? 1 : 0); + } + } catch (SQLException ex) { + updateCounts[commandIndex] = EXECUTE_FAILED; + + if (this.continueBatchOnError && !(ex instanceof MySQLTimeoutException) && !(ex instanceof MySQLStatementCancelledException) + && !hasDeadlockOrTimeoutRolledBackTx(ex)) { + sqlEx = ex; + } else { + long[] newUpdateCounts = new long[commandIndex]; + System.arraycopy(updateCounts, 0, newUpdateCounts, 0, commandIndex); + + throw SQLError.createBatchUpdateException(ex, newUpdateCounts, this.exceptionInterceptor); + } + } + } + } finally { + stopQueryTimer(timeoutTask, false, false); + resetCancelledState(); + } + + if (sqlEx != null) { + throw SQLError.createBatchUpdateException(sqlEx, updateCounts, this.exceptionInterceptor); + } + } + + return (updateCounts != null) ? updateCounts : new long[0]; + } finally { + ((ServerPreparedQuery) this.query).getQueryBindings().setBindValues(oldBindValues); + ((ServerPreparedQuery) this.query).getQueryBindings().getSendTypesToServer().set(true); + + clearBatch(); + } + } + } + + private static SQLException appendMessageToException(SQLException sqlEx, String messageToAppend, ExceptionInterceptor interceptor) { + String sqlState = sqlEx.getSQLState(); + int vendorErrorCode = sqlEx.getErrorCode(); + + SQLException sqlExceptionWithNewMessage = SQLError.createSQLException(sqlEx.getMessage() + messageToAppend, sqlState, vendorErrorCode, interceptor); + sqlExceptionWithNewMessage.setStackTrace(sqlEx.getStackTrace()); + + return sqlExceptionWithNewMessage; + } + + @Override + protected com.mysql.cj.jdbc.result.ResultSetInternalMethods executeInternal(int maxRowsToRetrieve, M sendPacket, + boolean createStreamingResultSet, boolean queryIsSelectOnly, ColumnDefinition metadata, boolean isBatch) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + ((PreparedQuery) this.query).getQueryBindings() + .setNumberOfExecutions(((PreparedQuery) this.query).getQueryBindings().getNumberOfExecutions() + 1); + + // We defer to server-side execution + try { + return serverExecute(maxRowsToRetrieve, createStreamingResultSet, metadata); + } catch (SQLException sqlEx) { + // don't wrap SQLExceptions + if (this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_enablePacketDebug).getValue()) { + this.session.dumpPacketRingBuffer(); + } + + if (this.dumpQueriesOnException.getValue()) { + String extractedSql = toString(); + StringBuilder messageBuf = new StringBuilder(extractedSql.length() + 32); + messageBuf.append("\n\nQuery being executed when exception was thrown:\n"); + messageBuf.append(extractedSql); + messageBuf.append("\n\n"); + + sqlEx = appendMessageToException(sqlEx, messageBuf.toString(), this.exceptionInterceptor); + } + + throw sqlEx; + } catch (Exception ex) { + if (this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_enablePacketDebug).getValue()) { + this.session.dumpPacketRingBuffer(); + } + + SQLException sqlEx = SQLError.createSQLException(ex.toString(), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, ex, this.exceptionInterceptor); + + if (this.dumpQueriesOnException.getValue()) { + String extractedSql = toString(); + StringBuilder messageBuf = new StringBuilder(extractedSql.length() + 32); + messageBuf.append("\n\nQuery being executed when exception was thrown:\n"); + messageBuf.append(extractedSql); + messageBuf.append("\n\n"); + + sqlEx = appendMessageToException(sqlEx, messageBuf.toString(), this.exceptionInterceptor); + } + + throw sqlEx; + } + } + } + + /** + * Returns the structure representing the value that (can be)/(is) + * bound at the given parameter index. + * + * @param parameterIndex + * 1-based + * @param forLongData + * is this for a stream? + * @throws SQLException + */ + protected ServerPreparedQueryBindValue getBinding(int parameterIndex, boolean forLongData) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + int i = getCoreParameterIndex(parameterIndex); + return ((ServerPreparedQuery) this.query).getQueryBindings().getBinding(i, forLongData); + } + } + + @Override + public java.sql.ResultSetMetaData getMetaData() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + ColumnDefinition resultFields = ((ServerPreparedQuery) this.query).getResultFields(); + + return resultFields == null || resultFields.getFields() == null ? null : new ResultSetMetaData(this.session, resultFields.getFields(), + this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useOldAliasMetadataBehavior).getValue(), + this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_yearIsDateType).getValue(), this.exceptionInterceptor); + } + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (this.parameterMetaData == null) { + this.parameterMetaData = new MysqlParameterMetadata(this.session, ((ServerPreparedQuery) this.query).getParameterFields(), + ((PreparedQuery) this.query).getParameterCount(), this.exceptionInterceptor); + } + + return this.parameterMetaData; + } + } + + @Override + public boolean isNull(int paramIndex) { + throw new IllegalArgumentException(Messages.getString("ServerPreparedStatement.7")); + } + + /** + * Closes this connection and frees all resources. + * + * @param calledExplicitly + * was this called from close()? + * @param closeOpenResults + * should open result sets be closed? + * + * @throws SQLException + * if an error occurs + */ + @Override + public void realClose(boolean calledExplicitly, boolean closeOpenResults) throws SQLException { + JdbcConnection locallyScopedConn = this.connection; + + if (locallyScopedConn == null) { + return; // already closed + } + + synchronized (locallyScopedConn.getConnectionMutex()) { + + if (this.connection != null) { + + // + // Don't communicate with the server if we're being called from the finalizer... + // + // This will leak server resources, but if we don't do this, we'll deadlock (potentially, because there's no guarantee when, what order, and + // what concurrency finalizers will be called with). Well-behaved programs won't rely on finalizers to clean up their statements. + // + + CJException exceptionDuringClose = null; + + if (calledExplicitly && !this.connection.isClosed()) { + synchronized (this.connection.getConnectionMutex()) { + try { + + this.session.sendCommand(this.commandBuilder.buildComStmtClose(null, ((ServerPreparedQuery) this.query).getServerStatementId()), + true, 0); + } catch (CJException sqlEx) { + exceptionDuringClose = sqlEx; + } + } + } + + if (this.isCached) { + this.connection.decachePreparedStatement(this); + this.isCached = false; + } + super.realClose(calledExplicitly, closeOpenResults); + + ((ServerPreparedQuery) this.query).clearParameters(false); + + if (exceptionDuringClose != null) { + throw exceptionDuringClose; + } + } + } + } + + /** + * Used by Connection when auto-reconnecting to retrieve 'lost' prepared + * statements. + * + * @throws CJException + * if an error occurs. + */ + protected void rePrepare() { + synchronized (checkClosed().getConnectionMutex()) { + this.invalidationException = null; + + try { + serverPrepare(((PreparedQuery) this.query).getOriginalSql()); + } catch (Exception ex) { + this.invalidationException = ExceptionFactory.createException(ex.getMessage(), ex); + } + + if (this.invalidationException != null) { + this.invalid = true; + + this.query.closeQuery(); + + if (this.results != null) { + try { + this.results.close(); + } catch (Exception ex) { + } + } + + if (this.generatedKeysResults != null) { + try { + this.generatedKeysResults.close(); + } catch (Exception ex) { + } + } + + try { + closeAllOpenResults(); + } catch (Exception e) { + } + + if (this.connection != null && !this.dontTrackOpenResources.getValue()) { + this.connection.unregisterStatement(this); + } + } + } + } + + /** + * Tells the server to execute this prepared statement with the current + * parameter bindings. + * + *
+     *    -   Server gets the command 'COM_EXECUTE' to execute the
+     *        previously         prepared query. If there is any param markers;
+     *  then client will send the data in the following format:
+     * 
+     *  [COM_EXECUTE:1]
+     *  [STMT_ID:4]
+     *  [NULL_BITS:(param_count+7)/8)]
+     *  [TYPES_SUPPLIED_BY_CLIENT(0/1):1]
+     *  [[length]data]
+     *  [[length]data] .. [[length]data].
+     * 
+     *  (Note: Except for string/binary types; all other types will not be
+     *  supplied with length field)
+     * 
+ * + * @param maxRowsToRetrieve + * @param createStreamingResultSet + * @param metadata + * use this metadata instead of the one provided on wire + * + * @throws SQLException + */ + protected ResultSetInternalMethods serverExecute(int maxRowsToRetrieve, boolean createStreamingResultSet, ColumnDefinition metadata) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.results = ((ServerPreparedQuery) this.query).serverExecute(maxRowsToRetrieve, createStreamingResultSet, metadata, this.resultSetFactory); + return this.results; + } + } + + protected void serverPrepare(String sql) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + try { + + ServerPreparedQuery q = (ServerPreparedQuery) this.query; + q.serverPrepare(sql); + } catch (IOException ioEx) { + throw SQLError.createCommunicationsException(this.connection, this.session.getProtocol().getPacketSentTimeHolder().getLastPacketSentTime(), + this.session.getProtocol().getPacketReceivedTimeHolder().getLastPacketReceivedTime(), ioEx, this.exceptionInterceptor); + } catch (CJException sqlEx) { + SQLException ex = SQLExceptionsMapping.translateException(sqlEx); + + if (this.dumpQueriesOnException.getValue()) { + StringBuilder messageBuf = new StringBuilder(((PreparedQuery) this.query).getOriginalSql().length() + 32); + messageBuf.append("\n\nQuery being prepared when exception was thrown:\n\n"); + messageBuf.append(((PreparedQuery) this.query).getOriginalSql()); + + ex = appendMessageToException(ex, messageBuf.toString(), this.exceptionInterceptor); + } + + throw ex; + } finally { + // Leave the I/O channel in a known state...there might be packets out there that we're not interested in + this.session.clearInputStream(); + } + } + } + + @Override + protected void checkBounds(int parameterIndex, int parameterIndexOffset) throws SQLException { + int paramCount = ((PreparedQuery) this.query).getParameterCount(); + if (paramCount == 0) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ServerPreparedStatement.8"), + this.session.getExceptionInterceptor()); + } + + if ((parameterIndex < 0) || (parameterIndex > paramCount)) { + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("ServerPreparedStatement.9") + (parameterIndex + 1) + Messages.getString("ServerPreparedStatement.10") + paramCount, + this.session.getExceptionInterceptor()); + } + } + + @Deprecated + @Override + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + checkClosed(); + + throw SQLError.createSQLFeatureNotSupportedException(); + } + + @Override + public void setURL(int parameterIndex, URL x) throws SQLException { + checkClosed(); + + setString(parameterIndex, x.toString()); + } + + @Override + public long getServerStatementId() { + return ((ServerPreparedQuery) this.query).getServerStatementId(); + } + + @Override + protected int setOneBatchedParameterSet(java.sql.PreparedStatement batchedStatement, int batchedParamIndex, Object paramSet) throws SQLException { + ServerPreparedQueryBindValue[] paramArg = ((ServerPreparedQueryBindings) paramSet).getBindValues(); + + for (int j = 0; j < paramArg.length; j++) { + if (paramArg[j].isNull()) { + batchedStatement.setNull(batchedParamIndex++, MysqlType.NULL.getJdbcType()); + } else { + if (paramArg[j].isLongData) { + Object value = paramArg[j].value; + + if (value instanceof InputStream) { + batchedStatement.setBinaryStream(batchedParamIndex++, (InputStream) value, (int) paramArg[j].bindLength); + } else { + batchedStatement.setCharacterStream(batchedParamIndex++, (Reader) value, (int) paramArg[j].bindLength); + } + } else { + + switch (paramArg[j].bufferType) { + + case MysqlType.FIELD_TYPE_TINY: + batchedStatement.setByte(batchedParamIndex++, (byte) paramArg[j].longBinding); + break; + case MysqlType.FIELD_TYPE_SHORT: + batchedStatement.setShort(batchedParamIndex++, (short) paramArg[j].longBinding); + break; + case MysqlType.FIELD_TYPE_LONG: + batchedStatement.setInt(batchedParamIndex++, (int) paramArg[j].longBinding); + break; + case MysqlType.FIELD_TYPE_LONGLONG: + batchedStatement.setLong(batchedParamIndex++, paramArg[j].longBinding); + break; + case MysqlType.FIELD_TYPE_FLOAT: + batchedStatement.setFloat(batchedParamIndex++, paramArg[j].floatBinding); + break; + case MysqlType.FIELD_TYPE_DOUBLE: + batchedStatement.setDouble(batchedParamIndex++, paramArg[j].doubleBinding); + break; + case MysqlType.FIELD_TYPE_TIME: + batchedStatement.setTime(batchedParamIndex++, (Time) paramArg[j].value); + break; + case MysqlType.FIELD_TYPE_DATE: + batchedStatement.setDate(batchedParamIndex++, (Date) paramArg[j].value); + break; + case MysqlType.FIELD_TYPE_DATETIME: + case MysqlType.FIELD_TYPE_TIMESTAMP: + batchedStatement.setTimestamp(batchedParamIndex++, (Timestamp) paramArg[j].value); + break; + case MysqlType.FIELD_TYPE_VAR_STRING: + case MysqlType.FIELD_TYPE_STRING: + case MysqlType.FIELD_TYPE_VARCHAR: + case MysqlType.FIELD_TYPE_DECIMAL: + case MysqlType.FIELD_TYPE_NEWDECIMAL: + Object value = paramArg[j].value; + + if (value instanceof byte[]) { + batchedStatement.setBytes(batchedParamIndex, (byte[]) value); + } else { + batchedStatement.setString(batchedParamIndex, (String) value); + } + + // If we ended up here as a multi-statement, we're not working with a server prepared statement + + if (batchedStatement instanceof ServerPreparedStatement) { + ServerPreparedQueryBindValue asBound = ((ServerPreparedStatement) batchedStatement).getBinding(batchedParamIndex, false); + asBound.bufferType = paramArg[j].bufferType; + } + + batchedParamIndex++; + + break; + default: + throw new IllegalArgumentException(Messages.getString("ServerPreparedStatement.26", new Object[] { batchedParamIndex })); + } + } + } + } + + return batchedParamIndex; + } + + @Override + protected boolean containsOnDuplicateKeyUpdateInSQL() { + return this.hasOnDuplicateKeyUpdate; + } + + @Override + protected ClientPreparedStatement prepareBatchedInsertSQL(JdbcConnection localConn, int numBatches) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + try { + ClientPreparedStatement pstmt = ((Wrapper) localConn.prepareStatement(((PreparedQuery) this.query).getParseInfo().getSqlForBatch(numBatches), + this.resultSetConcurrency, this.query.getResultType().getIntValue())).unwrap(ClientPreparedStatement.class); + pstmt.setRetrieveGeneratedKeys(this.retrieveGeneratedKeys); + + return pstmt; + } catch (UnsupportedEncodingException e) { + SQLException sqlEx = SQLError.createSQLException(Messages.getString("ServerPreparedStatement.27"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + sqlEx.initCause(e); + + throw sqlEx; + } + } + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + if (!poolable) { + this.connection.decachePreparedStatement(this); + } + super.setPoolable(poolable); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/StatementImpl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/StatementImpl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/StatementImpl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,2535 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.sql.BatchUpdateException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.mysql.cj.CancelQueryTask; +import com.mysql.cj.CharsetMapping; +import com.mysql.cj.Constants; +import com.mysql.cj.Messages; +import com.mysql.cj.MysqlType; +import com.mysql.cj.NativeSession; +import com.mysql.cj.ParseInfo; +import com.mysql.cj.PingTarget; +import com.mysql.cj.Query; +import com.mysql.cj.Session; +import com.mysql.cj.SimpleQuery; +import com.mysql.cj.TransactionEventHandler; +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.ReadableProperty; +import com.mysql.cj.exceptions.AssertionFailedException; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.CJOperationNotSupportedException; +import com.mysql.cj.exceptions.CJTimeoutException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.exceptions.OperationCancelledException; +import com.mysql.cj.exceptions.StatementIsClosedException; +import com.mysql.cj.jdbc.exceptions.MySQLStatementCancelledException; +import com.mysql.cj.jdbc.exceptions.MySQLTimeoutException; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping; +import com.mysql.cj.jdbc.result.CachedResultSetMetaData; +import com.mysql.cj.jdbc.result.ResultSetFactory; +import com.mysql.cj.jdbc.result.ResultSetImpl; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; +import com.mysql.cj.log.ProfilerEvent; +import com.mysql.cj.log.ProfilerEventHandler; +import com.mysql.cj.log.ProfilerEventHandlerFactory; +import com.mysql.cj.log.ProfilerEventImpl; +import com.mysql.cj.protocol.Message; +import com.mysql.cj.protocol.ProtocolEntityFactory; +import com.mysql.cj.protocol.Resultset; +import com.mysql.cj.protocol.Resultset.Type; +import com.mysql.cj.protocol.a.NativeConstants; +import com.mysql.cj.protocol.a.NativeMessageBuilder; +import com.mysql.cj.protocol.a.result.ByteArrayRow; +import com.mysql.cj.protocol.a.result.ResultsetRowsStatic; +import com.mysql.cj.result.DefaultColumnDefinition; +import com.mysql.cj.result.Field; +import com.mysql.cj.result.Row; +import com.mysql.cj.util.LogUtils; +import com.mysql.cj.util.StringUtils; +import com.mysql.cj.util.Util; + +/** + * A Statement object is used for executing a static SQL statement and obtaining + * the results produced by it. + * + * Only one ResultSet per Statement can be open at any point in time. Therefore, if the reading of one ResultSet is interleaved with the reading of another, + * each must have been generated by different Statements. All statement execute methods implicitly close a statement's current ResultSet if an open one exists. + */ +public class StatementImpl implements JdbcStatement { + protected static final String PING_MARKER = "/* ping */"; + + protected NativeMessageBuilder commandBuilder = new NativeMessageBuilder(); // TODO use shared builder + + public final static byte USES_VARIABLES_FALSE = 0; + + public final static byte USES_VARIABLES_TRUE = 1; + + public final static byte USES_VARIABLES_UNKNOWN = -1; + + /** The character encoding to use (if available) */ + protected String charEncoding = null; + + /** The connection that created us */ + protected volatile JdbcConnection connection = null; + + /** Should we process escape codes? */ + protected boolean doEscapeProcessing = true; + + /** Has this statement been closed? */ + protected boolean isClosed = false; + + /** The auto_increment value for the last insert */ + protected long lastInsertId = -1; + + /** The max field size for this statement */ + protected int maxFieldSize = (Integer) PropertyDefinitions.getPropertyDefinition(PropertyDefinitions.PNAME_maxAllowedPacket).getDefaultValue(); + + /** + * The maximum number of rows to return for this statement (-1 means _all_ + * rows) + */ + public int maxRows = -1; + + /** Set of currently-open ResultSets */ + protected Set openResults = new HashSet<>(); + + /** Are we in pedantic mode? */ + protected boolean pedantic = false; + + /** + * Where this statement was created, only used if profileSQL or + * useUsageAdvisor set to true. + */ + protected String pointOfOrigin; + + /** Should we profile? */ + protected boolean profileSQL = false; + + /** The current results */ + protected ResultSetInternalMethods results = null; + + protected ResultSetInternalMethods generatedKeysResults = null; + + /** The concurrency for this result set (updatable or not) */ + protected int resultSetConcurrency = 0; + + /** The update count for this statement */ + protected long updateCount = -1; + + /** Should we use the usage advisor? */ + protected boolean useUsageAdvisor = false; + + /** The warnings chain. */ + protected SQLWarning warningChain = null; + + /** + * Should this statement hold results open over .close() irregardless of + * connection's setting? + */ + protected boolean holdResultsOpenOverClose = false; + + protected ArrayList batchedGeneratedKeys = null; + + protected boolean retrieveGeneratedKeys = false; + + protected boolean continueBatchOnError = false; + + protected PingTarget pingTarget = null; + + protected ExceptionInterceptor exceptionInterceptor; + + /** Whether or not the last query was of the form ON DUPLICATE KEY UPDATE */ + protected boolean lastQueryIsOnDupKeyUpdate = false; + + /** Are we currently closing results implicitly (internally)? */ + private boolean isImplicitlyClosingResults = false; + + protected ReadableProperty dontTrackOpenResources; + protected ReadableProperty dumpQueriesOnException; + protected boolean logSlowQueries = false; + protected ReadableProperty rewriteBatchedStatements; + protected ReadableProperty maxAllowedPacket; + protected boolean dontCheckOnDuplicateKeyUpdateInSQL; + protected ReadableProperty sendFractionalSeconds; + + protected ResultSetFactory resultSetFactory; + + protected Query query; + protected NativeSession session = null; + + /** + * Constructor for a Statement. + * + * @param c + * the Connection instance that creates us + * @param catalog + * the database name in use when we were created + * + * @throws SQLException + * if an error occurs. + */ + public StatementImpl(JdbcConnection c, String catalog) throws SQLException { + if ((c == null) || c.isClosed()) { + throw SQLError.createSQLException(Messages.getString("Statement.0"), MysqlErrorNumbers.SQL_STATE_CONNECTION_NOT_OPEN, null); + } + + this.connection = c; + this.session = (NativeSession) c.getSession(); + this.exceptionInterceptor = c.getExceptionInterceptor(); + + initQuery(); + + this.query.setCurrentCatalog(catalog); + + JdbcPropertySet pset = c.getPropertySet(); + + this.dontTrackOpenResources = pset.getBooleanReadableProperty(PropertyDefinitions.PNAME_dontTrackOpenResources); + this.dumpQueriesOnException = pset.getBooleanReadableProperty(PropertyDefinitions.PNAME_dumpQueriesOnException); + this.continueBatchOnError = pset.getBooleanReadableProperty(PropertyDefinitions.PNAME_continueBatchOnError).getValue(); + this.pedantic = pset.getBooleanReadableProperty(PropertyDefinitions.PNAME_pedantic).getValue(); + this.rewriteBatchedStatements = pset.getBooleanReadableProperty(PropertyDefinitions.PNAME_rewriteBatchedStatements); + this.charEncoding = pset.getStringReadableProperty(PropertyDefinitions.PNAME_characterEncoding).getValue(); + this.profileSQL = pset.getBooleanReadableProperty(PropertyDefinitions.PNAME_profileSQL).getValue(); + this.useUsageAdvisor = pset.getBooleanReadableProperty(PropertyDefinitions.PNAME_useUsageAdvisor).getValue(); + this.logSlowQueries = pset.getBooleanReadableProperty(PropertyDefinitions.PNAME_logSlowQueries).getValue(); + this.maxAllowedPacket = pset.getIntegerReadableProperty(PropertyDefinitions.PNAME_maxAllowedPacket); + this.dontCheckOnDuplicateKeyUpdateInSQL = pset.getBooleanReadableProperty(PropertyDefinitions.PNAME_dontCheckOnDuplicateKeyUpdateInSQL).getValue(); + this.sendFractionalSeconds = pset.getBooleanReadableProperty(PropertyDefinitions.PNAME_sendFractionalSeconds); + this.doEscapeProcessing = pset.getBooleanReadableProperty(PropertyDefinitions.PNAME_enableEscapeProcessing).getValue(); + + this.maxFieldSize = this.maxAllowedPacket.getValue(); + + if (!this.dontTrackOpenResources.getValue()) { + c.registerStatement(this); + } + + int defaultFetchSize = pset.getIntegerReadableProperty(PropertyDefinitions.PNAME_defaultFetchSize).getValue(); + if (defaultFetchSize != 0) { + setFetchSize(defaultFetchSize); + } + + boolean profiling = this.profileSQL || this.useUsageAdvisor || this.logSlowQueries; + + if (profiling) { + this.pointOfOrigin = LogUtils.findCallingClassAndMethod(new Throwable()); + try { + this.query.setEventSink(ProfilerEventHandlerFactory.getInstance(this.session)); + } catch (CJException e) { + throw SQLExceptionsMapping.translateException(e, getExceptionInterceptor()); + } + } + + int maxRowsConn = pset.getIntegerReadableProperty(PropertyDefinitions.PNAME_maxRows).getValue(); + + if (maxRowsConn != -1) { + setMaxRows(maxRowsConn); + } + + this.holdResultsOpenOverClose = pset. getModifiableProperty(PropertyDefinitions.PNAME_holdResultsOpenOverStatementClose).getValue(); + + this.resultSetFactory = new ResultSetFactory(this.connection, this); + } + + protected void initQuery() { + this.query = new SimpleQuery(this.session); + } + + /** + * @param sql + * + * @throws SQLException + */ + public void addBatch(String sql) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (sql != null) { + this.query.addBatch(sql); + } + } + } + + @Override + public void addBatch(Object batch) { + this.query.addBatch(batch); + } + + @Override + public List getBatchedArgs() { + return this.query.getBatchedArgs(); + } + + /** + * Cancels this Statement object if both the DBMS and driver support + * aborting an SQL statement. This method can be used by one thread to + * cancel a statement that is being executed by another thread. + */ + public void cancel() throws SQLException { + if (!this.query.getStatementExecuting().get()) { + return; + } + + if (!this.isClosed && this.connection != null) { + JdbcConnection cancelConn = null; + java.sql.Statement cancelStmt = null; + + try { + HostInfo hostInfo = this.session.getHostInfo(); + String database = hostInfo.getDatabase(); + String user = StringUtils.isNullOrEmpty(hostInfo.getUser()) ? "" : hostInfo.getUser(); + String password = StringUtils.isNullOrEmpty(hostInfo.getPassword()) ? "" : hostInfo.getPassword(); + NativeSession newSession = new NativeSession(this.session.getHostInfo(), this.session.getPropertySet()); + newSession.connect(hostInfo, user, password, database, 30000, new TransactionEventHandler() { + @Override + public void transactionCompleted() { + } + + public void transactionBegun() { + } + }); + newSession.sendCommand(new NativeMessageBuilder().buildComQuery(newSession.getSharedSendPacket(), "KILL QUERY " + this.session.getThreadId()), + false, 0); + setCancelStatus(CancelStatus.CANCELED_BY_USER); + } catch (IOException e) { + throw SQLExceptionsMapping.translateException(e, this.exceptionInterceptor); + } finally { + if (cancelStmt != null) { + cancelStmt.close(); + } + + if (cancelConn != null) { + cancelConn.close(); + } + } + + } + } + + // --------------------------JDBC 2.0----------------------------- + + /** + * Checks if closed() has been called, and throws an exception if so + * + * @throws StatementIsClosedException + * if this statement has been closed + */ + protected JdbcConnection checkClosed() { + JdbcConnection c = this.connection; + + if (c == null) { + throw ExceptionFactory.createException(StatementIsClosedException.class, Messages.getString("Statement.AlreadyClosed"), getExceptionInterceptor()); + } + + return c; + } + + /** + * Checks if the given SQL query with the given first non-ws char is a DML + * statement. Throws an exception if it is. + * + * @param sql + * the SQL to check + * @param firstStatementChar + * the UC first non-ws char of the statement + * + * @throws SQLException + * if the statement contains DML + */ + protected void checkForDml(String sql, char firstStatementChar) throws SQLException { + if ((firstStatementChar == 'I') || (firstStatementChar == 'U') || (firstStatementChar == 'D') || (firstStatementChar == 'A') + || (firstStatementChar == 'C') || (firstStatementChar == 'T') || (firstStatementChar == 'R')) { + String noCommentSql = StringUtils.stripComments(sql, "'\"", "'\"", true, false, true, true); + + if (StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "INSERT") || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "UPDATE") + || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DELETE") || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DROP") + || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "CREATE") || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "ALTER") + || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "TRUNCATE") || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "RENAME")) { + throw SQLError.createSQLException(Messages.getString("Statement.57"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + } + + /** + * Method checkNullOrEmptyQuery. + * + * @param sql + * the SQL to check + * + * @throws SQLException + * if query is null or empty. + */ + protected void checkNullOrEmptyQuery(String sql) throws SQLException { + if (sql == null) { + throw SQLError.createSQLException(Messages.getString("Statement.59"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (sql.length() == 0) { + throw SQLError.createSQLException(Messages.getString("Statement.61"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * JDBC 2.0 Make the set of commands in the current batch empty. This method + * is optional. + * + * @exception SQLException + * if a database-access error occurs, or the driver does not + * support batch statements + */ + public void clearBatch() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.query.clearBatchedArgs(); + } + } + + public void clearBatchedArgs() { + this.query.clearBatchedArgs(); + } + + /** + * After this call, getWarnings returns null until a new warning is reported + * for this Statement. + * + * @exception SQLException + * if a database access error occurs (why?) + */ + public void clearWarnings() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + setClearWarningsCalled(true); + this.warningChain = null; + // TODO souldn't we also clear warnings from _server_ ? + } + } + + /** + * In many cases, it is desirable to immediately release a Statement's + * database and JDBC resources instead of waiting for this to happen when it + * is automatically closed. The close method provides this immediate + * release. + * + *

+ * Note: A Statement is automatically closed when it is garbage collected. When a Statement is closed, its current ResultSet, if one exists, is also + * closed. + *

+ * + * @exception SQLException + * if a database access error occurs + */ + public void close() throws SQLException { + realClose(true, true); + } + + /** + * Close any open result sets that have been 'held open' + */ + protected void closeAllOpenResults() throws SQLException { + JdbcConnection locallyScopedConn = this.connection; + + if (locallyScopedConn == null) { + return; // already closed + } + + synchronized (locallyScopedConn.getConnectionMutex()) { + if (this.openResults != null) { + for (ResultSetInternalMethods element : this.openResults) { + try { + element.realClose(false); + } catch (SQLException sqlEx) { + AssertionFailedException.shouldNotHappen(sqlEx); + } + } + + this.openResults.clear(); + } + } + } + + /** + * Close all result sets in this statement. This includes multi-results + */ + protected void implicitlyCloseAllOpenResults() throws SQLException { + this.isImplicitlyClosingResults = true; + try { + if (!(this.holdResultsOpenOverClose || this.dontTrackOpenResources.getValue())) { + if (this.results != null) { + this.results.realClose(false); + } + if (this.generatedKeysResults != null) { + this.generatedKeysResults.realClose(false); + } + closeAllOpenResults(); + } + } finally { + this.isImplicitlyClosingResults = false; + } + } + + public void removeOpenResultSet(ResultSetInternalMethods rs) { + try { + synchronized (checkClosed().getConnectionMutex()) { + if (this.openResults != null) { + this.openResults.remove(rs); + } + + boolean hasMoreResults = rs.getNextResultset() != null; + + // clear the current results or GGK results + if (this.results == rs && !hasMoreResults) { + this.results = null; + } + if (this.generatedKeysResults == rs) { + this.generatedKeysResults = null; + } + + // trigger closeOnCompletion if: + // a) the result set removal wasn't triggered internally + // b) there are no additional results + if (!this.isImplicitlyClosingResults && !hasMoreResults) { + checkAndPerformCloseOnCompletionAction(); + } + } + } catch (StatementIsClosedException e) { + // we can't break the interface, having this be no-op in case of error is ok + } + } + + public int getOpenResultSetCount() { + try { + synchronized (checkClosed().getConnectionMutex()) { + if (this.openResults != null) { + return this.openResults.size(); + } + + return 0; + } + } catch (StatementIsClosedException e) { + // we can't break the interface, having this be no-op in case of error is ok + + return 0; + } + } + + /** + * Check if all ResultSets generated by this statement are closed. If so, + * close this statement. + */ + private void checkAndPerformCloseOnCompletionAction() { + try { + synchronized (checkClosed().getConnectionMutex()) { + if (isCloseOnCompletion() && !this.dontTrackOpenResources.getValue() && getOpenResultSetCount() == 0 + && (this.results == null || !this.results.hasRows() || this.results.isClosed()) + && (this.generatedKeysResults == null || !this.generatedKeysResults.hasRows() || this.generatedKeysResults.isClosed())) { + realClose(false, false); + } + } + } catch (SQLException e) { + } + } + + /** + * @param sql + */ + private ResultSetInternalMethods createResultSetUsingServerFetch(String sql) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + java.sql.PreparedStatement pStmt = this.connection.prepareStatement(sql, this.query.getResultType().getIntValue(), this.resultSetConcurrency); + + pStmt.setFetchSize(this.query.getResultFetchSize()); + + if (this.maxRows > -1) { + pStmt.setMaxRows(this.maxRows); + } + + statementBegins(); + + pStmt.execute(); + + // + // Need to be able to get resultset irrespective if we issued DML or not to make this work. + // + ResultSetInternalMethods rs = ((StatementImpl) pStmt).getResultSetInternal(); + + rs.setStatementUsedForFetchingRows((ClientPreparedStatement) pStmt); + + this.results = rs; + + return rs; + } + } + + /** + * We only stream result sets when they are forward-only, read-only, and the + * fetch size has been set to Integer.MIN_VALUE + * + * @return true if this result set should be streamed row at-a-time, rather + * than read all at once. + */ + protected boolean createStreamingResultSet() { + return ((this.query.getResultType() == Type.FORWARD_ONLY) && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY) + && (this.query.getResultFetchSize() == Integer.MIN_VALUE)); + } + + private Resultset.Type originalResultSetType = Type.FORWARD_ONLY; + private int originalFetchSize = 0; + + public void enableStreamingResults() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.originalResultSetType = this.query.getResultType(); + this.originalFetchSize = this.query.getResultFetchSize(); + + setFetchSize(Integer.MIN_VALUE); + setResultSetType(Type.FORWARD_ONLY); + } + } + + public void disableStreamingResults() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.query.getResultFetchSize() == Integer.MIN_VALUE && this.query.getResultType() == Type.FORWARD_ONLY) { + setFetchSize(this.originalFetchSize); + setResultSetType(this.originalResultSetType); + } + } + } + + /** + * Adjust net_write_timeout to a higher value if we're streaming result sets. More often than not, someone runs into + * an issue where they blow net_write_timeout when using this feature, and if they're willing to hold a result set open + * for 30 seconds or more, one more round-trip isn't going to hurt. + * + * This is reset by RowDataDynamic.close(). + */ + protected void setupStreamingTimeout(JdbcConnection con) throws SQLException { + int netTimeoutForStreamingResults = this.session.getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_netTimeoutForStreamingResults) + .getValue(); + + if (createStreamingResultSet() && netTimeoutForStreamingResults > 0) { + executeSimpleNonQuery(con, "SET net_write_timeout=" + netTimeoutForStreamingResults); + } + } + + public CancelQueryTask startQueryTimer(Query stmtToCancel, int timeout) { + return this.query.startQueryTimer(stmtToCancel, timeout); + } + + public void stopQueryTimer(CancelQueryTask timeoutTask, boolean rethrowCancelReason, boolean checkCancelTimeout) { + this.query.stopQueryTimer(timeoutTask, rethrowCancelReason, checkCancelTimeout); + } + + /** + * Execute a SQL statement that may return multiple results. We don't have + * to worry about this since we do not support multiple ResultSets. You can + * use getResultSet or getUpdateCount to retrieve the result. + * + * @param sql + * any SQL statement + * + * @return true if the next result is a ResulSet, false if it is an update + * count or there are no more results + * + * @exception SQLException + * if a database access error occurs + */ + public boolean execute(String sql) throws SQLException { + return executeInternal(sql, false); + } + + private boolean executeInternal(String sql, boolean returnGeneratedKeys) throws SQLException { + JdbcConnection locallyScopedConn = checkClosed(); + + synchronized (locallyScopedConn.getConnectionMutex()) { + checkClosed(); + + checkNullOrEmptyQuery(sql); + + resetCancelledState(); + + implicitlyCloseAllOpenResults(); + + if (sql.charAt(0) == '/') { + if (sql.startsWith(PING_MARKER)) { + doPingInstead(); + + return true; + } + } + + char firstNonWsChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql)); + boolean maybeSelect = firstNonWsChar == 'S'; + + this.retrieveGeneratedKeys = returnGeneratedKeys; + + this.lastQueryIsOnDupKeyUpdate = returnGeneratedKeys && firstNonWsChar == 'I' && containsOnDuplicateKeyInString(sql); + + if (!maybeSelect && locallyScopedConn.isReadOnly()) { + throw SQLError.createSQLException(Messages.getString("Statement.27") + Messages.getString("Statement.28"), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + try { + setupStreamingTimeout(locallyScopedConn); + + if (this.doEscapeProcessing) { + Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, this.session.getServerSession().getDefaultTimeZone(), + this.session.getServerSession().getCapabilities().serverSupportsFracSecs(), getExceptionInterceptor()); + sql = escapedSqlResult instanceof String ? (String) escapedSqlResult : ((EscapeProcessorResult) escapedSqlResult).escapedSql; + } + + CachedResultSetMetaData cachedMetaData = null; + + ResultSetInternalMethods rs = null; + + this.batchedGeneratedKeys = null; + + if (useServerFetch()) { + rs = createResultSetUsingServerFetch(sql); + } else { + CancelQueryTask timeoutTask = null; + + String oldCatalog = null; + + try { + timeoutTask = startQueryTimer(this, getTimeoutInMillis()); + + if (!locallyScopedConn.getCatalog().equals(getCurrentCatalog())) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(getCurrentCatalog()); + } + + // Check if we have cached metadata for this query... + if (locallyScopedConn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_cacheResultSetMetadata).getValue()) { + cachedMetaData = locallyScopedConn.getCachedMetaData(sql); + } + + // Only apply max_rows to selects + locallyScopedConn.setSessionMaxRows(maybeSelect ? this.maxRows : -1); + + statementBegins(); + + rs = ((NativeSession) locallyScopedConn.getSession()).execSQL(this, sql, this.maxRows, null, createStreamingResultSet(), + getResultSetFactory(), getCurrentCatalog(), cachedMetaData, false); + + if (timeoutTask != null) { + stopQueryTimer(timeoutTask, true, true); + timeoutTask = null; + } + + } catch (CJTimeoutException | OperationCancelledException e) { + throw SQLExceptionsMapping.translateException(e, this.exceptionInterceptor); + + } finally { + stopQueryTimer(timeoutTask, false, false); + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + } + } + + if (rs != null) { + this.lastInsertId = rs.getUpdateID(); + + this.results = rs; + + rs.setFirstCharOfQuery(firstNonWsChar); + + if (rs.hasRows()) { + if (cachedMetaData != null) { + locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData, this.results); + } else if (this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_cacheResultSetMetadata).getValue()) { + locallyScopedConn.initializeResultsMetadataFromCache(sql, null /* will be created */, this.results); + } + } + } + + return ((rs != null) && rs.hasRows()); + } finally { + this.query.getStatementExecuting().set(false); + } + } + } + + public void statementBegins() { + this.query.statementBegins(); + } + + @Override + public void resetCancelledState() { + synchronized (checkClosed().getConnectionMutex()) { + this.query.resetCancelledState(); + } + } + + /** + * @see StatementImpl#execute(String, int) + */ + public boolean execute(String sql, int returnGeneratedKeys) throws SQLException { + return executeInternal(sql, returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS); + } + + /** + * @see StatementImpl#execute(String, int[]) + */ + public boolean execute(String sql, int[] generatedKeyIndices) throws SQLException { + return executeInternal(sql, generatedKeyIndices != null && generatedKeyIndices.length > 0); + } + + /** + * @see StatementImpl#execute(String, String[]) + */ + public boolean execute(String sql, String[] generatedKeyNames) throws SQLException { + return executeInternal(sql, generatedKeyNames != null && generatedKeyNames.length > 0); + } + + /** + * JDBC 2.0 Submit a batch of commands to the database for execution. This + * method is optional. + * + * @return an array of update counts containing one element for each command + * in the batch. The array is ordered according to the order in + * which commands were inserted into the batch + * + * @exception SQLException + * if a database-access error occurs, or the driver does not + * support batch statements + * @throws java.sql.BatchUpdateException + */ + public int[] executeBatch() throws SQLException { + return Util.truncateAndConvertToInt(executeBatchInternal()); + } + + protected long[] executeBatchInternal() throws SQLException { + JdbcConnection locallyScopedConn = checkClosed(); + + synchronized (locallyScopedConn.getConnectionMutex()) { + if (locallyScopedConn.isReadOnly()) { + throw SQLError.createSQLException(Messages.getString("Statement.34") + Messages.getString("Statement.35"), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + implicitlyCloseAllOpenResults(); + + List batchedArgs = this.query.getBatchedArgs(); + + if (batchedArgs == null || batchedArgs.size() == 0) { + return new long[0]; + } + + // we timeout the entire batch, not individual statements + int individualStatementTimeout = getTimeoutInMillis(); + setTimeoutInMillis(0); + + CancelQueryTask timeoutTask = null; + + try { + resetCancelledState(); + + statementBegins(); + + try { + this.retrieveGeneratedKeys = true; // The JDBC spec doesn't forbid this, but doesn't provide for it either...we do.. + + long[] updateCounts = null; + + if (batchedArgs != null) { + int nbrCommands = batchedArgs.size(); + + this.batchedGeneratedKeys = new ArrayList<>(batchedArgs.size()); + + boolean multiQueriesEnabled = locallyScopedConn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_allowMultiQueries) + .getValue(); + + if (multiQueriesEnabled + || (locallyScopedConn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_rewriteBatchedStatements).getValue() + && nbrCommands > 4)) { + return executeBatchUsingMultiQueries(multiQueriesEnabled, nbrCommands, individualStatementTimeout); + } + + timeoutTask = startQueryTimer(this, individualStatementTimeout); + + updateCounts = new long[nbrCommands]; + + for (int i = 0; i < nbrCommands; i++) { + updateCounts[i] = -3; + } + + SQLException sqlEx = null; + + int commandIndex = 0; + + for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) { + try { + String sql = (String) batchedArgs.get(commandIndex); + updateCounts[commandIndex] = executeUpdateInternal(sql, true, true); + + if (timeoutTask != null) { + // we need to check the cancel state on each iteration to generate timeout exception if needed + checkCancelTimeout(); + } + + // limit one generated key per OnDuplicateKey statement + getBatchedGeneratedKeys(this.results.getFirstCharOfQuery() == 'I' && containsOnDuplicateKeyInString(sql) ? 1 : 0); + } catch (SQLException ex) { + updateCounts[commandIndex] = EXECUTE_FAILED; + + if (this.continueBatchOnError && !(ex instanceof MySQLTimeoutException) && !(ex instanceof MySQLStatementCancelledException) + && !hasDeadlockOrTimeoutRolledBackTx(ex)) { + sqlEx = ex; + } else { + long[] newUpdateCounts = new long[commandIndex]; + + if (hasDeadlockOrTimeoutRolledBackTx(ex)) { + for (int i = 0; i < newUpdateCounts.length; i++) { + newUpdateCounts[i] = java.sql.Statement.EXECUTE_FAILED; + } + } else { + System.arraycopy(updateCounts, 0, newUpdateCounts, 0, commandIndex); + } + + sqlEx = ex; + break; + //throw SQLError.createBatchUpdateException(ex, newUpdateCounts, getExceptionInterceptor()); + } + } + } + + if (sqlEx != null) { + throw SQLError.createBatchUpdateException(sqlEx, updateCounts, getExceptionInterceptor()); + } + } + + if (timeoutTask != null) { + stopQueryTimer(timeoutTask, true, true); + timeoutTask = null; + } + + return (updateCounts != null) ? updateCounts : new long[0]; + } finally { + this.query.getStatementExecuting().set(false); + } + } finally { + + stopQueryTimer(timeoutTask, false, false); + resetCancelledState(); + + setTimeoutInMillis(individualStatementTimeout); + + clearBatch(); + } + } + } + + protected final boolean hasDeadlockOrTimeoutRolledBackTx(SQLException ex) { + int vendorCode = ex.getErrorCode(); + + switch (vendorCode) { + case MysqlErrorNumbers.ER_LOCK_DEADLOCK: + case MysqlErrorNumbers.ER_LOCK_TABLE_FULL: + return true; + case MysqlErrorNumbers.ER_LOCK_WAIT_TIMEOUT: + return false; + default: + return false; + } + } + + /** + * Rewrites batch into a single query to send to the server. This method + * will constrain each batch to be shorter than max_allowed_packet on the + * server. + * + * @return update counts in the same manner as executeBatch() + * @throws SQLException + */ + private long[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled, int nbrCommands, int individualStatementTimeout) throws SQLException { + + JdbcConnection locallyScopedConn = checkClosed(); + + synchronized (locallyScopedConn.getConnectionMutex()) { + if (!multiQueriesEnabled) { + this.session.enableMultiQueries(); + } + + java.sql.Statement batchStmt = null; + + CancelQueryTask timeoutTask = null; + + try { + long[] updateCounts = new long[nbrCommands]; + + for (int i = 0; i < nbrCommands; i++) { + updateCounts[i] = JdbcStatement.EXECUTE_FAILED; + } + + int commandIndex = 0; + + StringBuilder queryBuf = new StringBuilder(); + + batchStmt = locallyScopedConn.createStatement(); + + timeoutTask = startQueryTimer((StatementImpl) batchStmt, individualStatementTimeout); + + int counter = 0; + + String connectionEncoding = locallyScopedConn.getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_characterEncoding) + .getValue(); + + int numberOfBytesPerChar = StringUtils.startsWithIgnoreCase(connectionEncoding, "utf") ? 3 + : (CharsetMapping.isMultibyteCharset(connectionEncoding) ? 2 : 1); + + int escapeAdjust = 1; + + batchStmt.setEscapeProcessing(this.doEscapeProcessing); + + if (this.doEscapeProcessing) { + escapeAdjust = 2; // We assume packet _could_ grow by this amount, as we're not sure how big statement will end up after escape processing + } + + SQLException sqlEx = null; + + int argumentSetsInBatchSoFar = 0; + + for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) { + String nextQuery = (String) this.query.getBatchedArgs().get(commandIndex); + + if (((((queryBuf.length() + nextQuery.length()) * numberOfBytesPerChar) + 1 /* for semicolon */ + + NativeConstants.HEADER_LENGTH) * escapeAdjust) + 32 > this.maxAllowedPacket.getValue()) { + try { + batchStmt.execute(queryBuf.toString(), java.sql.Statement.RETURN_GENERATED_KEYS); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(commandIndex, argumentSetsInBatchSoFar, updateCounts, ex); + } + + counter = processMultiCountsAndKeys((StatementImpl) batchStmt, counter, updateCounts); + + queryBuf = new StringBuilder(); + argumentSetsInBatchSoFar = 0; + } + + queryBuf.append(nextQuery); + queryBuf.append(";"); + argumentSetsInBatchSoFar++; + } + + if (queryBuf.length() > 0) { + try { + batchStmt.execute(queryBuf.toString(), java.sql.Statement.RETURN_GENERATED_KEYS); + } catch (SQLException ex) { + sqlEx = handleExceptionForBatch(commandIndex - 1, argumentSetsInBatchSoFar, updateCounts, ex); + } + + counter = processMultiCountsAndKeys((StatementImpl) batchStmt, counter, updateCounts); + } + + if (timeoutTask != null) { + stopQueryTimer(timeoutTask, true, true); + timeoutTask = null; + } + + if (sqlEx != null) { + throw SQLError.createBatchUpdateException(sqlEx, updateCounts, getExceptionInterceptor()); + } + + return (updateCounts != null) ? updateCounts : new long[0]; + } finally { + stopQueryTimer(timeoutTask, false, false); + resetCancelledState(); + + try { + if (batchStmt != null) { + batchStmt.close(); + } + } finally { + if (!multiQueriesEnabled) { + this.session.disableMultiQueries(); + } + } + } + } + } + + protected int processMultiCountsAndKeys(StatementImpl batchedStatement, int updateCountCounter, long[] updateCounts) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + updateCounts[updateCountCounter++] = batchedStatement.getLargeUpdateCount(); + + boolean doGenKeys = this.batchedGeneratedKeys != null; + + byte[][] row = null; + + if (doGenKeys) { + long generatedKey = batchedStatement.getLastInsertID(); + + row = new byte[1][]; + row[0] = StringUtils.getBytes(Long.toString(generatedKey)); + this.batchedGeneratedKeys.add(new ByteArrayRow(row, getExceptionInterceptor())); + } + + while (batchedStatement.getMoreResults() || batchedStatement.getLargeUpdateCount() != -1) { + updateCounts[updateCountCounter++] = batchedStatement.getLargeUpdateCount(); + + if (doGenKeys) { + long generatedKey = batchedStatement.getLastInsertID(); + + row = new byte[1][]; + row[0] = StringUtils.getBytes(Long.toString(generatedKey)); + this.batchedGeneratedKeys.add(new ByteArrayRow(row, getExceptionInterceptor())); + } + } + + return updateCountCounter; + } + } + + protected SQLException handleExceptionForBatch(int endOfBatchIndex, int numValuesPerBatch, long[] updateCounts, SQLException ex) + throws BatchUpdateException, SQLException { + for (int j = endOfBatchIndex; j > endOfBatchIndex - numValuesPerBatch; j--) { + updateCounts[j] = EXECUTE_FAILED; + } + + if (this.continueBatchOnError && !(ex instanceof MySQLTimeoutException) && !(ex instanceof MySQLStatementCancelledException) + && !hasDeadlockOrTimeoutRolledBackTx(ex)) { + return ex; + } // else: throw the exception immediately + + long[] newUpdateCounts = new long[endOfBatchIndex]; + System.arraycopy(updateCounts, 0, newUpdateCounts, 0, endOfBatchIndex); + + throw SQLError.createBatchUpdateException(ex, newUpdateCounts, getExceptionInterceptor()); + } + + /** + * Execute a SQL statement that returns a single ResultSet + * + * @param sql + * typically a static SQL SELECT statement + * + * @return a ResulSet that contains the data produced by the query + * + * @exception SQLException + * if a database access error occurs + */ + public java.sql.ResultSet executeQuery(String sql) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + JdbcConnection locallyScopedConn = this.connection; + + this.retrieveGeneratedKeys = false; + + checkNullOrEmptyQuery(sql); + + resetCancelledState(); + + implicitlyCloseAllOpenResults(); + + if (sql.charAt(0) == '/') { + if (sql.startsWith(PING_MARKER)) { + doPingInstead(); + + return this.results; + } + } + + setupStreamingTimeout(locallyScopedConn); + + if (this.doEscapeProcessing) { + Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, this.session.getServerSession().getDefaultTimeZone(), + this.session.getServerSession().getCapabilities().serverSupportsFracSecs(), getExceptionInterceptor()); + sql = escapedSqlResult instanceof String ? (String) escapedSqlResult : ((EscapeProcessorResult) escapedSqlResult).escapedSql; + } + + char firstStatementChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql)); + + checkForDml(sql, firstStatementChar); + + CachedResultSetMetaData cachedMetaData = null; + + if (useServerFetch()) { + this.results = createResultSetUsingServerFetch(sql); + + return this.results; + } + + CancelQueryTask timeoutTask = null; + + String oldCatalog = null; + + try { + timeoutTask = startQueryTimer(this, getTimeoutInMillis()); + + if (!locallyScopedConn.getCatalog().equals(getCurrentCatalog())) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(getCurrentCatalog()); + } + + // + // Check if we have cached metadata for this query... + // + if (locallyScopedConn.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_cacheResultSetMetadata).getValue()) { + cachedMetaData = locallyScopedConn.getCachedMetaData(sql); + } + + locallyScopedConn.setSessionMaxRows(this.maxRows); + + statementBegins(); + + this.results = ((NativeSession) locallyScopedConn.getSession()).execSQL(this, sql, this.maxRows, null, createStreamingResultSet(), + getResultSetFactory(), getCurrentCatalog(), cachedMetaData, false); + + if (timeoutTask != null) { + stopQueryTimer(timeoutTask, true, true); + timeoutTask = null; + } + + } catch (CJTimeoutException | OperationCancelledException e) { + throw SQLExceptionsMapping.translateException(e, this.exceptionInterceptor); + + } finally { + this.query.getStatementExecuting().set(false); + + stopQueryTimer(timeoutTask, false, false); + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + } + + this.lastInsertId = this.results.getUpdateID(); + + if (cachedMetaData != null) { + locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData, this.results); + } else { + if (this.connection.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_cacheResultSetMetadata).getValue()) { + locallyScopedConn.initializeResultsMetadataFromCache(sql, null /* will be created */, this.results); + } + } + + return this.results; + } + } + + protected void doPingInstead() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.pingTarget != null) { + try { + this.pingTarget.doPing(); + } catch (SQLException e) { + throw e; + } catch (Exception e) { + throw SQLError.createSQLException(e.getMessage(), MysqlErrorNumbers.SQL_STATE_COMMUNICATION_LINK_FAILURE, e, getExceptionInterceptor()); + } + } else { + this.connection.ping(); + } + + ResultSetInternalMethods fakeSelectOneResultSet = generatePingResultSet(); + this.results = fakeSelectOneResultSet; + } + } + + protected ResultSetInternalMethods generatePingResultSet() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + String encoding = this.session.getServerSession().getCharacterSetMetadata(); + int collationIndex = this.session.getServerSession().getMetadataCollationIndex(); + Field[] fields = { new Field(null, "1", collationIndex, encoding, MysqlType.BIGINT, 1) }; + ArrayList rows = new ArrayList<>(); + byte[] colVal = new byte[] { (byte) '1' }; + + rows.add(new ByteArrayRow(new byte[][] { colVal }, getExceptionInterceptor())); + + return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(rows, new DefaultColumnDefinition(fields))); + } + } + + public void executeSimpleNonQuery(JdbcConnection c, String nonQuery) throws SQLException { + synchronized (c.getConnectionMutex()) { + ((NativeSession) c.getSession()). execSQL(this, nonQuery, -1, null, false, getResultSetFactory(), getCurrentCatalog(), null, false) + .close(); + } + } + + /** + * Execute a SQL INSERT, UPDATE or DELETE statement. In addition SQL statements that return nothing such as SQL DDL statements can be executed. + * + * @param sql + * a SQL statement + * + * @return either a row count, or 0 for SQL commands + * + * @exception SQLException + * if a database access error occurs + */ + public int executeUpdate(String sql) throws SQLException { + return Util.truncateAndConvertToInt(executeLargeUpdate(sql)); + } + + protected long executeUpdateInternal(String sql, boolean isBatch, boolean returnGeneratedKeys) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + JdbcConnection locallyScopedConn = this.connection; + + checkNullOrEmptyQuery(sql); + + resetCancelledState(); + + char firstStatementChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql)); + + this.retrieveGeneratedKeys = returnGeneratedKeys; + + this.lastQueryIsOnDupKeyUpdate = returnGeneratedKeys && firstStatementChar == 'I' && containsOnDuplicateKeyInString(sql); + + ResultSetInternalMethods rs = null; + + if (this.doEscapeProcessing) { + Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, this.session.getServerSession().getDefaultTimeZone(), + this.session.getServerSession().getCapabilities().serverSupportsFracSecs(), getExceptionInterceptor()); + sql = escapedSqlResult instanceof String ? (String) escapedSqlResult : ((EscapeProcessorResult) escapedSqlResult).escapedSql; + } + + if (locallyScopedConn.isReadOnly(false)) { + throw SQLError.createSQLException(Messages.getString("Statement.42") + Messages.getString("Statement.43"), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (StringUtils.startsWithIgnoreCaseAndWs(sql, "select")) { + throw SQLError.createSQLException(Messages.getString("Statement.46"), "01S03", getExceptionInterceptor()); + } + + implicitlyCloseAllOpenResults(); + + // The checking and changing of catalogs must happen in sequence, so synchronize on the same mutex that _conn is using + + CancelQueryTask timeoutTask = null; + + String oldCatalog = null; + + try { + timeoutTask = startQueryTimer(this, getTimeoutInMillis()); + + if (!locallyScopedConn.getCatalog().equals(getCurrentCatalog())) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(getCurrentCatalog()); + } + + // + // Only apply max_rows to selects + // + locallyScopedConn.setSessionMaxRows(-1); + + statementBegins(); + + // null catalog: force read of field info on DML + rs = ((NativeSession) locallyScopedConn.getSession()).execSQL(this, sql, -1, null, false, getResultSetFactory(), getCurrentCatalog(), null, + isBatch); + + if (timeoutTask != null) { + stopQueryTimer(timeoutTask, true, true); + timeoutTask = null; + } + + } catch (CJTimeoutException | OperationCancelledException e) { + throw SQLExceptionsMapping.translateException(e, this.exceptionInterceptor); + + } finally { + stopQueryTimer(timeoutTask, false, false); + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + + if (!isBatch) { + this.query.getStatementExecuting().set(false); + } + } + + this.results = rs; + + rs.setFirstCharOfQuery(firstStatementChar); + + this.updateCount = rs.getUpdateCount(); + + this.lastInsertId = rs.getUpdateID(); + + return this.updateCount; + } + } + + /** + * @see StatementImpl#executeUpdate(String, int) + */ + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + return Util.truncateAndConvertToInt(executeLargeUpdate(sql, autoGeneratedKeys)); + } + + /** + * @see StatementImpl#executeUpdate(String, int[]) + */ + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + return Util.truncateAndConvertToInt(executeLargeUpdate(sql, columnIndexes)); + } + + /** + * @see StatementImpl#executeUpdate(String, String[]) + */ + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + return Util.truncateAndConvertToInt(executeLargeUpdate(sql, columnNames)); + } + + /** + * JDBC 2.0 Return the Connection that produced the Statement. + * + * @return the Connection that produced the Statement + * + * @throws SQLException + * if an error occurs + */ + public java.sql.Connection getConnection() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.connection; + } + } + + /** + * JDBC 2.0 Determine the fetch direction. + * + * @return the default fetch direction + * + * @exception SQLException + * if a database-access error occurs + */ + public int getFetchDirection() throws SQLException { + return java.sql.ResultSet.FETCH_FORWARD; + } + + /** + * JDBC 2.0 Determine the default fetch size. + * + * @return the number of rows to fetch at a time + * + * @throws SQLException + * if an error occurs + */ + public int getFetchSize() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.query.getResultFetchSize(); + } + } + + /** + * @throws SQLException + */ + public java.sql.ResultSet getGeneratedKeys() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (!this.retrieveGeneratedKeys) { + throw SQLError.createSQLException(Messages.getString("Statement.GeneratedKeysNotRequested"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + if (this.batchedGeneratedKeys == null) { + if (this.lastQueryIsOnDupKeyUpdate) { + return this.generatedKeysResults = getGeneratedKeysInternal(1); + } + return this.generatedKeysResults = getGeneratedKeysInternal(); + } + + String encoding = this.session.getServerSession().getCharacterSetMetadata(); + int collationIndex = this.session.getServerSession().getMetadataCollationIndex(); + Field[] fields = new Field[1]; + fields[0] = new Field("", "GENERATED_KEY", collationIndex, encoding, MysqlType.BIGINT_UNSIGNED, 20); + + this.generatedKeysResults = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(this.batchedGeneratedKeys, new DefaultColumnDefinition(fields))); + + return this.generatedKeysResults; + } + } + + /* + * Needed because there's no concept of super.super to get to this + * implementation from ServerPreparedStatement when dealing with batched + * updates. + */ + protected ResultSetInternalMethods getGeneratedKeysInternal() throws SQLException { + long numKeys = getLargeUpdateCount(); + return getGeneratedKeysInternal(numKeys); + } + + protected ResultSetInternalMethods getGeneratedKeysInternal(long numKeys) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + String encoding = this.session.getServerSession().getCharacterSetMetadata(); + int collationIndex = this.session.getServerSession().getMetadataCollationIndex(); + Field[] fields = new Field[1]; + fields[0] = new Field("", "GENERATED_KEY", collationIndex, encoding, MysqlType.BIGINT_UNSIGNED, 20); + + ArrayList rowSet = new ArrayList<>(); + + long beginAt = getLastInsertID(); + + if (this.results != null) { + String serverInfo = this.results.getServerInfo(); + + // + // Only parse server info messages for 'REPLACE' queries + // + if ((numKeys > 0) && (this.results.getFirstCharOfQuery() == 'R') && (serverInfo != null) && (serverInfo.length() > 0)) { + numKeys = getRecordCountFromInfo(serverInfo); + } + + if ((beginAt != 0 /* BIGINT UNSIGNED can wrap the protocol representation */) && (numKeys > 0)) { + for (int i = 0; i < numKeys; i++) { + byte[][] row = new byte[1][]; + if (beginAt > 0) { + row[0] = StringUtils.getBytes(Long.toString(beginAt)); + } else { + byte[] asBytes = new byte[8]; + asBytes[7] = (byte) (beginAt & 0xff); + asBytes[6] = (byte) (beginAt >>> 8); + asBytes[5] = (byte) (beginAt >>> 16); + asBytes[4] = (byte) (beginAt >>> 24); + asBytes[3] = (byte) (beginAt >>> 32); + asBytes[2] = (byte) (beginAt >>> 40); + asBytes[1] = (byte) (beginAt >>> 48); + asBytes[0] = (byte) (beginAt >>> 56); + + BigInteger val = new BigInteger(1, asBytes); + + row[0] = val.toString().getBytes(); + } + rowSet.add(new ByteArrayRow(row, getExceptionInterceptor())); + beginAt += this.connection.getAutoIncrementIncrement(); + } + } + } + + ResultSetImpl gkRs = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE, + new ResultsetRowsStatic(rowSet, new DefaultColumnDefinition(fields))); + + return gkRs; + } + } + + /** + * getLastInsertID returns the value of the auto_incremented key after an + * executeQuery() or excute() call. + * + *

+ * This gets around the un-threadsafe behavior of "select LAST_INSERT_ID()" which is tied to the Connection that created this Statement, and therefore could + * have had many INSERTS performed before one gets a chance to call "select LAST_INSERT_ID()". + *

+ * + * @return the last update ID. + */ + public long getLastInsertID() { + synchronized (checkClosed().getConnectionMutex()) { + return this.lastInsertId; + } + } + + /** + * getLongUpdateCount returns the current result as an update count, if the + * result is a ResultSet or there are no more results, -1 is returned. It + * should only be called once per result. + * + *

+ * This method returns longs as MySQL server returns 64-bit values for update counts + *

+ * + * @return the current update count. + */ + public long getLongUpdateCount() { + synchronized (checkClosed().getConnectionMutex()) { + if (this.results == null) { + return -1; + } + + if (this.results.hasRows()) { + return -1; + } + + return this.updateCount; + } + } + + /** + * The maxFieldSize limit (in bytes) is the maximum amount of data returned + * for any column value; it only applies to BINARY, VARBINARY, + * LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR columns. If the limit is + * exceeded, the excess data is silently discarded. + * + * @return the current max column size limit; zero means unlimited + * + * @exception SQLException + * if a database access error occurs + */ + public int getMaxFieldSize() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.maxFieldSize; + } + } + + /** + * The maxRows limit is set to limit the number of rows that any ResultSet + * can contain. If the limit is exceeded, the excess rows are silently + * dropped. + * + * @return the current maximum row limit; zero means unlimited + * + * @exception SQLException + * if a database access error occurs + */ + public int getMaxRows() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.maxRows <= 0) { + return 0; + } + + return this.maxRows; + } + } + + /** + * getMoreResults moves to a Statement's next result. If it returns true, + * this result is a ResulSet. + * + * @return true if the next ResultSet is valid + * + * @exception SQLException + * if a database access error occurs + */ + public boolean getMoreResults() throws SQLException { + return getMoreResults(CLOSE_CURRENT_RESULT); + } + + /** + * @see StatementImpl#getMoreResults(int) + */ + public boolean getMoreResults(int current) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.results == null) { + return false; + } + + boolean streamingMode = createStreamingResultSet(); + + if (streamingMode) { + if (this.results.hasRows()) { + while (this.results.next()) { + // need to drain remaining rows to get to server status which tells us whether more results actually exist or not + } + } + } + + ResultSetInternalMethods nextResultSet = (ResultSetInternalMethods) this.results.getNextResultset(); + + switch (current) { + case java.sql.Statement.CLOSE_CURRENT_RESULT: + + if (this.results != null) { + if (!(streamingMode || this.dontTrackOpenResources.getValue())) { + this.results.realClose(false); + } + + this.results.clearNextResultset(); + } + + break; + + case java.sql.Statement.CLOSE_ALL_RESULTS: + + if (this.results != null) { + if (!(streamingMode || this.dontTrackOpenResources.getValue())) { + this.results.realClose(false); + } + + this.results.clearNextResultset(); + } + + closeAllOpenResults(); + + break; + + case java.sql.Statement.KEEP_CURRENT_RESULT: + if (!this.dontTrackOpenResources.getValue()) { + this.openResults.add(this.results); + } + + this.results.clearNextResultset(); // nobody besides us should + // ever need this value... + break; + + default: + throw SQLError.createSQLException(Messages.getString("Statement.19"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + getExceptionInterceptor()); + } + + this.results = nextResultSet; + + if (this.results == null) { + this.updateCount = -1; + this.lastInsertId = -1; + } else if (this.results.hasRows()) { + this.updateCount = -1; + this.lastInsertId = -1; + } else { + this.updateCount = this.results.getUpdateCount(); + this.lastInsertId = this.results.getUpdateID(); + } + + boolean moreResults = (this.results != null) && this.results.hasRows(); + if (!moreResults) { + checkAndPerformCloseOnCompletionAction(); + } + return moreResults; + } + } + + /** + * The queryTimeout limit is the number of seconds the driver will wait for + * a Statement to execute. If the limit is exceeded, a SQLException is + * thrown. + * + * @return the current query timeout limit in seconds; 0 = unlimited + * + * @exception SQLException + * if a database access error occurs + */ + public int getQueryTimeout() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return getTimeoutInMillis() / 1000; + } + } + + /** + * Parses actual record count from 'info' message + * + * @param serverInfo + */ + private long getRecordCountFromInfo(String serverInfo) { + StringBuilder recordsBuf = new StringBuilder(); + long recordsCount = 0; + long duplicatesCount = 0; + + char c = (char) 0; + + int length = serverInfo.length(); + int i = 0; + + for (; i < length; i++) { + c = serverInfo.charAt(i); + + if (Character.isDigit(c)) { + break; + } + } + + recordsBuf.append(c); + i++; + + for (; i < length; i++) { + c = serverInfo.charAt(i); + + if (!Character.isDigit(c)) { + break; + } + + recordsBuf.append(c); + } + + recordsCount = Long.parseLong(recordsBuf.toString()); + + StringBuilder duplicatesBuf = new StringBuilder(); + + for (; i < length; i++) { + c = serverInfo.charAt(i); + + if (Character.isDigit(c)) { + break; + } + } + + duplicatesBuf.append(c); + i++; + + for (; i < length; i++) { + c = serverInfo.charAt(i); + + if (!Character.isDigit(c)) { + break; + } + + duplicatesBuf.append(c); + } + + duplicatesCount = Long.parseLong(duplicatesBuf.toString()); + + return recordsCount - duplicatesCount; + } + + /** + * getResultSet returns the current result as a ResultSet. It should only be + * called once per result. + * + * @return the current result set; null if there are no more + * + * @exception SQLException + * if a database access error occurs (why?) + */ + public java.sql.ResultSet getResultSet() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return ((this.results != null) && this.results.hasRows()) ? (java.sql.ResultSet) this.results : null; + } + } + + /** + * JDBC 2.0 Determine the result set concurrency. + * + * @return CONCUR_UPDATABLE or CONCUR_READONLY + * + * @throws SQLException + * if an error occurs + */ + public int getResultSetConcurrency() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.resultSetConcurrency; + } + } + + /** + * @see StatementImpl#getResultSetHoldability() + */ + public int getResultSetHoldability() throws SQLException { + return java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT; + } + + protected ResultSetInternalMethods getResultSetInternal() { + try { + synchronized (checkClosed().getConnectionMutex()) { + return this.results; + } + } catch (StatementIsClosedException e) { + return this.results; // you end up with the same thing as before, you'll get exception when actually trying to use it + } + } + + /** + * JDBC 2.0 Determine the result set type. + * + * @return the ResultSet type (TYPE_FORWARD_ONLY, SCROLL_SENSITIVE or SCROLL_INSENSITIVE) + * + * @throws SQLException + * if an error occurs. + */ + public int getResultSetType() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.query.getResultType().getIntValue(); + } + } + + /** + * getUpdateCount returns the current result as an update count, if the + * result is a ResultSet or there are no more results, -1 is returned. It + * should only be called once per result. + * + * @return the current result as an update count. + * + * @exception SQLException + * if a database access error occurs + */ + public int getUpdateCount() throws SQLException { + return Util.truncateAndConvertToInt(getLargeUpdateCount()); + } + + /** + * The first warning reported by calls on this Statement is returned. A + * Statement's execute methods clear its java.sql.SQLWarning chain. + * Subsequent Statement warnings will be chained to this + * java.sql.SQLWarning. + * + *

+ * The Warning chain is automatically cleared each time a statement is (re)executed. + *

+ * + *

+ * Note: If you are processing a ResultSet then any warnings associated with ResultSet reads will be chained on the ResultSet object. + *

+ * + * @return the first java.sql.SQLWarning or null + * + * @exception SQLException + * if a database access error occurs + */ + public java.sql.SQLWarning getWarnings() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + + if (isClearWarningsCalled()) { + return null; + } + + SQLWarning pendingWarningsFromServer = this.session.getProtocol().convertShowWarningsToSQLWarnings(0, false); + + if (this.warningChain != null) { + this.warningChain.setNextWarning(pendingWarningsFromServer); + } else { + this.warningChain = pendingWarningsFromServer; + } + + return this.warningChain; + } + } + + /** + * Closes this statement, and frees resources. + * + * @param calledExplicitly + * was this called from close()? + * + * @throws SQLException + * if an error occurs + */ + protected void realClose(boolean calledExplicitly, boolean closeOpenResults) throws SQLException { + JdbcConnection locallyScopedConn = this.connection; + + if (locallyScopedConn == null || this.isClosed) { + return; // already closed + } + + // do it ASAP to reduce the chance of calling this method concurrently from ConnectionImpl.closeAllOpenStatements() + if (!this.dontTrackOpenResources.getValue()) { + locallyScopedConn.unregisterStatement(this); + } + + if (this.useUsageAdvisor) { + if (!calledExplicitly) { + String message = Messages.getString("Statement.63") + Messages.getString("Statement.64"); + + this.query.getEventSink().consumeEvent(new ProfilerEventImpl(ProfilerEvent.TYPE_WARN, "", getCurrentCatalog(), this.session.getThreadId(), + this.getId(), -1, System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, message)); + } + } + + if (closeOpenResults) { + closeOpenResults = !(this.holdResultsOpenOverClose || this.dontTrackOpenResources.getValue()); + } + + if (closeOpenResults) { + if (this.results != null) { + + try { + this.results.close(); + } catch (Exception ex) { + } + } + + if (this.generatedKeysResults != null) { + + try { + this.generatedKeysResults.close(); + } catch (Exception ex) { + } + } + + closeAllOpenResults(); + } + + this.isClosed = true; + + closeQuery(); + + this.results = null; + this.generatedKeysResults = null; + this.connection = null; + this.session = null; + this.warningChain = null; + this.openResults = null; + this.batchedGeneratedKeys = null; + this.pingTarget = null; + this.resultSetFactory = null; + } + + /** + * setCursorName defines the SQL cursor name that will be used by subsequent + * execute methods. This name can then be used in SQL positioned + * update/delete statements to identify the current row in the ResultSet + * generated by this statement. If a database doesn't support positioned + * update/delete, this method is a no-op. + * + *

+ * Note: This MySQL driver does not support cursors. + *

+ * + * @param name + * the new cursor name + * + * @exception SQLException + * if a database access error occurs + */ + public void setCursorName(String name) throws SQLException { + // No-op + } + + /** + * If escape scanning is on (the default), the driver will do escape + * substitution before sending the SQL to the database. + * + * @param enable + * true to enable; false to disable + * + * @exception SQLException + * if a database access error occurs + */ + public void setEscapeProcessing(boolean enable) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.doEscapeProcessing = enable; + } + } + + /** + * JDBC 2.0 Give a hint as to the direction in which the rows in a result + * set will be processed. The hint applies only to result sets created using + * this Statement object. The default value is ResultSet.FETCH_FORWARD. + * + * @param direction + * the initial direction for processing rows + * + * @exception SQLException + * if a database-access error occurs or direction is not one + * of ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, or + * ResultSet.FETCH_UNKNOWN + */ + public void setFetchDirection(int direction) throws SQLException { + switch (direction) { + case java.sql.ResultSet.FETCH_FORWARD: + case java.sql.ResultSet.FETCH_REVERSE: + case java.sql.ResultSet.FETCH_UNKNOWN: + break; + + default: + throw SQLError.createSQLException(Messages.getString("Statement.5"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that should + * be fetched from the database when more rows are needed. The number of + * rows specified only affects result sets created using this statement. If + * the value specified is zero, then the hint is ignored. The default value + * is zero. + * + * @param rows + * the number of rows to fetch + * + * @exception SQLException + * if a database-access error occurs, or the condition 0 + * <= rows <= this.getMaxRows() is not satisfied. + */ + public void setFetchSize(int rows) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (((rows < 0) && (rows != Integer.MIN_VALUE)) || ((this.maxRows > 0) && (rows > this.getMaxRows()))) { + throw SQLError.createSQLException(Messages.getString("Statement.7"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + this.query.setResultFetchSize(rows); + } + } + + public void setHoldResultsOpenOverClose(boolean holdResultsOpenOverClose) { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.holdResultsOpenOverClose = holdResultsOpenOverClose; + } + } catch (StatementIsClosedException e) { + // FIXME: can't break interface at this point + } + } + + /** + * Sets the maxFieldSize + * + * @param max + * the new max column size limit; zero means unlimited + * + * @exception SQLException + * if size exceeds buffer size + */ + public void setMaxFieldSize(int max) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (max < 0) { + throw SQLError.createSQLException(Messages.getString("Statement.11"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + int maxBuf = this.maxAllowedPacket.getValue(); + + if (max > maxBuf) { + throw SQLError.createSQLException(Messages.getString("Statement.13", new Object[] { Long.valueOf(maxBuf) }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + this.maxFieldSize = max; + } + } + + /** + * Set the maximum number of rows + * + * @param max + * the new max rows limit; zero means unlimited + * + * @exception SQLException + * if a database access error occurs + * + * @see getMaxRows + */ + public void setMaxRows(int max) throws SQLException { + setLargeMaxRows(max); + } + + /** + * Sets the queryTimeout limit + * + * @param seconds + * - + * the new query timeout limit in seconds + * + * @exception SQLException + * if a database access error occurs + */ + public void setQueryTimeout(int seconds) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (seconds < 0) { + throw SQLError.createSQLException(Messages.getString("Statement.21"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + setTimeoutInMillis(seconds * 1000); + } + } + + /** + * Sets the concurrency for result sets generated by this statement + * + * @param concurrencyFlag + * @throws SQLException + */ + void setResultSetConcurrency(int concurrencyFlag) throws SQLException { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.resultSetConcurrency = concurrencyFlag; + // updating resultset factory because concurrency is cached there + this.resultSetFactory = new ResultSetFactory(this.connection, this); + } + } catch (StatementIsClosedException e) { + // FIXME: Can't break interface atm, we'll get the exception later when you try and do something useful with a closed statement... + } + } + + /** + * Sets the result set type for result sets generated by this statement + * + * @param typeFlag + * @throws SQLException + */ + void setResultSetType(Resultset.Type typeFlag) throws SQLException { + try { + synchronized (checkClosed().getConnectionMutex()) { + this.query.setResultType(typeFlag); + // updating resultset factory because type is cached there + this.resultSetFactory = new ResultSetFactory(this.connection, this); + } + } catch (StatementIsClosedException e) { + // FIXME: Can't break interface atm, we'll get the exception later when you try and do something useful with a closed statement... + } + } + + void setResultSetType(int typeFlag) throws SQLException { + Type.fromValue(typeFlag, Type.FORWARD_ONLY); + } + + protected void getBatchedGeneratedKeys(java.sql.Statement batchedStatement) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.retrieveGeneratedKeys) { + java.sql.ResultSet rs = null; + + try { + rs = batchedStatement.getGeneratedKeys(); + + while (rs.next()) { + this.batchedGeneratedKeys.add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor())); + } + } finally { + if (rs != null) { + rs.close(); + } + } + } + } + } + + protected void getBatchedGeneratedKeys(int maxKeys) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.retrieveGeneratedKeys) { + java.sql.ResultSet rs = null; + + try { + rs = maxKeys == 0 ? getGeneratedKeysInternal() : getGeneratedKeysInternal(maxKeys); + while (rs.next()) { + this.batchedGeneratedKeys.add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor())); + } + } finally { + this.isImplicitlyClosingResults = true; + try { + if (rs != null) { + rs.close(); + } + } finally { + this.isImplicitlyClosingResults = false; + } + } + } + } + } + + private boolean useServerFetch() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.session.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_useCursorFetch).getValue() + && this.query.getResultFetchSize() > 0 && this.query.getResultType() == Type.FORWARD_ONLY; + } + } + + public boolean isClosed() throws SQLException { + JdbcConnection locallyScopedConn = this.connection; + if (locallyScopedConn == null) { + return true; + } + synchronized (locallyScopedConn.getConnectionMutex()) { + return this.isClosed; + } + } + + private boolean isPoolable = true; + + public boolean isPoolable() throws SQLException { + return this.isPoolable; + } + + public void setPoolable(boolean poolable) throws SQLException { + this.isPoolable = poolable; + } + + /** + * @see java.sql.Wrapper#isWrapperFor(Class) + */ + public boolean isWrapperFor(Class iface) throws SQLException { + checkClosed(); + + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + /** + * @see java.sql.Wrapper#unwrap(Class) + */ + public T unwrap(Class iface) throws SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("Common.UnableToUnwrap", new Object[] { iface.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + protected static int findStartOfStatement(String sql) { + int statementStartPos = 0; + + if (StringUtils.startsWithIgnoreCaseAndWs(sql, "/*")) { + statementStartPos = sql.indexOf("*/"); + + if (statementStartPos == -1) { + statementStartPos = 0; + } else { + statementStartPos += 2; + } + } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "--") || StringUtils.startsWithIgnoreCaseAndWs(sql, "#")) { + statementStartPos = sql.indexOf('\n'); + + if (statementStartPos == -1) { + statementStartPos = sql.indexOf('\r'); + + if (statementStartPos == -1) { + statementStartPos = 0; + } + } + } + + return statementStartPos; + } + + public InputStream getLocalInfileInputStream() { + return this.session.getLocalInfileInputStream(); + } + + public void setLocalInfileInputStream(InputStream stream) { + this.session.setLocalInfileInputStream(stream); + } + + public void setPingTarget(PingTarget pingTarget) { + this.pingTarget = pingTarget; + } + + public ExceptionInterceptor getExceptionInterceptor() { + return this.exceptionInterceptor; + } + + protected boolean containsOnDuplicateKeyInString(String sql) { + return ParseInfo.getOnDuplicateKeyLocation(sql, this.dontCheckOnDuplicateKeyUpdateInSQL, this.rewriteBatchedStatements.getValue(), + this.session.getServerSession().isNoBackslashEscapesSet()) != -1; + } + + private boolean closeOnCompletion = false; + + public void closeOnCompletion() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + this.closeOnCompletion = true; + } + } + + public boolean isCloseOnCompletion() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + return this.closeOnCompletion; + } + } + + /** + * JDBC 4.2 + * Same as {@link #executeBatch()} but returns long[] instead of int[]. + */ + public long[] executeLargeBatch() throws SQLException { + return executeBatchInternal(); + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String)} but returns long instead of int. + */ + public long executeLargeUpdate(String sql) throws SQLException { + return executeUpdateInternal(sql, false, false); + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String, int)} but returns long instead of int. + */ + public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + return executeUpdateInternal(sql, false, autoGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS); + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String, int[])} but returns long instead of int. + */ + public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { + return executeUpdateInternal(sql, false, columnIndexes != null && columnIndexes.length > 0); + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String, String[])} but returns long instead of int. + */ + public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { + return executeUpdateInternal(sql, false, columnNames != null && columnNames.length > 0); + } + + /** + * JDBC 4.2 + * Same as {@link #getMaxRows()} but returns long instead of int. + */ + public long getLargeMaxRows() throws SQLException { + // Max rows is limited by MySQLDefs.MAX_ROWS anyway... + return getMaxRows(); + } + + /** + * JDBC 4.2 + * Same as {@link #getUpdateCount()} but returns long instead of int; + */ + public long getLargeUpdateCount() throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if (this.results == null) { + return -1; + } + + if (this.results.hasRows()) { + return -1; + } + + return this.results.getUpdateCount(); + } + } + + /** + * JDBC 4.2 + * Same as {@link #setMaxRows(int)} but accepts a long value instead of an int. + */ + public void setLargeMaxRows(long max) throws SQLException { + synchronized (checkClosed().getConnectionMutex()) { + if ((max > MAX_ROWS) || (max < 0)) { + throw SQLError.createSQLException(Messages.getString("Statement.15") + max + " > " + MAX_ROWS + ".", + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + + if (max == 0) { + max = -1; + } + + this.maxRows = (int) max; + } + } + + public String getCurrentCatalog() { + return this.query.getCurrentCatalog(); + } + + public long getServerStatementId() { + throw ExceptionFactory.createException(CJOperationNotSupportedException.class, Messages.getString("Statement.65")); + } + + @Override + @SuppressWarnings("unchecked") + public ProtocolEntityFactory getResultSetFactory() { + return (ProtocolEntityFactory) this.resultSetFactory; + } + + @Override + public int getId() { + return this.query.getId(); + } + + @Override + public void setCancelStatus(CancelStatus cs) { + this.query.setCancelStatus(cs); + } + + @Override + public void checkCancelTimeout() { + this.query.checkCancelTimeout(); + } + + @Override + public Session getSession() { + return this.session; + } + + @Override + public Object getCancelTimeoutMutex() { + return this.query.getCancelTimeoutMutex(); + } + + @Override + public void closeQuery() { + if (this.query != null) { + this.query.closeQuery(); + } + } + + @Override + public int getResultFetchSize() { + return this.query.getResultFetchSize(); + } + + @Override + public void setResultFetchSize(int fetchSize) { + this.query.setResultFetchSize(fetchSize); + } + + @Override + public Resultset.Type getResultType() { + return this.query.getResultType(); + } + + @Override + public void setResultType(Resultset.Type resultSetType) { + this.query.setResultType(resultSetType); + } + + @Override + public int getTimeoutInMillis() { + return this.query.getTimeoutInMillis(); + } + + @Override + public void setTimeoutInMillis(int timeoutInMillis) { + this.query.setTimeoutInMillis(timeoutInMillis); + } + + @Override + public ProfilerEventHandler getEventSink() { + return this.query.getEventSink(); + } + + @Override + public void setEventSink(ProfilerEventHandler eventSink) { + this.query.setEventSink(eventSink); + } + + @Override + public AtomicBoolean getStatementExecuting() { + return this.query.getStatementExecuting(); + } + + @Override + public void setCurrentCatalog(String currentCatalog) { + this.query.setCurrentCatalog(currentCatalog); + } + + @Override + public boolean isClearWarningsCalled() { + return this.query.isClearWarningsCalled(); + } + + @Override + public void setClearWarningsCalled(boolean clearWarningsCalled) { + this.query.setClearWarningsCalled(clearWarningsCalled); + } + + public Query getQuery() { + return this.query; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/StatementWrapper.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/StatementWrapper.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/StatementWrapper.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,871 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.lang.reflect.Proxy; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; +import java.util.HashMap; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; + +/** + * Wraps statements so that errors can be reported correctly to ConnectionEventListeners. + */ +public class StatementWrapper extends WrapperBase implements Statement { + + protected static StatementWrapper getInstance(ConnectionWrapper c, MysqlPooledConnection conn, Statement toWrap) throws SQLException { + return new StatementWrapper(c, conn, toWrap); + } + + protected Statement wrappedStmt; + + protected ConnectionWrapper wrappedConn; + + public StatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn, Statement toWrap) { + super(conn); + this.wrappedStmt = toWrap; + this.wrappedConn = c; + } + + public Connection getConnection() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedConn; + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; // we actually never get here, but the compiler can't figure that out + } + + public void setCursorName(String name) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setCursorName(name); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setEscapeProcessing(boolean enable) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setEscapeProcessing(enable); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void setFetchDirection(int direction) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setFetchDirection(direction); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public int getFetchDirection() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getFetchDirection(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return ResultSet.FETCH_FORWARD; // we actually never get here, but the compiler can't figure that out + } + + public void setFetchSize(int rows) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setFetchSize(rows); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public int getFetchSize() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getFetchSize(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; // we actually never get here, but the compiler can't figure that out + } + + public ResultSet getGeneratedKeys() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getGeneratedKeys(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; // we actually never get here, but the compiler can't figure that out + } + + public void setMaxFieldSize(int max) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setMaxFieldSize(max); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public int getMaxFieldSize() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getMaxFieldSize(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; // we actually never get here, but the compiler can't figure that out + } + + public void setMaxRows(int max) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setMaxRows(max); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public int getMaxRows() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getMaxRows(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; // we actually never get here, but the compiler can't figure that out + } + + public boolean getMoreResults() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getMoreResults(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; + } + + public boolean getMoreResults(int current) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getMoreResults(current); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; + } + + public void setQueryTimeout(int seconds) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setQueryTimeout(seconds); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public int getQueryTimeout() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getQueryTimeout(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public ResultSet getResultSet() throws SQLException { + try { + if (this.wrappedStmt != null) { + ResultSet rs = this.wrappedStmt.getResultSet(); + + if (rs != null) { + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).setWrapperStatement(this); + } + return rs; + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public int getResultSetConcurrency() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getResultSetConcurrency(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; + } + + public int getResultSetHoldability() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getResultSetHoldability(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return Statement.CLOSE_CURRENT_RESULT; + } + + public int getResultSetType() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getResultSetType(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return ResultSet.TYPE_FORWARD_ONLY; + } + + public int getUpdateCount() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getUpdateCount(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; + } + + public SQLWarning getWarnings() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.getWarnings(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; + } + + public void addBatch(String sql) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.addBatch(sql); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void cancel() throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.cancel(); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void clearBatch() throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.clearBatch(); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void clearWarnings() throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.clearWarnings(); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public void close() throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.close(); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } finally { + this.wrappedStmt = null; + this.pooledConnection = null; + this.unwrappedInterfaces = null; + } + } + + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.execute(sql, autoGeneratedKeys); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // we actually never get here, but the compiler can't figure that out + } + + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.execute(sql, columnIndexes); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // we actually never get here, but the compiler can't figure that out + } + + public boolean execute(String sql, String[] columnNames) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.execute(sql, columnNames); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // we actually never get here, but the compiler can't figure that out + } + + public boolean execute(String sql) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.execute(sql); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // we actually never get here, but the compiler can't figure that out + } + + public int[] executeBatch() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.executeBatch(); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; // we actually never get here, but the compiler can't figure that out + } + + public ResultSet executeQuery(String sql) throws SQLException { + ResultSet rs = null; + try { + if (this.wrappedStmt == null) { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + + rs = this.wrappedStmt.executeQuery(sql); + ((com.mysql.cj.jdbc.result.ResultSetInternalMethods) rs).setWrapperStatement(this); + + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return rs; + } + + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.executeUpdate(sql, autoGeneratedKeys); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.executeUpdate(sql, columnIndexes); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.executeUpdate(sql, columnNames); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + public int executeUpdate(String sql) throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.executeUpdate(sql); + } + + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + public void enableStreamingResults() throws SQLException { + try { + if (this.wrappedStmt != null) { + ((com.mysql.cj.jdbc.JdbcStatement) this.wrappedStmt).enableStreamingResults(); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + /** + * Returns an object that implements the given interface to allow access to + * non-standard methods, or standard methods not exposed by the proxy. The + * result may be either the object found to implement the interface or a + * proxy for that object. If the receiver implements the interface then that + * is the object. If the receiver is a wrapper and the wrapped object + * implements the interface then that is the object. Otherwise the object is + * the result of calling unwrap recursively on the wrapped + * object. If the receiver is not a wrapper and does not implement the + * interface, then an SQLException is thrown. + * + * @param iface + * A Class defining an interface that the result must implement. + * @return an object that implements the interface. May be a proxy for the + * actual implementing object. + * @throws java.sql.SQLException + * If no object found that implements the interface + * @since 1.6 + */ + public synchronized T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + if ("java.sql.Statement".equals(iface.getName()) || "java.sql.Wrapper.class".equals(iface.getName())) { + return iface.cast(this); + } + + if (this.unwrappedInterfaces == null) { + this.unwrappedInterfaces = new HashMap<>(); + } + + Object cachedUnwrapped = this.unwrappedInterfaces.get(iface); + + if (cachedUnwrapped == null) { + cachedUnwrapped = Proxy.newProxyInstance(this.wrappedStmt.getClass().getClassLoader(), new Class[] { iface }, + new ConnectionErrorFiringInvocationHandler(this.wrappedStmt)); + this.unwrappedInterfaces.put(iface, cachedUnwrapped); + } + + return iface.cast(cachedUnwrapped); + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("Common.UnableToUnwrap", new Object[] { iface.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } + + /** + * Returns true if this either implements the interface argument or is + * directly or indirectly a wrapper for an object that does. Returns false + * otherwise. If this implements the interface then return true, else if + * this is a wrapper then return the result of recursively calling isWrapperFor on the wrapped object. If this does not + * implement the interface and is not a wrapper, return false. This method + * should be implemented as a low-cost operation compared to unwrap so that callers can use this method to avoid + * expensive unwrap calls that may fail. If this method + * returns true then calling unwrap with the same argument + * should succeed. + * + * @param interfaces + * a Class defining an interface. + * @return true if this implements the interface or directly or indirectly + * wraps an object that does. + * @throws java.sql.SQLException + * if an error occurs while determining whether this is a + * wrapper for an object with the given interface. + * @since 1.6 + */ + public boolean isWrapperFor(Class iface) throws SQLException { + + boolean isInstance = iface.isInstance(this); + + if (isInstance) { + return true; + } + + String interfaceClassName = iface.getName(); + + return (interfaceClassName.equals("com.mysql.cj.jdbc.Statement") || interfaceClassName.equals("java.sql.Statement") + || interfaceClassName.equals("java.sql.Wrapper")); // TODO check other interfaces + } + + public boolean isClosed() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.isClosed(); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // We never get here, compiler can't tell + } + + public void setPoolable(boolean poolable) throws SQLException { + try { + if (this.wrappedStmt != null) { + this.wrappedStmt.setPoolable(poolable); + } else { + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } + + public boolean isPoolable() throws SQLException { + try { + if (this.wrappedStmt != null) { + return this.wrappedStmt.isPoolable(); + } + throw SQLError.createSQLException(Messages.getString("Statement.AlreadyClosed"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, + this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return false; // We never get here, compiler can't tell + } + + public void closeOnCompletion() throws SQLException { + } + + public boolean isCloseOnCompletion() throws SQLException { + return false; + } + + /** + * JDBC 4.2 + * Same as {@link #executeBatch()} but returns long[] instead of int[]. + */ + public long[] executeLargeBatch() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).executeLargeBatch(); + } + + throw SQLError.createSQLException("Statement already closed", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return null; // we actually never get here, but the compiler can't figure that out + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String)} but returns long instead of int. + */ + public long executeLargeUpdate(String sql) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).executeLargeUpdate(sql); + } + + throw SQLError.createSQLException("Statement already closed", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String, int)} but returns long instead of int. + */ + public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).executeLargeUpdate(sql, autoGeneratedKeys); + } + + throw SQLError.createSQLException("Statement already closed", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String, int[])} but returns long instead of int. + */ + public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).executeLargeUpdate(sql, columnIndexes); + } + + throw SQLError.createSQLException("Statement already closed", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + /** + * JDBC 4.2 + * Same as {@link #executeUpdate(String, String[])} but returns long instead of int. + */ + public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).executeLargeUpdate(sql, columnNames); + } + + throw SQLError.createSQLException("Statement already closed", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; // we actually never get here, but the compiler can't figure that out + } + + /** + * JDBC 4.2 + * Same as {@link #getMaxRows()} but returns long instead of int. + */ + public long getLargeMaxRows() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).getLargeMaxRows(); + } + + throw SQLError.createSQLException("Statement already closed", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return 0; // we actually never get here, but the compiler can't figure that out + } + + /** + * JDBC 4.2 + * Same as {@link #getUpdateCount()} but returns long instead of int; + */ + public long getLargeUpdateCount() throws SQLException { + try { + if (this.wrappedStmt != null) { + return ((StatementImpl) this.wrappedStmt).getLargeUpdateCount(); + } + + throw SQLError.createSQLException("Statement already closed", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + + return -1; + } + + /** + * JDBC 4.2 + * Same as {@link #setMaxRows(int)} but accepts a long value instead of an int. + */ + public void setLargeMaxRows(long max) throws SQLException { + try { + if (this.wrappedStmt != null) { + ((StatementImpl) this.wrappedStmt).setLargeMaxRows(max); + } else { + throw SQLError.createSQLException("Statement already closed", MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); + } + } catch (SQLException sqlEx) { + checkAndFireConnectionError(sqlEx); + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/SuspendableXAConnection.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/SuspendableXAConnection.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/SuspendableXAConnection.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import javax.sql.XAConnection; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import com.mysql.cj.conf.PropertyDefinitions; + +public class SuspendableXAConnection extends MysqlPooledConnection implements XAConnection, XAResource { + + protected static SuspendableXAConnection getInstance(JdbcConnection mysqlConnection) throws SQLException { + return new SuspendableXAConnection(mysqlConnection); + } + + public SuspendableXAConnection(JdbcConnection connection) { + super(connection); + this.underlyingConnection = connection; + } + + private static final Map XIDS_TO_PHYSICAL_CONNECTIONS = new HashMap<>(); + + private Xid currentXid; + + private XAConnection currentXAConnection; + private XAResource currentXAResource; + + private JdbcConnection underlyingConnection; + + private static synchronized XAConnection findConnectionForXid(JdbcConnection connectionToWrap, Xid xid) throws SQLException { + // TODO: check for same GTRID, but different BQUALs...MySQL doesn't allow this yet + + // Note, we don't need to check for XIDs here, because MySQL itself will complain with a XAER_NOTA if need be. + + XAConnection conn = XIDS_TO_PHYSICAL_CONNECTIONS.get(xid); + + if (conn == null) { + conn = new MysqlXAConnection(connectionToWrap, + connectionToWrap.getPropertySet().getBooleanReadableProperty(PropertyDefinitions.PNAME_logXaCommands).getValue()); + XIDS_TO_PHYSICAL_CONNECTIONS.put(xid, conn); + } + + return conn; + } + + private static synchronized void removeXAConnectionMapping(Xid xid) { + XIDS_TO_PHYSICAL_CONNECTIONS.remove(xid); + } + + private synchronized void switchToXid(Xid xid) throws XAException { + if (xid == null) { + throw new XAException(); + } + + try { + if (!xid.equals(this.currentXid)) { + XAConnection toSwitchTo = findConnectionForXid(this.underlyingConnection, xid); + this.currentXAConnection = toSwitchTo; + this.currentXid = xid; + this.currentXAResource = toSwitchTo.getXAResource(); + } + } catch (SQLException sqlEx) { + throw new XAException(); + } + } + + public XAResource getXAResource() throws SQLException { + return this; + } + + public void commit(Xid xid, boolean arg1) throws XAException { + switchToXid(xid); + this.currentXAResource.commit(xid, arg1); + removeXAConnectionMapping(xid); + } + + public void end(Xid xid, int arg1) throws XAException { + switchToXid(xid); + this.currentXAResource.end(xid, arg1); + } + + public void forget(Xid xid) throws XAException { + switchToXid(xid); + this.currentXAResource.forget(xid); + // remove? + removeXAConnectionMapping(xid); + } + + public int getTransactionTimeout() throws XAException { + return 0; + } + + public boolean isSameRM(XAResource xaRes) throws XAException { + return xaRes == this; + } + + public int prepare(Xid xid) throws XAException { + switchToXid(xid); + return this.currentXAResource.prepare(xid); + } + + public Xid[] recover(int flag) throws XAException { + return MysqlXAConnection.recover(this.underlyingConnection, flag); + } + + public void rollback(Xid xid) throws XAException { + switchToXid(xid); + this.currentXAResource.rollback(xid); + removeXAConnectionMapping(xid); + } + + public boolean setTransactionTimeout(int arg0) throws XAException { + return false; + } + + public void start(Xid xid, int arg1) throws XAException { + switchToXid(xid); + + if (arg1 != XAResource.TMJOIN) { + this.currentXAResource.start(xid, arg1); + + return; + } + + // + // Emulate join, by using resume on the same physical connection + // + + this.currentXAResource.start(xid, XAResource.TMRESUME); + } + + @Override + public synchronized java.sql.Connection getConnection() throws SQLException { + if (this.currentXAConnection == null) { + return getConnection(false, true); + } + + return this.currentXAConnection.getConnection(); + } + + @Override + public void close() throws SQLException { + if (this.currentXAConnection == null) { + super.close(); + } else { + removeXAConnectionMapping(this.currentXid); + this.currentXAConnection.close(); + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/WrapperBase.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/WrapperBase.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/WrapperBase.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.SQLException; +import java.util.Map; + +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.util.Util; + +/** + * Base class for all wrapped instances created by LogicalHandle + */ +abstract class WrapperBase { + protected MysqlPooledConnection pooledConnection; + + /** + * Fires connection error event if required, before re-throwing exception + * + * @param sqlEx + * the SQLException that has occurred + * @throws SQLException + * (rethrown) + */ + protected void checkAndFireConnectionError(SQLException sqlEx) throws SQLException { + if (this.pooledConnection != null) { + if (MysqlErrorNumbers.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlEx.getSQLState())) { + this.pooledConnection.callConnectionEventListeners(MysqlPooledConnection.CONNECTION_ERROR_EVENT, sqlEx); + } + } + + throw sqlEx; + } + + protected Map, Object> unwrappedInterfaces = null; + protected ExceptionInterceptor exceptionInterceptor; + + protected WrapperBase(MysqlPooledConnection pooledConnection) { + this.pooledConnection = pooledConnection; + this.exceptionInterceptor = this.pooledConnection.getExceptionInterceptor(); + } + + protected class ConnectionErrorFiringInvocationHandler implements InvocationHandler { + Object invokeOn = null; + + public ConnectionErrorFiringInvocationHandler(Object toInvokeOn) { + this.invokeOn = toInvokeOn; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("equals".equals(method.getName())) { + // Let args[0] "unwrap" to its InvocationHandler if it is a proxy. + return args[0].equals(this); + } + + Object result = null; + + try { + result = method.invoke(this.invokeOn, args); + + if (result != null) { + result = proxyIfInterfaceIsJdbc(result, result.getClass()); + } + } catch (InvocationTargetException e) { + if (e.getTargetException() instanceof SQLException) { + checkAndFireConnectionError((SQLException) e.getTargetException()); + } else { + throw e; + } + } + + return result; + } + + /** + * Recursively checks for interfaces on the given object to determine + * if it implements a java.sql interface, and if so, proxies the + * instance so that we can catch and fire SQL errors. + * + * @param toProxy + * @param clazz + */ + private Object proxyIfInterfaceIsJdbc(Object toProxy, Class clazz) { + Class[] interfaces = clazz.getInterfaces(); + + for (Class iclass : interfaces) { + String packageName = Util.getPackageName(iclass); + + if ("java.sql".equals(packageName) || "javax.sql".equals(packageName)) { + return Proxy.newProxyInstance(toProxy.getClass().getClassLoader(), interfaces, new ConnectionErrorFiringInvocationHandler(toProxy)); + } + + return proxyIfInterfaceIsJdbc(toProxy, iclass); + } + + return toProxy; + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/admin/MiniAdmin.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/admin/MiniAdmin.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/admin/MiniAdmin.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.admin; + +import java.sql.SQLException; +import java.util.Properties; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.Driver; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.exceptions.SQLError; + +/** + * Utility functions for admin functionality from Java. + */ +public class MiniAdmin { + private JdbcConnection conn; + + /** + * Create a new MiniAdmin using the given connection + * + * @param conn + * the existing connection to use. + * + * @throws SQLException + * if an error occurs + */ + public MiniAdmin(java.sql.Connection conn) throws SQLException { + if (conn == null) { + throw SQLError.createSQLException(Messages.getString("MiniAdmin.0"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, null); + } + + if (!(conn instanceof JdbcConnection)) { + throw SQLError.createSQLException(Messages.getString("MiniAdmin.1"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, + ((com.mysql.cj.jdbc.ConnectionImpl) conn).getExceptionInterceptor()); + } + + this.conn = (JdbcConnection) conn; + } + + /** + * Create a new MiniAdmin, connecting using the given JDBC URL. + * + * @param jdbcUrl + * the JDBC URL to use + * + * @throws SQLException + * if an error occurs + */ + public MiniAdmin(String jdbcUrl) throws SQLException { + this(jdbcUrl, new Properties()); + } + + /** + * Create a new MiniAdmin, connecting using the given JDBC URL and + * properties + * + * @param jdbcUrl + * the JDBC URL to use + * @param props + * the properties to use when connecting + * + * @throws SQLException + * if an error occurs + */ + public MiniAdmin(String jdbcUrl, Properties props) throws SQLException { + this.conn = (JdbcConnection) (new Driver().connect(jdbcUrl, props)); + } + + /** + * Shuts down the MySQL server at the other end of the connection that this + * MiniAdmin was created from/for. + * + * @throws SQLException + * if an error occurs + */ + public void shutdown() throws SQLException { + this.conn.shutdownServer(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/admin/TimezoneDump.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/admin/TimezoneDump.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/admin/TimezoneDump.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.admin; + +import java.sql.DriverManager; +import java.sql.ResultSet; + +import com.mysql.cj.util.TimeUtil; + +/** + * Dumps the timezone of the MySQL server represented by the JDBC url given on the commandline (or localhost/test if none provided). + */ +public class TimezoneDump { + private static final String DEFAULT_URL = "jdbc:mysql:///test"; + + /** + * Constructor for TimezoneDump. + */ + public TimezoneDump() { + super(); + } + + /** + * Entry point for program when called from the command line. + * + * @param args + * command-line args. Arg 1 is JDBC URL. + * @throws Exception + * if any errors occur + */ + public static void main(String[] args) throws Exception { + String jdbcUrl = DEFAULT_URL; + + if ((args.length == 1) && (args[0] != null)) { + jdbcUrl = args[0]; + } + + Class.forName("com.mysql.cj.jdbc.Driver").newInstance(); + + ResultSet rs = null; + + try { + rs = DriverManager.getConnection(jdbcUrl).createStatement().executeQuery("SHOW VARIABLES LIKE 'timezone'"); + + while (rs.next()) { + String timezoneFromServer = rs.getString(2); + System.out.println("MySQL timezone name: " + timezoneFromServer); + + String canonicalTimezone = TimeUtil.getCanonicalTimezone(timezoneFromServer, null); + System.out.println("Java timezone name: " + canonicalTimezone); + } + } finally { + if (rs != null) { + rs.close(); + } + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/CommunicationsException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/CommunicationsException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/CommunicationsException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.exceptions; + +import java.sql.SQLRecoverableException; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.exceptions.StreamingNotifiable; +import com.mysql.cj.jdbc.JdbcConnection; + +/** + * An exception to represent communications errors with the database. + * + * Attempts to provide 'friendlier' error messages to end-users, including the last time a packet was sent to the database, + * what the client-timeout is set to, and whether the idle time has been exceeded. + */ +public class CommunicationsException extends SQLRecoverableException implements StreamingNotifiable { + + private static final long serialVersionUID = 4317904269000988676L; + + private String exceptionMessage; + + public CommunicationsException(JdbcConnection conn, long lastPacketSentTimeMs, long lastPacketReceivedTimeMs, Exception underlyingException) { + this(ExceptionFactory.createLinkFailureMessageBasedOnHeuristics(conn.getPropertySet(), conn.getSession().getServerSession(), lastPacketSentTimeMs, + lastPacketReceivedTimeMs, underlyingException), underlyingException); + } + + public CommunicationsException(String message, Throwable underlyingException) { + this.exceptionMessage = message; + + if (underlyingException != null) { + initCause(underlyingException); + } + } + + /* + * @see java.lang.Throwable#getMessage() + */ + @Override + public String getMessage() { + return this.exceptionMessage; + } + + /* + * @see java.sql.SQLException#getSQLState() + */ + @Override + public String getSQLState() { + return MysqlErrorNumbers.SQL_STATE_COMMUNICATION_LINK_FAILURE; + } + + public void setWasStreamingResults() { + // replace exception message + this.exceptionMessage = Messages.getString("CommunicationsException.ClientWasStreaming"); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/ConnectionFeatureNotAvailableException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/ConnectionFeatureNotAvailableException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/ConnectionFeatureNotAvailableException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.exceptions; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.JdbcConnection; + +/** + * Thrown when a client requests a connection-level feature that isn't available for this particular distribution of Connector/J (currently only used by code + * that is export-controlled). + */ +public class ConnectionFeatureNotAvailableException extends CommunicationsException { + + private static final long serialVersionUID = 8315412078945570018L; + + public ConnectionFeatureNotAvailableException(JdbcConnection conn, long lastPacketSentTimeMs, Exception underlyingException) { + super(conn, lastPacketSentTimeMs, 0, underlyingException); + } + + public ConnectionFeatureNotAvailableException(String message, Throwable underlyingException) { + super(message, underlyingException); + } + + @Override + public String getMessage() { + return Messages.getString("ConnectionFeatureNotAvailableException.0"); + } + + @Override + public String getSQLState() { + return MysqlErrorNumbers.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MySQLQueryInterruptedException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MySQLQueryInterruptedException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MySQLQueryInterruptedException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.exceptions; + +import java.sql.SQLNonTransientException; + +public class MySQLQueryInterruptedException extends SQLNonTransientException { + + private static final long serialVersionUID = -8714521137662613517L; + + public MySQLQueryInterruptedException() { + super(); + } + + public MySQLQueryInterruptedException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLQueryInterruptedException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLQueryInterruptedException(String reason) { + super(reason); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MySQLStatementCancelledException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MySQLStatementCancelledException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MySQLStatementCancelledException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.exceptions; + +import java.sql.SQLNonTransientException; + +import com.mysql.cj.Messages; + +public class MySQLStatementCancelledException extends SQLNonTransientException { + + static final long serialVersionUID = -8762717748377197378L; + + public MySQLStatementCancelledException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLStatementCancelledException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLStatementCancelledException(String reason) { + super(reason); + } + + public MySQLStatementCancelledException() { + super(Messages.getString("MySQLStatementCancelledException.0")); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MySQLTimeoutException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MySQLTimeoutException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MySQLTimeoutException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.exceptions; + +import java.sql.SQLTimeoutException; + +import com.mysql.cj.Messages; + +public class MySQLTimeoutException extends SQLTimeoutException { + + static final long serialVersionUID = -789621240523239939L; + + public MySQLTimeoutException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLTimeoutException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLTimeoutException(String reason) { + super(reason); + } + + public MySQLTimeoutException() { + super(Messages.getString("MySQLTimeoutException.0")); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MySQLTransactionRollbackException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MySQLTransactionRollbackException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MySQLTransactionRollbackException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.exceptions; + +import java.sql.SQLTransactionRollbackException; + +import com.mysql.cj.exceptions.DeadlockTimeoutRollbackMarker; + +public class MySQLTransactionRollbackException extends SQLTransactionRollbackException implements DeadlockTimeoutRollbackMarker { + + static final long serialVersionUID = 6034999468737899730L; + + public MySQLTransactionRollbackException(String reason, String SQLState, int vendorCode) { + super(reason, SQLState, vendorCode); + } + + public MySQLTransactionRollbackException(String reason, String SQLState) { + super(reason, SQLState); + } + + public MySQLTransactionRollbackException(String reason) { + super(reason); + } + + public MySQLTransactionRollbackException() { + super(); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MysqlDataTruncation.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MysqlDataTruncation.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/MysqlDataTruncation.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.exceptions; + +import java.sql.DataTruncation; + +/** + * MySQL wrapper for DataTruncation until the server can support sending all needed information. + */ +public class MysqlDataTruncation extends DataTruncation { + + static final long serialVersionUID = 3263928195256986226L; + + private String message; + + private int vendorErrorCode; + + /** + * Creates a new MysqlDataTruncation exception/warning. + * + * @param message + * the message from the server + * @param index + * of column or parameter + * @param parameter + * was a parameter? + * @param read + * was truncated on read? + * @param dataSize + * size requested + * @param transferSize + * size actually used + */ + public MysqlDataTruncation(String message, int index, boolean parameter, boolean read, int dataSize, int transferSize, int vendorErrorCode) { + super(index, parameter, read, dataSize, transferSize); + + this.message = message; + this.vendorErrorCode = vendorErrorCode; + } + + @Override + public int getErrorCode() { + return this.vendorErrorCode; + } + + @Override + public String getMessage() { + return super.getMessage() + ": " + this.message; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/NotUpdatable.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/NotUpdatable.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/NotUpdatable.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.exceptions; + +import java.sql.SQLException; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.MysqlErrorNumbers; + +/** + * Thrown when a result sate is not updatable + */ +public class NotUpdatable extends SQLException { + + private static final long serialVersionUID = 8084742846039782258L; + + /** + * The message to use when result set is not updatable. + * + * The same message is used in the warnings generated by Updatabale result + * set. + */ + public static final String NOT_UPDATEABLE_MESSAGE = Messages.getString("NotUpdatable.0") + Messages.getString("NotUpdatable.1") + + Messages.getString("NotUpdatable.2") + Messages.getString("NotUpdatable.3") + Messages.getString("NotUpdatable.4") + + Messages.getString("NotUpdatable.5"); + + /** + * Creates a new NotUpdatable exception. + */ + public NotUpdatable() { + this(NOT_UPDATEABLE_MESSAGE); + } + + /** + * Append the given reason to the not updatable message if the reason is not + * null. + */ + public NotUpdatable(String reason) { + super(reason + Messages.getString("NotUpdatable.1") + Messages.getString("NotUpdatable.2") + Messages.getString("NotUpdatable.3") + + Messages.getString("NotUpdatable.4") + Messages.getString("NotUpdatable.5"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/OperationNotSupportedException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/OperationNotSupportedException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/OperationNotSupportedException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.exceptions; + +import java.sql.SQLException; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.MysqlErrorNumbers; + +public class OperationNotSupportedException extends SQLException { + + static final long serialVersionUID = 474918612056813430L; + + public OperationNotSupportedException() { + super(Messages.getString("RowDataDynamic.10"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT); + } + + public OperationNotSupportedException(String message) { + super(message, MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/PacketTooBigException.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/PacketTooBigException.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/PacketTooBigException.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.exceptions; + +import java.sql.SQLException; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.MysqlErrorNumbers; + +/** + * Thrown when a packet that is too big for the server is created. + */ +public class PacketTooBigException extends SQLException { + + static final long serialVersionUID = 7248633977685452174L; + + /** + * Creates a new PacketTooBigException object. + * + * @param packetSize + * the size of the packet that was going to be sent + * @param maximumPacketSize + * the maximum size the server will accept + */ + public PacketTooBigException(long packetSize, long maximumPacketSize) { + super(Messages.getString("PacketTooBigException.0", new Object[] { packetSize, maximumPacketSize }), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR); + } + + public PacketTooBigException(String message) { + super(message, MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/SQLError.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/SQLError.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/SQLError.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.exceptions; + +import java.sql.SQLDataException; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.SQLIntegrityConstraintViolationException; +import java.sql.SQLNonTransientConnectionException; +import java.sql.SQLSyntaxErrorException; +import java.sql.SQLTransientConnectionException; + +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.util.Util; + +/** + * SQLError is a utility class that maps MySQL error codes to SQL error codes as is required by the JDBC spec. + */ +public class SQLError { + /* + * SQL State Class SQLNonTransientException Subclass 08 + * SQLNonTransientConnectionException 22 SQLDataException 23 + * SQLIntegrityConstraintViolationException N/A + * SQLInvalidAuthorizationException 42 SQLSyntaxErrorException + * + * SQL State Class SQLTransientException Subclass 08 + * SQLTransientConnectionException 40 SQLTransactionRollbackException N/A + * SQLTimeoutException + */ + + public static SQLException createSQLException(String message, String sqlState, ExceptionInterceptor interceptor) { + return createSQLException(message, sqlState, 0, interceptor); + } + + public static SQLException createSQLException(String message, ExceptionInterceptor interceptor) { + SQLException sqlEx = new SQLException(message); + + return runThroughExceptionInterceptor(interceptor, sqlEx); + } + + public static SQLException createSQLException(String message, String sqlState, Throwable cause, ExceptionInterceptor interceptor) { + SQLException sqlEx = createSQLException(message, sqlState, null); + + if (sqlEx.getCause() == null) { + if (cause != null) { + try { + sqlEx.initCause(cause); + } catch (Throwable t) { + // we're not going to muck with that here, since it's an error condition anyway! + } + } + } + // Run through the exception interceptor after setting the init cause. + return runThroughExceptionInterceptor(interceptor, sqlEx); + } + + public static SQLException createSQLException(String message, String sqlState, int vendorErrorCode, ExceptionInterceptor interceptor) { + return createSQLException(message, sqlState, vendorErrorCode, false, interceptor); + } + + public static SQLException createSQLException(String message, String sqlState, int vendorErrorCode, Throwable cause, ExceptionInterceptor interceptor) { + return createSQLException(message, sqlState, vendorErrorCode, false, cause, interceptor); + } + + public static SQLException createSQLException(String message, String sqlState, int vendorErrorCode, boolean isTransient, ExceptionInterceptor interceptor) { + return createSQLException(message, sqlState, vendorErrorCode, isTransient, null, interceptor); + } + + public static SQLException createSQLException(String message, String sqlState, int vendorErrorCode, boolean isTransient, Throwable cause, + ExceptionInterceptor interceptor) { + try { + SQLException sqlEx = null; + + if (sqlState != null) { + if (sqlState.startsWith("08")) { + if (isTransient) { + sqlEx = new SQLTransientConnectionException(message, sqlState, vendorErrorCode); + } else { + sqlEx = new SQLNonTransientConnectionException(message, sqlState, vendorErrorCode); + } + + } else if (sqlState.startsWith("22")) { + sqlEx = new SQLDataException(message, sqlState, vendorErrorCode); + + } else if (sqlState.startsWith("23")) { + sqlEx = new SQLIntegrityConstraintViolationException(message, sqlState, vendorErrorCode); + + } else if (sqlState.startsWith("42")) { + sqlEx = new SQLSyntaxErrorException(message, sqlState, vendorErrorCode); + + } else if (sqlState.startsWith("40")) { + sqlEx = new MySQLTransactionRollbackException(message, sqlState, vendorErrorCode); + + } else if (sqlState.startsWith("70100")) { + sqlEx = new MySQLQueryInterruptedException(message, sqlState, vendorErrorCode); + + } else { + sqlEx = new SQLException(message, sqlState, vendorErrorCode); + } + } else { + sqlEx = new SQLException(message, sqlState, vendorErrorCode); + } + + if (cause != null) { + try { + sqlEx.initCause(cause); + } catch (Throwable t) { + // we're not going to muck with that here, since it's an error condition anyway! + } + } + + return runThroughExceptionInterceptor(interceptor, sqlEx); + + } catch (Exception sqlEx) { + SQLException unexpectedEx = new SQLException( + "Unable to create correct SQLException class instance, error class/codes may be incorrect. Reason: " + Util.stackTraceToString(sqlEx), + MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR); + + return runThroughExceptionInterceptor(interceptor, unexpectedEx); + + } + } + + public static SQLException createCommunicationsException(JdbcConnection conn, long lastPacketSentTimeMs, long lastPacketReceivedTimeMs, + Exception underlyingException, ExceptionInterceptor interceptor) { + + SQLException exToReturn = new CommunicationsException(conn, lastPacketSentTimeMs, lastPacketReceivedTimeMs, underlyingException); + + if (underlyingException != null) { + try { + exToReturn.initCause(underlyingException); + } catch (Throwable t) { + // we're not going to muck with that here, since it's an error condition anyway! + } + } + + return runThroughExceptionInterceptor(interceptor, exToReturn); + } + + public static SQLException createCommunicationsException(String message, Throwable underlyingException, ExceptionInterceptor interceptor) { + SQLException exToReturn = null; + + exToReturn = new CommunicationsException(message, underlyingException); + + if (underlyingException != null) { + try { + exToReturn.initCause(underlyingException); + } catch (Throwable t) { + // we're not going to muck with that here, since it's an error condition anyway! + } + } + + return runThroughExceptionInterceptor(interceptor, exToReturn); + } + + public static NotUpdatable notUpdatable() { + return new NotUpdatable(); + } + + /** + * Run exception through an ExceptionInterceptor chain. + * + * @param exInterceptor + * @param sqlEx + * @param conn + * @return + */ + private static SQLException runThroughExceptionInterceptor(ExceptionInterceptor exInterceptor, SQLException sqlEx) { + if (exInterceptor != null) { + SQLException interceptedEx = (SQLException) exInterceptor.interceptException(sqlEx); + + if (interceptedEx != null) { + return interceptedEx; + } + } + return sqlEx; + } + + /** + * Create a BatchUpdateException taking in consideration the JDBC version in use. For JDBC version prior to 4.2 the updates count array has int elements + * while JDBC 4.2 and beyond uses long values. + * + * @param underlyingEx + * @param updateCounts + * @param interceptor + */ + public static SQLException createBatchUpdateException(SQLException underlyingEx, long[] updateCounts, ExceptionInterceptor interceptor) + throws SQLException { + SQLException newEx = (SQLException) Util.getInstance("java.sql.BatchUpdateException", + new Class[] { String.class, String.class, int.class, long[].class, Throwable.class }, + new Object[] { underlyingEx.getMessage(), underlyingEx.getSQLState(), underlyingEx.getErrorCode(), updateCounts, underlyingEx }, interceptor); + return runThroughExceptionInterceptor(interceptor, newEx); + } + + /** + * Create a SQLFeatureNotSupportedException or a NotImplemented exception according to the JDBC version in use. + */ + public static SQLException createSQLFeatureNotSupportedException() { + return new SQLFeatureNotSupportedException(); + } + + /** + * Create a SQLFeatureNotSupportedException or a NotImplemented exception according to the JDBC version in use. + * + * @param message + * @param sqlState + * @param interceptor + */ + public static SQLException createSQLFeatureNotSupportedException(String message, String sqlState, ExceptionInterceptor interceptor) throws SQLException { + SQLException newEx = new SQLFeatureNotSupportedException(message, sqlState); + return runThroughExceptionInterceptor(interceptor, newEx); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/SQLExceptionsMapping.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/SQLExceptionsMapping.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/exceptions/SQLExceptionsMapping.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.exceptions; + +import java.sql.SQLException; + +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.CJTimeoutException; +import com.mysql.cj.exceptions.ConnectionIsClosedException; +import com.mysql.cj.exceptions.DataConversionException; +import com.mysql.cj.exceptions.DataReadException; +import com.mysql.cj.exceptions.DataTruncationException; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.InvalidConnectionAttributeException; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.exceptions.NumberOutOfRange; +import com.mysql.cj.exceptions.OperationCancelledException; +import com.mysql.cj.exceptions.SSLParamsException; +import com.mysql.cj.exceptions.StatementIsClosedException; +import com.mysql.cj.exceptions.UnableToConnectException; +import com.mysql.cj.exceptions.WrongArgumentException; + +public class SQLExceptionsMapping { + + public static SQLException translateException(Throwable ex, ExceptionInterceptor interceptor) { + if (ex instanceof SQLException) { + return (SQLException) ex; + + } else if (ex.getCause() != null && ex.getCause() instanceof SQLException) { + return (SQLException) ex.getCause(); + + } else if (ex instanceof CJCommunicationsException) { + return SQLError.createCommunicationsException(ex.getMessage(), ex, interceptor); + + } else if (ex instanceof CJConnectionFeatureNotAvailableException) { + return new ConnectionFeatureNotAvailableException(ex.getMessage(), ex); + + } else if (ex instanceof SSLParamsException) { + return SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_BAD_SSL_PARAMS, 0, false, ex, interceptor); + + } else if (ex instanceof ConnectionIsClosedException) { + return SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_CONNECTION_NOT_OPEN, ex, interceptor); + + } else if (ex instanceof InvalidConnectionAttributeException) { + return SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, ex, interceptor); + + } else if (ex instanceof UnableToConnectException) { + return SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, ex, interceptor); + + } else if (ex instanceof StatementIsClosedException) { + return SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, ex, interceptor); + + } else if (ex instanceof WrongArgumentException) { + return SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, ex, interceptor); + + } else if (ex instanceof StringIndexOutOfBoundsException) { + return SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, ex, interceptor); + + } else if (ex instanceof NumberOutOfRange) { + // must come before DataReadException as it's more specific + return SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE, ex, interceptor); + + } else if (ex instanceof DataConversionException) { + // must come before DataReadException as it's more specific + return SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST, ex, interceptor); + + } else if (ex instanceof DataReadException) { + return SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, ex, interceptor); + + } else if (ex instanceof DataTruncationException) { + return new MysqlDataTruncation(((DataTruncationException) ex).getMessage(), ((DataTruncationException) ex).getIndex(), + ((DataTruncationException) ex).isParameter(), ((DataTruncationException) ex).isRead(), ((DataTruncationException) ex).getDataSize(), + ((DataTruncationException) ex).getTransferSize(), ((DataTruncationException) ex).getVendorCode()); + + } else if (ex instanceof CJPacketTooBigException) { + return new PacketTooBigException(ex.getMessage()); + + } else if (ex instanceof OperationCancelledException) { + return new MySQLStatementCancelledException(ex.getMessage()); + + } else if (ex instanceof CJTimeoutException) { + return new MySQLTimeoutException(ex.getMessage()); + + } else if (ex instanceof CJOperationNotSupportedException) { + return new OperationNotSupportedException(ex.getMessage()); + + } else if (ex instanceof UnsupportedOperationException) { + return new OperationNotSupportedException(ex.getMessage()); + + } else if (ex instanceof CJException) { + return SQLError.createSQLException(ex.getMessage(), ((CJException) ex).getSQLState(), ((CJException) ex).getVendorCode(), + ((CJException) ex).isTransient(), interceptor); + + } else { + return SQLError.createSQLException(ex.getMessage(), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR, ex, interceptor); + } + } + + public static SQLException translateException(Throwable ex) { + return translateException(ex, null); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/BalanceStrategy.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/BalanceStrategy.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/BalanceStrategy.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.lang.reflect.InvocationHandler; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +import com.mysql.cj.jdbc.JdbcConnection; + +/** + * Implement this interface to provide a new load balancing strategy for URLs of the form "jdbc:mysql:loadbalance://..", and provide the implementation class + * name as the configuration parameter "loadBalanceStrategy". + * + * The driver will not pass in a Connection instance when calling init(), but it will pass in the Properties, otherwise it acts like a normal Extension. + * + * One instance of a strategy *per* JDBC connection instance will be created. If you need singleton-like behavior, you're on your own to provide it. + */ +public interface BalanceStrategy { + /** + * Called by the driver to pick a new connection to route requests over. + * See LoadBalancedConnectionProxy.createConnectionForHost(String) + * + * @param proxy + * the InvocationHandler that deals with actual method calls to + * the JDBC connection, and serves as a factory for new + * connections for this strategy via the + * createConnectionForHost() method. + * + * This proxy takes care of maintaining the response time list, map of + * host/ports to live connections, and taking connections out of the live + * connections map if they receive a network-related error while they are in + * use by the application. + * @param configuredHosts + * the list of hosts/ports (in "host:port" form) as passed in by + * the user. + * @param liveConnections + * a map of host/ports to "live" connections to them. + * @param responseTimes + * the list of response times for a transaction + * for each host in the configured hosts list. + * @param numRetries + * the number of times the driver expects this strategy to re-try + * connection attempts if creating a new connection fails. + * @return the physical JDBC connection for the application to use, based + * upon the strategy employed. + * @throws SQLException + * if a new connection can not be found or created by this + * strategy. + */ + abstract JdbcConnection pickConnection(InvocationHandler proxy, List configuredHosts, Map liveConnections, + long[] responseTimes, int numRetries) throws SQLException; +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/BestResponseTimeBalanceStrategy.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/BestResponseTimeBalanceStrategy.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/BestResponseTimeBalanceStrategy.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.lang.reflect.InvocationHandler; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +import com.mysql.cj.jdbc.ConnectionImpl; +import com.mysql.cj.jdbc.JdbcConnection; + +public class BestResponseTimeBalanceStrategy implements BalanceStrategy { + + public BestResponseTimeBalanceStrategy() { + } + + public ConnectionImpl pickConnection(InvocationHandler proxy, List configuredHosts, Map liveConnections, + long[] responseTimes, int numRetries) throws SQLException { + + Map blackList = ((LoadBalancedConnectionProxy) proxy).getGlobalBlacklist(); + + SQLException ex = null; + + for (int attempts = 0; attempts < numRetries;) { + long minResponseTime = Long.MAX_VALUE; + + int bestHostIndex = 0; + + // safety + if (blackList.size() == configuredHosts.size()) { + blackList = ((LoadBalancedConnectionProxy) proxy).getGlobalBlacklist(); + } + + for (int i = 0; i < responseTimes.length; i++) { + long candidateResponseTime = responseTimes[i]; + + if (candidateResponseTime < minResponseTime && !blackList.containsKey(configuredHosts.get(i))) { + if (candidateResponseTime == 0) { + bestHostIndex = i; + + break; + } + + bestHostIndex = i; + minResponseTime = candidateResponseTime; + } + } + + String bestHost = configuredHosts.get(bestHostIndex); + + ConnectionImpl conn = (ConnectionImpl) liveConnections.get(bestHost); + + if (conn == null) { + try { + conn = ((LoadBalancedConnectionProxy) proxy).createConnectionForHost(bestHost); + } catch (SQLException sqlEx) { + ex = sqlEx; + + if (((LoadBalancedConnectionProxy) proxy).shouldExceptionTriggerConnectionSwitch(sqlEx)) { + ((LoadBalancedConnectionProxy) proxy).addToGlobalBlacklist(bestHost); + blackList.put(bestHost, null); + + if (blackList.size() == configuredHosts.size()) { + attempts++; + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + blackList = ((LoadBalancedConnectionProxy) proxy).getGlobalBlacklist(); // try again after a little bit + } + + continue; + } + + throw sqlEx; + } + } + + return conn; + } + + if (ex != null) { + throw ex; + } + + return null; // we won't get here, compiler can't tell + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/FailoverConnectionProxy.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/FailoverConnectionProxy.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/FailoverConnectionProxy.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.util.concurrent.Executor; + +import com.mysql.cj.conf.ConnectionUrl; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.CJCommunicationsException; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.ConnectionImpl; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.JdbcPropertySetImpl; +import com.mysql.cj.jdbc.exceptions.CommunicationsException; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping; +import com.mysql.cj.util.Util; + +/** + * A proxy for a dynamic com.mysql.cj.jdbc.JdbcConnection implementation that provides failover features for list of hosts. Connection switching occurs on + * communications related exceptions and/or user defined settings, namely when one of the conditions set in 'secondsBeforeRetryMaster' or + * 'queriesBeforeRetryMaster' is met. + */ +public class FailoverConnectionProxy extends MultiHostConnectionProxy { + private static final String METHOD_SET_READ_ONLY = "setReadOnly"; + private static final String METHOD_SET_AUTO_COMMIT = "setAutoCommit"; + private static final String METHOD_COMMIT = "commit"; + private static final String METHOD_ROLLBACK = "rollback"; + + private static final int NO_CONNECTION_INDEX = -1; + private static final int DEFAULT_PRIMARY_HOST_INDEX = 0; + + private int secondsBeforeRetryPrimaryHost; + private long queriesBeforeRetryPrimaryHost; + private boolean failoverReadOnly; + private int retriesAllDown; + + private int currentHostIndex = NO_CONNECTION_INDEX; + private int primaryHostIndex = DEFAULT_PRIMARY_HOST_INDEX; + private Boolean explicitlyReadOnly = null; + private boolean explicitlyAutoCommit = true; + + private boolean enableFallBackToPrimaryHost = true; + private long primaryHostFailTimeMillis = 0; + private long queriesIssuedSinceFailover = 0; + + /** + * Proxy class to intercept and deal with errors that may occur in any object bound to the current connection. + * Additionally intercepts query executions and triggers an execution count on the outer class. + */ + class FailoverJdbcInterfaceProxy extends JdbcInterfaceProxy { + FailoverJdbcInterfaceProxy(Object toInvokeOn) { + super(toInvokeOn); + } + + @SuppressWarnings("synthetic-access") + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + + boolean isExecute = methodName.startsWith("execute"); + + if (FailoverConnectionProxy.this.connectedToSecondaryHost() && isExecute) { + FailoverConnectionProxy.this.incrementQueriesIssuedSinceFailover(); + } + + Object result = super.invoke(proxy, method, args); + + if (FailoverConnectionProxy.this.explicitlyAutoCommit && isExecute && readyToFallBackToPrimaryHost()) { + // Fall back to primary host at transaction boundary + fallBackToPrimaryIfAvailable(); + } + + return result; + } + } + + public static JdbcConnection createProxyInstance(ConnectionUrl connectionUrl) throws SQLException { + FailoverConnectionProxy connProxy = new FailoverConnectionProxy(connectionUrl); + + return (JdbcConnection) java.lang.reflect.Proxy.newProxyInstance(JdbcConnection.class.getClassLoader(), new Class[] { JdbcConnection.class }, + connProxy); + } + + /** + * Instantiates a new FailoverConnectionProxy for the given list of hosts and connection properties. + * + * @param hosts + * The lists of hosts available to switch on. + * @param props + * The properties to be used in new internal connections. + */ + private FailoverConnectionProxy(ConnectionUrl connectionUrl) throws SQLException { + super(connectionUrl); + + JdbcPropertySetImpl connProps = new JdbcPropertySetImpl(); + connProps.initializeProperties(connectionUrl.getConnectionArgumentsAsProperties()); + + this.secondsBeforeRetryPrimaryHost = connProps.getIntegerReadableProperty(PropertyDefinitions.PNAME_secondsBeforeRetryMaster).getValue(); + this.queriesBeforeRetryPrimaryHost = connProps.getIntegerReadableProperty(PropertyDefinitions.PNAME_queriesBeforeRetryMaster).getValue(); + this.failoverReadOnly = connProps.getBooleanReadableProperty(PropertyDefinitions.PNAME_failOverReadOnly).getValue(); + this.retriesAllDown = connProps.getIntegerReadableProperty(PropertyDefinitions.PNAME_retriesAllDown).getValue(); + + this.enableFallBackToPrimaryHost = this.secondsBeforeRetryPrimaryHost > 0 || this.queriesBeforeRetryPrimaryHost > 0; + + pickNewConnection(); + + this.explicitlyAutoCommit = this.currentConnection.getAutoCommit(); + } + + /** + * Gets locally bound instances of FailoverJdbcInterfaceProxy. + * + */ + @Override + JdbcInterfaceProxy getNewJdbcInterfaceProxy(Object toProxy) { + return new FailoverJdbcInterfaceProxy(toProxy); + } + + /* + * Local implementation for the connection switch exception checker. + */ + @Override + boolean shouldExceptionTriggerConnectionSwitch(Throwable t) { + + String sqlState = null; + if (t instanceof CommunicationsException || t instanceof CJCommunicationsException) { + return true; + } else if (t instanceof SQLException) { + sqlState = ((SQLException) t).getSQLState(); + } else if (t instanceof CJException) { + sqlState = ((CJException) t).getSQLState(); + } + + if (sqlState != null) { + if (sqlState.startsWith("08")) { + // connection error + return true; + } + } + + return false; + } + + /** + * Checks if current connection is to a master host. + */ + @Override + boolean isMasterConnection() { + return connectedToPrimaryHost(); + } + + /* + * Local implementation for the new connection picker. + */ + @Override + synchronized void pickNewConnection() throws SQLException { + if (this.isClosed && this.closedExplicitly) { + return; + } + + if (!isConnected() || readyToFallBackToPrimaryHost()) { + try { + connectTo(this.primaryHostIndex); + } catch (SQLException e) { + resetAutoFallBackCounters(); + failOver(this.primaryHostIndex); + } + } else { + failOver(); + } + } + + /** + * Creates a new connection instance for host pointed out by the given host index. + * + * @param hostIndex + * The host index in the global hosts list. + * @return + * The new connection instance. + */ + synchronized ConnectionImpl createConnectionForHostIndex(int hostIndex) throws SQLException { + return createConnectionForHost(this.hostsList.get(hostIndex)); + } + + /** + * Connects this dynamic failover connection proxy to the host pointed out by the given host index. + * + * @param hostIndex + * The host index in the global hosts list. + */ + private synchronized void connectTo(int hostIndex) throws SQLException { + try { + switchCurrentConnectionTo(hostIndex, createConnectionForHostIndex(hostIndex)); + } catch (SQLException e) { + if (this.currentConnection != null) { + StringBuilder msg = new StringBuilder("Connection to ").append(isPrimaryHostIndex(hostIndex) ? "primary" : "secondary").append(" host '") + .append(this.hostsList.get(hostIndex)).append("' failed"); + try { + this.currentConnection.getSession().getLog().logWarn(msg.toString(), e); + } catch (CJException ex) { + throw SQLExceptionsMapping.translateException(e, this.currentConnection.getExceptionInterceptor()); + } + } + throw e; + } + } + + /** + * Replaces the previous underlying connection by the connection given. State from previous connection, if any, is synchronized with the new one. + * + * @param hostIndex + * The host index in the global hosts list that matches the given connection. + * @param connection + * The connection instance to switch to. + */ + private synchronized void switchCurrentConnectionTo(int hostIndex, JdbcConnection connection) throws SQLException { + invalidateCurrentConnection(); + + boolean readOnly; + if (isPrimaryHostIndex(hostIndex)) { + readOnly = this.explicitlyReadOnly == null ? false : this.explicitlyReadOnly; + } else if (this.failoverReadOnly) { + readOnly = true; + } else if (this.explicitlyReadOnly != null) { + readOnly = this.explicitlyReadOnly; + } else if (this.currentConnection != null) { + readOnly = this.currentConnection.isReadOnly(); + } else { + readOnly = false; + } + syncSessionState(this.currentConnection, connection, readOnly); + this.currentConnection = connection; + this.currentHostIndex = hostIndex; + } + + /** + * Initiates a default failover procedure starting at the current connection host index. + */ + private synchronized void failOver() throws SQLException { + failOver(this.currentHostIndex); + } + + /** + * Initiates a default failover procedure starting at the given host index. + * This process tries to connect, sequentially, to the next host in the list. The primary host may or may not be excluded from the connection attempts. + * + * @param failedHostIdx + * The host index where to start from. First connection attempt will be the next one. + */ + private synchronized void failOver(int failedHostIdx) throws SQLException { + int prevHostIndex = this.currentHostIndex; + int nextHostIndex = nextHost(failedHostIdx, false); + int firstHostIndexTried = nextHostIndex; + + SQLException lastExceptionCaught = null; + int attempts = 0; + boolean gotConnection = false; + boolean firstConnOrPassedByPrimaryHost = prevHostIndex == NO_CONNECTION_INDEX || isPrimaryHostIndex(prevHostIndex); + do { + try { + firstConnOrPassedByPrimaryHost = firstConnOrPassedByPrimaryHost || isPrimaryHostIndex(nextHostIndex); + + connectTo(nextHostIndex); + + if (firstConnOrPassedByPrimaryHost && connectedToSecondaryHost()) { + resetAutoFallBackCounters(); + } + gotConnection = true; + + } catch (SQLException e) { + lastExceptionCaught = e; + + if (shouldExceptionTriggerConnectionSwitch(e)) { + int newNextHostIndex = nextHost(nextHostIndex, attempts > 0); + + if (newNextHostIndex == firstHostIndexTried && newNextHostIndex == (newNextHostIndex = nextHost(nextHostIndex, true))) { // Full turn + attempts++; + + try { + Thread.sleep(250); + } catch (InterruptedException ie) { + } + } + + nextHostIndex = newNextHostIndex; + + } else { + throw e; + } + } + } while (attempts < this.retriesAllDown && !gotConnection); + + if (!gotConnection) { + throw lastExceptionCaught; + } + } + + /** + * Falls back to primary host or keep current connection if primary not available. + */ + synchronized void fallBackToPrimaryIfAvailable() { + JdbcConnection connection = null; + try { + connection = createConnectionForHostIndex(this.primaryHostIndex); + switchCurrentConnectionTo(this.primaryHostIndex, connection); + } catch (SQLException e1) { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e2) { + } + } + // Keep current connection and reset counters + resetAutoFallBackCounters(); + } + } + + /** + * Gets the next host on the hosts list. Uses a round-robin algorithm to find the next element, but it may skip the index for the primary host. + * General rules to include the primary host are: + * - not currently connected to any host. + * - primary host is vouched (usually because connection to all secondary hosts has failed). + * - conditions to fall back to primary host are met (or they are disabled). + * + * @param currHostIdx + * Current host index. + * @param vouchForPrimaryHost + * Allows to return the primary host index even if the usual required conditions aren't met. + */ + private int nextHost(int currHostIdx, boolean vouchForPrimaryHost) { + int nextHostIdx = (currHostIdx + 1) % this.hostsList.size(); + if (isPrimaryHostIndex(nextHostIdx) && isConnected() && !vouchForPrimaryHost && this.enableFallBackToPrimaryHost && !readyToFallBackToPrimaryHost()) { + // Skip primary host, assume this.hostList.size() >= 2 + nextHostIdx = nextHost(nextHostIdx, vouchForPrimaryHost); + } + return nextHostIdx; + } + + /** + * Increments counter for query executions. + */ + synchronized void incrementQueriesIssuedSinceFailover() { + this.queriesIssuedSinceFailover++; + } + + /** + * Checks if at least one of the required conditions to fall back to primary host is met, which is determined by the properties 'queriesBeforeRetryMaster' + * and 'secondsBeforeRetryMaster'. + */ + synchronized boolean readyToFallBackToPrimaryHost() { + return this.enableFallBackToPrimaryHost && connectedToSecondaryHost() && (secondsBeforeRetryPrimaryHostIsMet() || queriesBeforeRetryPrimaryHostIsMet()); + } + + /** + * Checks if there is a underlying connection for this proxy. + */ + synchronized boolean isConnected() { + return this.currentHostIndex != NO_CONNECTION_INDEX; + } + + /** + * Checks if the given host index points to the primary host. + * + * @param hostIndex + * The host index in the global hosts list. + */ + synchronized boolean isPrimaryHostIndex(int hostIndex) { + return hostIndex == this.primaryHostIndex; + } + + /** + * Checks if this proxy is using the primary host in the underlying connection. + */ + synchronized boolean connectedToPrimaryHost() { + return isPrimaryHostIndex(this.currentHostIndex); + } + + /** + * Checks if this proxy is using a secondary host in the underlying connection. + */ + synchronized boolean connectedToSecondaryHost() { + return this.currentHostIndex >= 0 && !isPrimaryHostIndex(this.currentHostIndex); + } + + /** + * Checks the condition set by the property 'secondsBeforeRetryMaster'. + */ + private synchronized boolean secondsBeforeRetryPrimaryHostIsMet() { + return this.secondsBeforeRetryPrimaryHost > 0 && Util.secondsSinceMillis(this.primaryHostFailTimeMillis) >= this.secondsBeforeRetryPrimaryHost; + } + + /** + * Checks the condition set by the property 'queriesBeforeRetryMaster'. + */ + private synchronized boolean queriesBeforeRetryPrimaryHostIsMet() { + return this.queriesBeforeRetryPrimaryHost > 0 && this.queriesIssuedSinceFailover >= this.queriesBeforeRetryPrimaryHost; + } + + /** + * Resets auto-fall back counters. + */ + private synchronized void resetAutoFallBackCounters() { + this.primaryHostFailTimeMillis = System.currentTimeMillis(); + this.queriesIssuedSinceFailover = 0; + } + + /** + * Closes current connection. + */ + @Override + synchronized void doClose() throws SQLException { + this.currentConnection.close(); + } + + /** + * Aborts current connection. + */ + @Override + synchronized void doAbortInternal() throws SQLException { + this.currentConnection.abortInternal(); + } + + /** + * Aborts current connection using the given executor. + */ + @Override + synchronized void doAbort(Executor executor) throws SQLException { + this.currentConnection.abort(executor); + } + + /* + * Local method invocation handling for this proxy. + * This is the continuation of MultiHostConnectionProxy#invoke(Object, Method, Object[]). + */ + @Override + public synchronized Object invokeMore(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + + if (METHOD_SET_READ_ONLY.equals(methodName)) { + this.explicitlyReadOnly = (Boolean) args[0]; + if (this.failoverReadOnly && connectedToSecondaryHost()) { + return null; + } + } + + if (this.isClosed && !allowedOnClosedConnection(method)) { + if (this.autoReconnect && !this.closedExplicitly) { + this.currentHostIndex = NO_CONNECTION_INDEX; // Act as if this is the first connection but let it sync with the previous one. + pickNewConnection(); + this.isClosed = false; + this.closedReason = null; + } else { + String reason = "No operations allowed after connection closed."; + if (this.closedReason != null) { + reason += (" " + this.closedReason); + } + throw SQLError.createSQLException(reason, MysqlErrorNumbers.SQL_STATE_CONNECTION_NOT_OPEN, null /* no access to a interceptor here... */); + } + } + + Object result = null; + + try { + result = method.invoke(this.thisAsConnection, args); + result = proxyIfReturnTypeIsJdbcInterface(method.getReturnType(), result); + } catch (InvocationTargetException e) { + dealWithInvocationException(e); + } + + if (METHOD_SET_AUTO_COMMIT.equals(methodName)) { + this.explicitlyAutoCommit = (Boolean) args[0]; + } + + if ((this.explicitlyAutoCommit || METHOD_COMMIT.equals(methodName) || METHOD_ROLLBACK.equals(methodName)) && readyToFallBackToPrimaryHost()) { + // Fall back to primary host at transaction boundary + fallBackToPrimaryIfAvailable(); + } + + return result; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalanceExceptionChecker.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalanceExceptionChecker.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalanceExceptionChecker.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.util.Properties; + +public interface LoadBalanceExceptionChecker { + + /** + * Called once per connection that wants to use the extension + * + * The properties are the same ones passed in in the URL or arguments to + * Driver.connect() or DriverManager.getConnection(). + * + * @param props + * configuration values as passed to the connection. Note that + * in order to support javax.sql.DataSources, configuration properties specific + * to an interceptor must be passed via setURL() on the + * DataSource. Extension properties are not exposed via + * accessor/mutator methods on DataSources. + */ + void init(Properties props); + + /** + * Called by the driver when this extension should release any resources + * it is holding and cleanup internally before the connection is + * closed. + */ + void destroy(); + + /** + * Invoked to determine whether or a given SQLException should + * trigger a failover in a load-balanced deployment. + * + * The driver will not pass in a Connection instance when calling init(), but it + * will pass in the Properties, otherwise it acts like a normal Extension. + * + * One instance of a handler *per* JDBC connection instance will be created. If + * you need singleton-like behavior, you're on your own to provide it. + * + * @param ex + * exception + * @return true if the exception should trigger failover. + */ + boolean shouldExceptionTriggerFailover(Throwable ex); + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalancedAutoCommitInterceptor.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalancedAutoCommitInterceptor.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalancedAutoCommitInterceptor.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.sql.SQLException; +import java.util.Properties; +import java.util.function.Supplier; + +import com.mysql.cj.MysqlConnection; +import com.mysql.cj.Query; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.interceptors.QueryInterceptor; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.log.Log; +import com.mysql.cj.protocol.Resultset; +import com.mysql.cj.protocol.ServerSession; +import com.mysql.cj.util.StringUtils; + +public class LoadBalancedAutoCommitInterceptor implements QueryInterceptor { + private int matchingAfterStatementCount = 0; + private int matchingAfterStatementThreshold = 0; + private String matchingAfterStatementRegex; + private JdbcConnection conn; + private LoadBalancedConnectionProxy proxy = null; + + private boolean countStatements = false; + + public void destroy() { + this.conn = null; + this.proxy = null; + } + + public boolean executeTopLevelOnly() { + // always return false + return false; + } + + public QueryInterceptor init(MysqlConnection connection, Properties props, Log log) { + this.conn = (JdbcConnection) connection; + + String autoCommitSwapThresholdAsString = props.getProperty(PropertyDefinitions.PNAME_loadBalanceAutoCommitStatementThreshold, "0"); + try { + this.matchingAfterStatementThreshold = Integer.parseInt(autoCommitSwapThresholdAsString); + } catch (NumberFormatException nfe) { + // nothing here, being handled in LoadBalancedConnectionProxy. + } + String autoCommitSwapRegex = props.getProperty(PropertyDefinitions.PNAME_loadBalanceAutoCommitStatementRegex, ""); + if (!"".equals(autoCommitSwapRegex)) { + this.matchingAfterStatementRegex = autoCommitSwapRegex; + } + return this; + + } + + @SuppressWarnings("resource") + public T postProcess(Supplier sql, Query interceptedQuery, T originalResultSet, ServerSession serverSession) { + + try { + // Don't count SETs neither SHOWs. Those are mostly used internally and must not trigger a connection switch. + if (!this.countStatements || StringUtils.startsWithIgnoreCase(sql.get(), "SET") || StringUtils.startsWithIgnoreCase(sql.get(), "SHOW")) { + return originalResultSet; + } + + // Don't care if auto-commit is not enabled. + if (!this.conn.getAutoCommit()) { + this.matchingAfterStatementCount = 0; + return originalResultSet; + } + + if (this.proxy == null && this.conn.isProxySet()) { + JdbcConnection lcl_proxy = this.conn.getMultiHostSafeProxy(); + while (lcl_proxy != null && !(lcl_proxy instanceof LoadBalancedMySQLConnection)) { + lcl_proxy = lcl_proxy.getMultiHostSafeProxy(); + } + if (lcl_proxy != null) { + this.proxy = ((LoadBalancedMySQLConnection) lcl_proxy).getThisAsProxy(); + } + } + + // Connection is not ready to rebalance yet. + if (this.proxy == null) { + return originalResultSet; + } + + // Increment the match count if no regex specified, or if matches. + if (this.matchingAfterStatementRegex == null || sql.get().matches(this.matchingAfterStatementRegex)) { + this.matchingAfterStatementCount++; + } + + // Trigger rebalance if count exceeds threshold. + if (this.matchingAfterStatementCount >= this.matchingAfterStatementThreshold) { + this.matchingAfterStatementCount = 0; + try { + this.proxy.pickNewConnection(); + } catch (SQLException e) { + // eat this exception, the auto-commit statement completed, but we could not rebalance for some reason. User may get exception when using + // connection next. + } + } + } catch (SQLException ex) { + throw ExceptionFactory.createException(ex.getMessage(), ex); + } + // always return the original result set. + return originalResultSet; + } + + public T preProcess(Supplier sql, Query interceptedQuery) { + // we do nothing before execution, it's unsafe to swap servers at this point. + return null; + } + + void pauseCounters() { + this.countStatements = false; + } + + void resumeCounters() { + this.countStatements = true; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalancedConnection.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalancedConnection.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalancedConnection.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.sql.SQLException; + +import com.mysql.cj.jdbc.JdbcConnection; + +public interface LoadBalancedConnection extends JdbcConnection { + + boolean addHost(String host) throws SQLException; + + void removeHost(String host) throws SQLException; + + void removeHostWhenNotInUse(String host) throws SQLException; + + void ping(boolean allConnections) throws SQLException; + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalancedConnectionProxy.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalancedConnectionProxy.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalancedConnectionProxy.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,910 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; + +import com.mysql.cj.Messages; +import com.mysql.cj.PingTarget; +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.url.LoadbalanceConnectionUrl; +import com.mysql.cj.exceptions.CJCommunicationsException; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.interceptors.QueryInterceptor; +import com.mysql.cj.jdbc.ConnectionGroup; +import com.mysql.cj.jdbc.ConnectionGroupManager; +import com.mysql.cj.jdbc.ConnectionImpl; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping; +import com.mysql.cj.util.Util; + +/** + * A proxy for a dynamic com.mysql.cj.jdbc.JdbcConnection implementation that load balances requests across a series of MySQL JDBC connections, where the + * balancing + * takes place at transaction commit. + * + * Therefore, for this to work (at all), you must use transactions, even if only reading data. + * + * This implementation will invalidate connections that it detects have had communication errors when processing a request. Problematic hosts will be added to a + * global blacklist for loadBalanceBlacklistTimeout ms, after which they will be removed from the blacklist and made eligible once again to be selected for new + * connections. + * + * This implementation is thread-safe, but it's questionable whether sharing a connection instance amongst threads is a good idea, given that transactions are + * scoped to connections in JDBC. + */ +public class LoadBalancedConnectionProxy extends MultiHostConnectionProxy implements PingTarget { + private ConnectionGroup connectionGroup = null; + private long connectionGroupProxyID = 0; + + protected Map liveConnections; + private Map hostsToListIndexMap; + private Map connectionsToHostsMap; + private long totalPhysicalConnections = 0; + private long[] responseTimes; + + private int retriesAllDown; + private BalanceStrategy balancer; + + private int globalBlacklistTimeout = 0; + private static Map globalBlacklist = new HashMap<>(); + private int hostRemovalGracePeriod = 0; + // host:port pairs to be considered as removed (definitely blacklisted) from the original hosts list. + private Set hostsToRemove = new HashSet<>(); + + private boolean inTransaction = false; + private long transactionStartTime = 0; + private long transactionCount = 0; + + private LoadBalanceExceptionChecker exceptionChecker; + + private static Class[] INTERFACES_TO_PROXY = new Class[] { LoadBalancedConnection.class, JdbcConnection.class }; + + /** + * Static factory to create {@link LoadBalancedConnection} instances. + * + * @param connectionUrl + * The connection URL containing the hosts in a load-balance setup. + * @return A {@link LoadBalancedConnection} proxy. + */ + public static LoadBalancedConnection createProxyInstance(LoadbalanceConnectionUrl connectionUrl) throws SQLException { + LoadBalancedConnectionProxy connProxy = new LoadBalancedConnectionProxy(connectionUrl); + return (LoadBalancedConnection) java.lang.reflect.Proxy.newProxyInstance(LoadBalancedConnection.class.getClassLoader(), INTERFACES_TO_PROXY, connProxy); + } + + /** + * Creates a proxy for java.sql.Connection that routes requests between the hosts in the connection URL. + * + * @param connectionUrl + * The connection URL containing the hosts to load balance. + * @throws SQLException + */ + public LoadBalancedConnectionProxy(LoadbalanceConnectionUrl connectionUrl) throws SQLException { + super(); + + List hosts; + Properties props = connectionUrl.getConnectionArgumentsAsProperties(); + + String group = props.getProperty(PropertyDefinitions.PNAME_loadBalanceConnectionGroup, null); + boolean enableJMX = false; + String enableJMXAsString = props.getProperty(PropertyDefinitions.PNAME_ha_enableJMX, "false"); + try { + enableJMX = Boolean.parseBoolean(enableJMXAsString); + } catch (Exception e) { + throw SQLError.createSQLException(Messages.getString("MultihostConnection.badValueForHaEnableJMX", new Object[] { enableJMXAsString }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + if (group != null) { + this.connectionGroup = ConnectionGroupManager.getConnectionGroupInstance(group); + if (enableJMX) { + ConnectionGroupManager.registerJmx(); + } + this.connectionGroupProxyID = this.connectionGroup.registerConnectionProxy(this, connectionUrl.getHostInfoListAsHostPortPairs()); + hosts = connectionUrl.getHostInfoListFromHostPortPairs(this.connectionGroup.getInitialHosts()); + } else { + hosts = connectionUrl.getHostsList(); + } + + // hosts specifications may have been reset with settings from a previous connection group + int numHosts = initializeHostsSpecs(connectionUrl, hosts); + + this.liveConnections = new HashMap<>(numHosts); + this.hostsToListIndexMap = new HashMap<>(numHosts); + for (int i = 0; i < numHosts; i++) { + this.hostsToListIndexMap.put(this.hostsList.get(i).getHostPortPair(), i); + } + this.connectionsToHostsMap = new HashMap<>(numHosts); + this.responseTimes = new long[numHosts]; + + String retriesAllDownAsString = props.getProperty(PropertyDefinitions.PNAME_retriesAllDown, "120"); + try { + this.retriesAllDown = Integer.parseInt(retriesAllDownAsString); + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException( + Messages.getString("LoadBalancedConnectionProxy.badValueForRetriesAllDown", new Object[] { retriesAllDownAsString }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String blacklistTimeoutAsString = props.getProperty(PropertyDefinitions.PNAME_loadBalanceBlacklistTimeout, "0"); + try { + this.globalBlacklistTimeout = Integer.parseInt(blacklistTimeoutAsString); + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException( + Messages.getString("LoadBalancedConnectionProxy.badValueForLoadBalanceBlacklistTimeout", new Object[] { blacklistTimeoutAsString }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String hostRemovalGracePeriodAsString = props.getProperty(PropertyDefinitions.PNAME_loadBalanceHostRemovalGracePeriod, "15000"); + try { + this.hostRemovalGracePeriod = Integer.parseInt(hostRemovalGracePeriodAsString); + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException(Messages.getString("LoadBalancedConnectionProxy.badValueForLoadBalanceHostRemovalGracePeriod", + new Object[] { hostRemovalGracePeriodAsString }), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String strategy = props.getProperty(PropertyDefinitions.PNAME_loadBalanceStrategy, "random"); + try { + switch (strategy) { + case "random": + this.balancer = new RandomBalanceStrategy(); + break; + case "bestResponseTime": + this.balancer = new BestResponseTimeBalanceStrategy(); + break; + case "serverAffinity": + this.balancer = new ServerAffinityStrategy(props.getProperty(PropertyDefinitions.PNAME_serverAffinityOrder, null)); + break; + default: + this.balancer = (BalanceStrategy) Class.forName(strategy).newInstance(); + } + } catch (Throwable t) { + throw SQLError.createSQLException(Messages.getString("InvalidLoadBalanceStrategy", new Object[] { strategy }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, t, null); + } + + String autoCommitSwapThresholdAsString = props.getProperty(PropertyDefinitions.PNAME_loadBalanceAutoCommitStatementThreshold, "0"); + try { + Integer.parseInt(autoCommitSwapThresholdAsString); + } catch (NumberFormatException nfe) { + throw SQLError.createSQLException(Messages.getString("LoadBalancedConnectionProxy.badValueForLoadBalanceAutoCommitStatementThreshold", + new Object[] { autoCommitSwapThresholdAsString }), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String autoCommitSwapRegex = props.getProperty(PropertyDefinitions.PNAME_loadBalanceAutoCommitStatementRegex, ""); + if (!("".equals(autoCommitSwapRegex))) { + try { + "".matches(autoCommitSwapRegex); + } catch (Exception e) { + throw SQLError.createSQLException( + Messages.getString("LoadBalancedConnectionProxy.badValueForLoadBalanceAutoCommitStatementRegex", new Object[] { autoCommitSwapRegex }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + } + + try { + String lbExceptionChecker = props.getProperty(PropertyDefinitions.PNAME_loadBalanceExceptionChecker, + StandardLoadBalanceExceptionChecker.class.getName()); + this.exceptionChecker = (LoadBalanceExceptionChecker) Util.getInstance(lbExceptionChecker, new Class[0], new Object[0], null, + Messages.getString("InvalidLoadBalanceExceptionChecker")); + this.exceptionChecker.init(props); + + } catch (CJException e) { + throw SQLExceptionsMapping.translateException(e, null); + } + + pickNewConnection(); + } + + /** + * Wraps this object with a new load balanced Connection instance. + * + * @return + * The connection object instance that wraps 'this'. + */ + @Override + JdbcConnection getNewWrapperForThisAsConnection() throws SQLException { + return new LoadBalancedMySQLConnection(this); + } + + /** + * Propagates the connection proxy down through all live connections. + * + * @param proxyConn + * The top level connection in the multi-host connections chain. + */ + @Override + protected void propagateProxyDown(JdbcConnection proxyConn) { + for (JdbcConnection c : this.liveConnections.values()) { + c.setProxy(proxyConn); + } + } + + @Deprecated + public boolean shouldExceptionTriggerFailover(Throwable t) { + return shouldExceptionTriggerConnectionSwitch(t); + } + + /** + * Consults the registered LoadBalanceExceptionChecker if the given exception should trigger a connection fail-over. + * + * @param ex + * The Exception instance to check. + * @return + */ + @Override + boolean shouldExceptionTriggerConnectionSwitch(Throwable t) { + return t instanceof SQLException && this.exceptionChecker.shouldExceptionTriggerFailover(t); + } + + /** + * Always returns 'true' as there are no "masters" and "slaves" in this type of connection. + */ + @Override + boolean isMasterConnection() { + return true; + } + + /** + * Closes specified connection and removes it from required mappings. + * + * @param conn + * @throws SQLException + */ + @Override + synchronized void invalidateConnection(JdbcConnection conn) throws SQLException { + super.invalidateConnection(conn); + + // add host to the global blacklist, if enabled + if (this.isGlobalBlacklistEnabled()) { + addToGlobalBlacklist(this.connectionsToHostsMap.get(conn)); + } + + // remove from liveConnections + this.liveConnections.remove(this.connectionsToHostsMap.get(conn)); + Object mappedHost = this.connectionsToHostsMap.remove(conn); + if (mappedHost != null && this.hostsToListIndexMap.containsKey(mappedHost)) { + int hostIndex = this.hostsToListIndexMap.get(mappedHost); + // reset the statistics for the host + synchronized (this.responseTimes) { + this.responseTimes[hostIndex] = 0; + } + } + } + + /** + * Picks the "best" connection to use for the next transaction based on the BalanceStrategy in use. + * + * @throws SQLException + */ + @Override + public synchronized void pickNewConnection() throws SQLException { + if (this.isClosed && this.closedExplicitly) { + return; + } + + List hostPortList = Collections.unmodifiableList(this.hostsList.stream().map(hi -> hi.getHostPortPair()).collect(Collectors.toList())); + + if (this.currentConnection == null) { // startup + this.currentConnection = this.balancer.pickConnection(this, hostPortList, Collections.unmodifiableMap(this.liveConnections), + this.responseTimes.clone(), this.retriesAllDown); + return; + } + + if (this.currentConnection.isClosed()) { + invalidateCurrentConnection(); + } + + int pingTimeout = this.currentConnection.getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_loadBalancePingTimeout).getValue(); + boolean pingBeforeReturn = this.currentConnection.getPropertySet() + .getBooleanReadableProperty(PropertyDefinitions.PNAME_loadBalanceValidateConnectionOnSwapServer).getValue(); + + for (int hostsTried = 0, hostsToTry = this.hostsList.size(); hostsTried < hostsToTry; hostsTried++) { + ConnectionImpl newConn = null; + try { + newConn = (ConnectionImpl) this.balancer.pickConnection(this, hostPortList, Collections.unmodifiableMap(this.liveConnections), + this.responseTimes.clone(), this.retriesAllDown); + + if (this.currentConnection != null) { + if (pingBeforeReturn) { + newConn.pingInternal(true, pingTimeout); + } + + syncSessionState(this.currentConnection, newConn); + } + + this.currentConnection = newConn; + return; + + } catch (SQLException e) { + if (shouldExceptionTriggerConnectionSwitch(e) && newConn != null) { + // connection error, close up shop on current connection + invalidateConnection(newConn); + } + } + } + + // no hosts available to swap connection to, close up. + this.isClosed = true; + this.closedReason = "Connection closed after inability to pick valid new connection during load-balance."; + } + + /** + * Creates a new physical connection for the given {@link HostInfo} and updates required internal mappings and statistics for that connection. + * + * @param hostInfo + * The host info instance. + * @return + * The new Connection instance. + */ + @Override + public synchronized ConnectionImpl createConnectionForHost(HostInfo hostInfo) throws SQLException { + ConnectionImpl conn = super.createConnectionForHost(hostInfo); + + this.liveConnections.put(hostInfo.getHostPortPair(), conn); + this.connectionsToHostsMap.put(conn, hostInfo.getHostPortPair()); + + this.totalPhysicalConnections++; + + for (QueryInterceptor stmtInterceptor : conn.getQueryInterceptorsInstances()) { + if (stmtInterceptor instanceof LoadBalancedAutoCommitInterceptor) { + ((LoadBalancedAutoCommitInterceptor) stmtInterceptor).resumeCounters(); + break; + } + } + + return conn; + } + + @Override + void syncSessionState(JdbcConnection source, JdbcConnection target, boolean readOnly) throws SQLException { + LoadBalancedAutoCommitInterceptor lbAutoCommitStmtInterceptor = null; + for (QueryInterceptor stmtInterceptor : target.getQueryInterceptorsInstances()) { + if (stmtInterceptor instanceof LoadBalancedAutoCommitInterceptor) { + lbAutoCommitStmtInterceptor = (LoadBalancedAutoCommitInterceptor) stmtInterceptor; + lbAutoCommitStmtInterceptor.pauseCounters(); + break; + } + } + super.syncSessionState(source, target, readOnly); + if (lbAutoCommitStmtInterceptor != null) { + lbAutoCommitStmtInterceptor.resumeCounters(); + } + } + + /** + * Creates a new physical connection for the given host:port info. If the this connection's connection URL knows about this host:port then its host info is + * used, otherwise a new host info based on current connection URL defaults is spawned. + * + * @param hostPortPair + * The host:port pair identifying the host to connect to. + * @return + * The new Connection instance. + */ + public synchronized ConnectionImpl createConnectionForHost(String hostPortPair) throws SQLException { + for (HostInfo hi : this.hostsList) { + if (hi.getHostPortPair().equals(hostPortPair)) { + return createConnectionForHost(hi); + } + } + return null; + } + + /** + * Closes all live connections. + */ + private synchronized void closeAllConnections() { + // close all underlying connections + for (Connection c : this.liveConnections.values()) { + try { + c.close(); + } catch (SQLException e) { + } + } + + if (!this.isClosed) { + if (this.connectionGroup != null) { + this.connectionGroup.closeConnectionProxy(this); + } + } + + this.liveConnections.clear(); + this.connectionsToHostsMap.clear(); + } + + /** + * Closes all live connections. + */ + @Override + synchronized void doClose() { + closeAllConnections(); + } + + /** + * Aborts all live connections + */ + @Override + synchronized void doAbortInternal() { + // abort all underlying connections + for (JdbcConnection c : this.liveConnections.values()) { + try { + c.abortInternal(); + } catch (SQLException e) { + } + } + + if (!this.isClosed) { + if (this.connectionGroup != null) { + this.connectionGroup.closeConnectionProxy(this); + } + } + + this.liveConnections.clear(); + this.connectionsToHostsMap.clear(); + } + + /** + * Aborts all live connections, using the provided Executor. + */ + @Override + synchronized void doAbort(Executor executor) { + // close all underlying connections + for (Connection c : this.liveConnections.values()) { + try { + c.abort(executor); + } catch (SQLException e) { + } + } + + if (!this.isClosed) { + if (this.connectionGroup != null) { + this.connectionGroup.closeConnectionProxy(this); + } + } + + this.liveConnections.clear(); + this.connectionsToHostsMap.clear(); + } + + /** + * Proxies method invocation on the java.sql.Connection interface, trapping "close", "isClosed" and "commit/rollback" to switch connections for load + * balancing. + * This is the continuation of MultiHostConnectionProxy#invoke(Object, Method, Object[]). + */ + @Override + public synchronized Object invokeMore(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + + if (this.isClosed && !allowedOnClosedConnection(method) && method.getExceptionTypes().length > 0) { // TODO remove method.getExceptionTypes().length ? + if (this.autoReconnect && !this.closedExplicitly) { + // try to reconnect first! + this.currentConnection = null; + pickNewConnection(); + this.isClosed = false; + this.closedReason = null; + } else { + String reason = "No operations allowed after connection closed."; + if (this.closedReason != null) { + reason += " " + this.closedReason; + } + + for (Class excls : method.getExceptionTypes()) { + if (SQLException.class.isAssignableFrom(excls)) { + throw SQLError.createSQLException(reason, MysqlErrorNumbers.SQL_STATE_CONNECTION_NOT_OPEN, + null /* no access to an interceptor here... */); + } + } + throw ExceptionFactory.createException(CJCommunicationsException.class, reason); + } + } + + if (!this.inTransaction) { + this.inTransaction = true; + this.transactionStartTime = System.nanoTime(); + this.transactionCount++; + } + + Object result = null; + + try { + result = method.invoke(this.thisAsConnection, args); + + if (result != null) { + if (result instanceof com.mysql.cj.jdbc.JdbcStatement) { + ((com.mysql.cj.jdbc.JdbcStatement) result).setPingTarget(this); + } + result = proxyIfReturnTypeIsJdbcInterface(method.getReturnType(), result); + } + + } catch (InvocationTargetException e) { + dealWithInvocationException(e); + + } finally { + if ("commit".equals(methodName) || "rollback".equals(methodName)) { + this.inTransaction = false; + + // Update stats + String host = this.connectionsToHostsMap.get(this.currentConnection); + // avoid NPE if the connection has already been removed from connectionsToHostsMap in invalidateCurrenctConnection() + if (host != null) { + synchronized (this.responseTimes) { + Integer hostIndex = (this.hostsToListIndexMap.get(host)); + + if (hostIndex != null && hostIndex < this.responseTimes.length) { + this.responseTimes[hostIndex] = System.nanoTime() - this.transactionStartTime; + } + } + } + pickNewConnection(); + } + } + + return result; + } + + /** + * Pings live connections. + */ + public synchronized void doPing() throws SQLException { + SQLException se = null; + boolean foundHost = false; + int pingTimeout = this.currentConnection.getPropertySet().getIntegerReadableProperty(PropertyDefinitions.PNAME_loadBalancePingTimeout).getValue(); + + synchronized (this) { + for (HostInfo hi : this.hostsList) { + String host = hi.getHostPortPair(); + ConnectionImpl conn = this.liveConnections.get(host); + if (conn == null) { + continue; + } + try { + if (pingTimeout == 0) { + conn.ping(); + } else { + conn.pingInternal(true, pingTimeout); + } + foundHost = true; + } catch (SQLException e) { + // give up if it is the current connection, otherwise NPE faking resultset later. + if (host.equals(this.connectionsToHostsMap.get(this.currentConnection))) { + // clean up underlying connections, since connection pool won't do it + closeAllConnections(); + this.isClosed = true; + this.closedReason = "Connection closed because ping of current connection failed."; + throw e; + } + + // if the Exception is caused by ping connection lifetime checks, don't add to blacklist + if (e.getMessage().equals(Messages.getString("Connection.exceededConnectionLifetime"))) { + // only set the return Exception if it's null + if (se == null) { + se = e; + } + } else { + // overwrite the return Exception no matter what + se = e; + if (isGlobalBlacklistEnabled()) { + addToGlobalBlacklist(host); + } + } + // take the connection out of the liveConnections Map + this.liveConnections.remove(this.connectionsToHostsMap.get(conn)); + } + } + } + // if there were no successful pings + if (!foundHost) { + closeAllConnections(); + this.isClosed = true; + this.closedReason = "Connection closed due to inability to ping any active connections."; + // throw the stored Exception, if exists + if (se != null) { + throw se; + } + // or create a new SQLException and throw it, must be no liveConnections + ((ConnectionImpl) this.currentConnection).throwConnectionClosedException(); + } + } + + /** + * Adds a host to the blacklist with the given timeout. + * + * @param host + * The host to be blacklisted. + * @param timeout + * The blacklist timeout for this entry. + */ + public void addToGlobalBlacklist(String host, long timeout) { + if (isGlobalBlacklistEnabled()) { + synchronized (globalBlacklist) { + globalBlacklist.put(host, timeout); + } + } + } + + /** + * Adds a host to the blacklist. + * + * @param host + * The host to be blacklisted. + */ + public void addToGlobalBlacklist(String host) { + addToGlobalBlacklist(host, System.currentTimeMillis() + this.globalBlacklistTimeout); + } + + /** + * Checks if host blacklist management was enabled. + * + * @return + */ + public boolean isGlobalBlacklistEnabled() { + return (this.globalBlacklistTimeout > 0); + } + + /** + * Returns a local hosts blacklist, while cleaning up expired records from the global blacklist, or a blacklist with the hosts to be removed. + * + * @return + * A local hosts blacklist. + */ + public synchronized Map getGlobalBlacklist() { + if (!isGlobalBlacklistEnabled()) { + if (this.hostsToRemove.isEmpty()) { + return new HashMap<>(1); + } + HashMap fakedBlacklist = new HashMap<>(); + for (String h : this.hostsToRemove) { + fakedBlacklist.put(h, System.currentTimeMillis() + 5000); + } + return fakedBlacklist; + } + + // Make a local copy of the blacklist + Map blacklistClone = new HashMap<>(globalBlacklist.size()); + // Copy everything from synchronized global blacklist to local copy for manipulation + synchronized (globalBlacklist) { + blacklistClone.putAll(globalBlacklist); + } + Set keys = blacklistClone.keySet(); + + // We're only interested in blacklisted hosts that are in the hostList + keys.retainAll(this.hostsList); + + // Don't need to synchronize here as we using a local copy + for (Iterator i = keys.iterator(); i.hasNext();) { + String host = i.next(); + // OK if null is returned because another thread already purged Map entry. + Long timeout = globalBlacklist.get(host); + if (timeout != null && timeout < System.currentTimeMillis()) { + // Timeout has expired, remove from blacklist + synchronized (globalBlacklist) { + globalBlacklist.remove(host); + } + i.remove(); + } + + } + if (keys.size() == this.hostsList.size()) { + // return an empty blacklist, let the BalanceStrategy implementations try to connect to everything since it appears that all hosts are + // unavailable - we don't want to wait for loadBalanceBlacklistTimeout to expire. + return new HashMap<>(1); + } + + return blacklistClone; + } + + /** + * Removes a host from the host list, allowing it some time to be released gracefully if needed. + * + * @param hostPortPair + * The host to be removed. + * @throws SQLException + */ + public void removeHostWhenNotInUse(String hostPortPair) throws SQLException { + if (this.hostRemovalGracePeriod <= 0) { + removeHost(hostPortPair); + return; + } + + int timeBetweenChecks = this.hostRemovalGracePeriod > 1000 ? 1000 : this.hostRemovalGracePeriod; + + synchronized (this) { + addToGlobalBlacklist(hostPortPair, System.currentTimeMillis() + this.hostRemovalGracePeriod + timeBetweenChecks); + + long cur = System.currentTimeMillis(); + + while (System.currentTimeMillis() < cur + this.hostRemovalGracePeriod) { + this.hostsToRemove.add(hostPortPair); + + if (!hostPortPair.equals(this.currentConnection.getHostPortPair())) { + removeHost(hostPortPair); + return; + } + + try { + Thread.sleep(timeBetweenChecks); + } catch (InterruptedException e) { + // better to swallow this and retry. + } + } + } + + removeHost(hostPortPair); + } + + /** + * Removes a host from the host list. + * + * @param hostPortPair + * The host to be removed. + * @throws SQLException + */ + public synchronized void removeHost(String hostPortPair) throws SQLException { + if (this.connectionGroup != null) { + if (this.connectionGroup.getInitialHosts().size() == 1 && this.connectionGroup.getInitialHosts().contains(hostPortPair)) { + throw SQLError.createSQLException(Messages.getString("LoadBalancedConnectionProxy.0"), null); + } + } + + this.hostsToRemove.add(hostPortPair); + + this.connectionsToHostsMap.remove(this.liveConnections.remove(hostPortPair)); + if (this.hostsToListIndexMap.remove(hostPortPair) != null) { + long[] newResponseTimes = new long[this.responseTimes.length - 1]; + int newIdx = 0; + for (HostInfo hostInfo : this.hostsList) { + String host = hostInfo.getHostPortPair(); + if (!this.hostsToRemove.contains(host)) { + Integer idx = this.hostsToListIndexMap.get(host); + if (idx != null && idx < this.responseTimes.length) { + newResponseTimes[newIdx] = this.responseTimes[idx]; + } + this.hostsToListIndexMap.put(host, newIdx++); + } + } + this.responseTimes = newResponseTimes; + } + + if (hostPortPair.equals(this.currentConnection.getHostPortPair())) { + invalidateConnection(this.currentConnection); + pickNewConnection(); + } + } + + /** + * Adds a host to the hosts list. + * + * @param hostPortPair + * The host to be added. + */ + public synchronized boolean addHost(String hostPortPair) { + if (this.hostsToListIndexMap.containsKey(hostPortPair)) { + return false; + } + + long[] newResponseTimes = new long[this.responseTimes.length + 1]; + System.arraycopy(this.responseTimes, 0, newResponseTimes, 0, this.responseTimes.length); + + this.responseTimes = newResponseTimes; + if (this.hostsList.stream().noneMatch(hi -> hostPortPair.equals(hi.getHostPortPair()))) { + this.hostsList.add(this.connectionUrl.getHostOrSpawnIsolated(hostPortPair)); + } + this.hostsToListIndexMap.put(hostPortPair, this.responseTimes.length - 1); + this.hostsToRemove.remove(hostPortPair); + + return true; + } + + public synchronized boolean inTransaction() { + return this.inTransaction; + } + + public synchronized long getTransactionCount() { + return this.transactionCount; + } + + public synchronized long getActivePhysicalConnectionCount() { + return this.liveConnections.size(); + } + + public synchronized long getTotalPhysicalConnectionCount() { + return this.totalPhysicalConnections; + } + + public synchronized long getConnectionGroupProxyID() { + return this.connectionGroupProxyID; + } + + public synchronized String getCurrentActiveHost() { + JdbcConnection c = this.currentConnection; + if (c != null) { + Object o = this.connectionsToHostsMap.get(c); + if (o != null) { + return o.toString(); + } + } + return null; + } + + public synchronized long getCurrentTransactionDuration() { + if (this.inTransaction && this.transactionStartTime > 0) { + return System.nanoTime() - this.transactionStartTime; + } + return 0; + } + + /** + * A LoadBalancedConnection proxy that provides null-functionality. It can be used as a replacement of the null keyword in the places where a + * LoadBalancedConnection object cannot be effectively null because that would be a potential source of NPEs. + */ + private static class NullLoadBalancedConnectionProxy implements InvocationHandler { + public NullLoadBalancedConnectionProxy() { + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + SQLException exceptionToThrow = SQLError.createSQLException(Messages.getString("LoadBalancedConnectionProxy.unusableConnection"), + MysqlErrorNumbers.SQL_STATE_INVALID_TRANSACTION_STATE, MysqlErrorNumbers.ERROR_CODE_NULL_LOAD_BALANCED_CONNECTION, true, null); + Class[] declaredException = method.getExceptionTypes(); + for (Class declEx : declaredException) { + if (declEx.isAssignableFrom(exceptionToThrow.getClass())) { + throw exceptionToThrow; + } + } + throw new IllegalStateException(exceptionToThrow.getMessage(), exceptionToThrow); + } + } + + private static LoadBalancedConnection nullLBConnectionInstance = null; + + static synchronized LoadBalancedConnection getNullLoadBalancedConnectionInstance() { + if (nullLBConnectionInstance == null) { + nullLBConnectionInstance = (LoadBalancedConnection) java.lang.reflect.Proxy.newProxyInstance(LoadBalancedConnection.class.getClassLoader(), + INTERFACES_TO_PROXY, new NullLoadBalancedConnectionProxy()); + } + return nullLBConnectionInstance; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalancedMySQLConnection.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalancedMySQLConnection.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/LoadBalancedMySQLConnection.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.sql.SQLException; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.exceptions.SQLError; + +public class LoadBalancedMySQLConnection extends MultiHostMySQLConnection implements LoadBalancedConnection { + public LoadBalancedMySQLConnection(LoadBalancedConnectionProxy proxy) { + super(proxy); + } + + @Override + public LoadBalancedConnectionProxy getThisAsProxy() { + return (LoadBalancedConnectionProxy) super.getThisAsProxy(); + } + + @Override + public void close() throws SQLException { + getThisAsProxy().doClose(); + } + + @Override + public void ping() throws SQLException { + ping(true); + } + + public void ping(boolean allConnections) throws SQLException { + if (allConnections) { + getThisAsProxy().doPing(); + } else { + getActiveMySQLConnection().ping(); + } + } + + public boolean addHost(String host) throws SQLException { + return getThisAsProxy().addHost(host); + } + + public void removeHost(String host) throws SQLException { + getThisAsProxy().removeHost(host); + } + + public void removeHostWhenNotInUse(String host) throws SQLException { + getThisAsProxy().removeHostWhenNotInUse(host); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + @Override + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("Common.UnableToUnwrap", new Object[] { iface.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/MultiHostConnectionProxy.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/MultiHostConnectionProxy.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/MultiHostConnectionProxy.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.Executor; + +import com.mysql.cj.conf.ConnectionUrl; +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.conf.ModifiableProperty; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.jdbc.ConnectionImpl; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.util.Util; + +/** + * An abstract class that processes generic multi-host configurations. This class has to be sub-classed by specific multi-host implementations, such as + * load-balancing and failover. + */ +public abstract class MultiHostConnectionProxy implements InvocationHandler { + private static final String METHOD_GET_MULTI_HOST_SAFE_PROXY = "getMultiHostSafeProxy"; + private static final String METHOD_EQUALS = "equals"; + private static final String METHOD_HASH_CODE = "hashCode"; + private static final String METHOD_CLOSE = "close"; + private static final String METHOD_ABORT_INTERNAL = "abortInternal"; + private static final String METHOD_ABORT = "abort"; + private static final String METHOD_IS_CLOSED = "isClosed"; + private static final String METHOD_GET_AUTO_COMMIT = "getAutoCommit"; + private static final String METHOD_GET_CATALOG = "getCatalog"; + private static final String METHOD_GET_TRANSACTION_ISOLATION = "getTransactionIsolation"; + private static final String METHOD_GET_SESSION_MAX_ROWS = "getSessionMaxRows"; + + List hostsList; + protected ConnectionUrl connectionUrl; + + boolean autoReconnect = false; + + JdbcConnection thisAsConnection = null; + JdbcConnection proxyConnection = null; + + JdbcConnection currentConnection = null; + + boolean isClosed = false; + boolean closedExplicitly = false; + String closedReason = null; + + // Keep track of the last exception processed in 'dealWithInvocationException()' in order to avoid creating connections repeatedly from each time the same + // exception is caught in every proxy instance belonging to the same call stack. + protected Throwable lastExceptionDealtWith = null; + + /** + * Proxy class to intercept and deal with errors that may occur in any object bound to the current connection. + */ + class JdbcInterfaceProxy implements InvocationHandler { + Object invokeOn = null; + + JdbcInterfaceProxy(Object toInvokeOn) { + this.invokeOn = toInvokeOn; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (METHOD_EQUALS.equals(method.getName())) { + // Let args[0] "unwrap" to its InvocationHandler if it is a proxy. + return args[0].equals(this); + } + + synchronized (MultiHostConnectionProxy.this) { + Object result = null; + + try { + result = method.invoke(this.invokeOn, args); + result = proxyIfReturnTypeIsJdbcInterface(method.getReturnType(), result); + } catch (InvocationTargetException e) { + dealWithInvocationException(e); + } + + return result; + } + } + } + + /** + * Initializes a connection wrapper for this MultiHostConnectionProxy instance. + */ + MultiHostConnectionProxy() throws SQLException { + this.thisAsConnection = getNewWrapperForThisAsConnection(); + } + + /** + * Constructs a MultiHostConnectionProxy instance for the given connection URL. + * + * @param connectionUrl + * The connection URL. + */ + MultiHostConnectionProxy(ConnectionUrl connectionUrl) throws SQLException { + this(); + initializeHostsSpecs(connectionUrl, connectionUrl.getHostsList()); + } + + /** + * Initializes the hosts lists and makes a "clean" local copy of the given connection properties so that it can be later used to create standard + * connections. + * + * @param connUrl + * The connection URL that initialized this multi-host connection. + * @param hosts + * The list of hosts for this multi-host connection. + * @return + * The number of hosts found in the hosts list. + */ + int initializeHostsSpecs(ConnectionUrl connUrl, List hosts) { + this.connectionUrl = connUrl; + + Properties props = connUrl.getConnectionArgumentsAsProperties(); + + this.autoReconnect = "true".equalsIgnoreCase(props.getProperty(PropertyDefinitions.PNAME_autoReconnect)) + || "true".equalsIgnoreCase(props.getProperty(PropertyDefinitions.PNAME_autoReconnectForPools)); + + this.hostsList = new ArrayList<>(hosts); + int numHosts = this.hostsList.size(); + return numHosts; + } + + /** + * Get this connection's proxy. + * A multi-host connection may not be at top level in the multi-host connections chain. In such case the first connection in the chain is available as a + * proxy. + * + * @return + * Returns this connection's proxy if there is one or itself if this is the first one. + */ + protected JdbcConnection getProxy() { + return this.proxyConnection != null ? this.proxyConnection : this.thisAsConnection; + } + + /** + * Sets this connection's proxy. This proxy should be the first connection in the multi-host connections chain. + * After setting the connection proxy locally, propagates it through the dependant connections. + * + * @param proxyConn + * The top level connection in the multi-host connections chain. + */ + protected final void setProxy(JdbcConnection proxyConn) { + this.proxyConnection = proxyConn; + propagateProxyDown(proxyConn); + } + + /** + * Propagates the connection proxy down through the multi-host connections chain. + * This method is intended to be overridden in subclasses that manage more than one active connection at same time. + * + * @param proxyConn + * The top level connection in the multi-host connections chain. + */ + protected void propagateProxyDown(JdbcConnection proxyConn) { + this.currentConnection.setProxy(proxyConn); + } + + /** + * Wraps this object with a new multi-host Connection instance. + * + * @return + * The connection object instance that wraps 'this'. + */ + JdbcConnection getNewWrapperForThisAsConnection() throws SQLException { + return new MultiHostMySQLConnection(this); + } + + /** + * If the given return type is or implements a JDBC interface, proxies the given object so that we can catch SQL errors and fire a connection switch. + * + * @param returnType + * The type the object instance to proxy is supposed to be. + * @param toProxy + * The object instance to proxy. + * @return + * The proxied object or the original one if it does not implement a JDBC interface. + */ + Object proxyIfReturnTypeIsJdbcInterface(Class returnType, Object toProxy) { + if (toProxy != null) { + if (Util.isJdbcInterface(returnType)) { + Class toProxyClass = toProxy.getClass(); + return Proxy.newProxyInstance(toProxyClass.getClassLoader(), Util.getImplementedInterfaces(toProxyClass), getNewJdbcInterfaceProxy(toProxy)); + } + } + return toProxy; + } + + /** + * Instantiates a new JdbcInterfaceProxy for the given object. Subclasses can override this to return instances of JdbcInterfaceProxy subclasses. + * + * @param toProxy + * The object instance to be proxied. + * @return + * The new InvocationHandler instance. + */ + InvocationHandler getNewJdbcInterfaceProxy(Object toProxy) { + return new JdbcInterfaceProxy(toProxy); + } + + /** + * Deals with InvocationException from proxied objects. + * + * @param e + * The Exception instance to check. + */ + void dealWithInvocationException(InvocationTargetException e) throws SQLException, Throwable, InvocationTargetException { + Throwable t = e.getTargetException(); + + if (t != null) { + if (this.lastExceptionDealtWith != t && shouldExceptionTriggerConnectionSwitch(t)) { + invalidateCurrentConnection(); + pickNewConnection(); + this.lastExceptionDealtWith = t; + } + throw t; + } + throw e; + } + + /** + * Checks if the given throwable should trigger a connection switch. + * + * @param t + * The Throwable instance to analyze. + */ + abstract boolean shouldExceptionTriggerConnectionSwitch(Throwable t); + + /** + * Checks if current connection is to a master host. + */ + abstract boolean isMasterConnection(); + + /** + * Invalidates the current connection. + */ + synchronized void invalidateCurrentConnection() throws SQLException { + invalidateConnection(this.currentConnection); + } + + /** + * Invalidates the specified connection by closing it. + * + * @param conn + * The connection instance to invalidate. + */ + synchronized void invalidateConnection(JdbcConnection conn) throws SQLException { + try { + if (conn != null && !conn.isClosed()) { + conn.realClose(true, !conn.getAutoCommit(), true, null); + } + } catch (SQLException e) { + // swallow this exception, current connection should be useless anyway. + } + } + + /** + * Picks the "best" connection to use from now on. Each subclass needs to implement its connection switch strategy on it. + */ + abstract void pickNewConnection() throws SQLException; + + /** + * Creates a new physical connection for the given {@link HostInfo}. + * + * @param hostInfo + * The host info instance. + * @return + * The new Connection instance. + */ + synchronized ConnectionImpl createConnectionForHost(HostInfo hostInfo) throws SQLException { + ConnectionImpl conn = (ConnectionImpl) ConnectionImpl.getInstance(hostInfo); + conn.setProxy(getProxy()); + return conn; + } + + /** + * Synchronizes session state between two connections. + * + * @param source + * The connection where to get state from. + * @param target + * The connection where to set state. + */ + void syncSessionState(JdbcConnection source, JdbcConnection target) throws SQLException { + if (source == null || target == null) { + return; + } + + ModifiableProperty sourceUseLocalSessionState = source.getPropertySet() + .getBooleanModifiableProperty(PropertyDefinitions.PNAME_useLocalSessionState); + boolean prevUseLocalSessionState = sourceUseLocalSessionState.getValue(); + sourceUseLocalSessionState.setValue(true); + boolean readOnly = source.isReadOnly(); + sourceUseLocalSessionState.setValue(prevUseLocalSessionState); + + syncSessionState(source, target, readOnly); + } + + /** + * Synchronizes session state between two connections, allowing to override the read-only status. + * + * @param source + * The connection where to get state from. + * @param target + * The connection where to set state. + * @param readOnly + * The new read-only status. + */ + void syncSessionState(JdbcConnection source, JdbcConnection target, boolean readOnly) throws SQLException { + if (target != null) { + target.setReadOnly(readOnly); + } + + if (source == null || target == null) { + return; + } + + ModifiableProperty sourceUseLocalSessionState = source.getPropertySet() + .getBooleanModifiableProperty(PropertyDefinitions.PNAME_useLocalSessionState); + boolean prevUseLocalSessionState = sourceUseLocalSessionState.getValue(); + sourceUseLocalSessionState.setValue(true); + + target.setAutoCommit(source.getAutoCommit()); + target.setCatalog(source.getCatalog()); + target.setTransactionIsolation(source.getTransactionIsolation()); + target.setSessionMaxRows(source.getSessionMaxRows()); + + sourceUseLocalSessionState.setValue(prevUseLocalSessionState); + } + + /** + * Executes a close() invocation; + */ + abstract void doClose() throws SQLException; + + /** + * Executes a abortInternal() invocation; + */ + abstract void doAbortInternal() throws SQLException; + + /** + * Executes a abort() invocation; + */ + abstract void doAbort(Executor executor) throws SQLException; + + /** + * Proxies method invocation on the java.sql.Connection interface, trapping multi-host specific methods and generic methods. + * Subclasses have to override this to complete the method invocation process, deal with exceptions and decide when to switch connection. + * To avoid unnecessary additional exception handling overriders should consult #canDealWith(Method) before chaining here. + */ + public synchronized Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + + if (METHOD_GET_MULTI_HOST_SAFE_PROXY.equals(methodName)) { + return this.thisAsConnection; + } + + if (METHOD_EQUALS.equals(methodName)) { + // Let args[0] "unwrap" to its InvocationHandler if it is a proxy. + return args[0].equals(this); + } + + if (METHOD_HASH_CODE.equals(methodName)) { + return this.hashCode(); + } + + if (METHOD_CLOSE.equals(methodName)) { + doClose(); + this.isClosed = true; + this.closedReason = "Connection explicitly closed."; + this.closedExplicitly = true; + return null; + } + + if (METHOD_ABORT_INTERNAL.equals(methodName)) { + doAbortInternal(); + this.currentConnection.abortInternal(); + this.isClosed = true; + this.closedReason = "Connection explicitly closed."; + return null; + } + + if (METHOD_ABORT.equals(methodName) && args.length == 1) { + doAbort((Executor) args[0]); + this.isClosed = true; + this.closedReason = "Connection explicitly closed."; + return null; + } + + if (METHOD_IS_CLOSED.equals(methodName)) { + return this.isClosed; + } + + try { + return invokeMore(proxy, method, args); + } catch (InvocationTargetException e) { + throw e.getCause() != null ? e.getCause() : e; + } catch (Exception e) { + // Check if the captured exception must be wrapped by an unchecked exception. + Class[] declaredException = method.getExceptionTypes(); + for (Class declEx : declaredException) { + if (declEx.isAssignableFrom(e.getClass())) { + throw e; + } + } + throw new IllegalStateException(e.getMessage(), e); + } + } + + /** + * Continuation of the method invocation process, to be implemented within each subclass. + */ + abstract Object invokeMore(Object proxy, Method method, Object[] args) throws Throwable; + + /** + * Checks if the given method is allowed on closed connections. + */ + protected boolean allowedOnClosedConnection(Method method) { + String methodName = method.getName(); + + return methodName.equals(METHOD_GET_AUTO_COMMIT) || methodName.equals(METHOD_GET_CATALOG) || methodName.equals(METHOD_GET_TRANSACTION_ISOLATION) + || methodName.equals(METHOD_GET_SESSION_MAX_ROWS); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/MultiHostMySQLConnection.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/MultiHostMySQLConnection.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/MultiHostMySQLConnection.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,630 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +import com.mysql.cj.Messages; +import com.mysql.cj.ServerVersion; +import com.mysql.cj.Session; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.interceptors.QueryInterceptor; +import com.mysql.cj.jdbc.ClientInfoProvider; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.JdbcPreparedStatement; +import com.mysql.cj.jdbc.JdbcPropertySet; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.result.CachedResultSetMetaData; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; + +/** + * Each instance of MultiHostMySQLConnection is coupled with a MultiHostConnectionProxy instance. + * + * While this class implements MySQLConnection directly, MultiHostConnectionProxy does the same but via a dynamic proxy. + * + * Most of the methods in this class refer directly to the active connection from its MultiHostConnectionProxy pair, providing a non-proxied access to the + * current active connection managed by this multi-host structure. The remaining methods either implement some local behavior or refer to the proxy itself + * instead of the sub-connection. + * + * Referring to the higher level proxy connection is needed when some operation needs to be extended to all open sub-connections existing in this multi-host + * structure as opposed to just refer to the active current connection, such as with close() which is most likely required to close all sub-connections as + * well. + */ +public class MultiHostMySQLConnection implements JdbcConnection { + + /** + * thisAsProxy holds the proxy (MultiHostConnectionProxy or one of its subclasses) this connection is associated with. + * It is used as a gateway to the current active sub-connection managed by this multi-host structure or as a target to where some of the methods implemented + * here in this class refer to. + */ + protected MultiHostConnectionProxy thisAsProxy; + + public MultiHostMySQLConnection(MultiHostConnectionProxy proxy) { + this.thisAsProxy = proxy; + } + + public MultiHostConnectionProxy getThisAsProxy() { + return this.thisAsProxy; + } + + public JdbcConnection getActiveMySQLConnection() { + synchronized (this.thisAsProxy) { + return this.thisAsProxy.currentConnection; + } + } + + public void abortInternal() throws SQLException { + getActiveMySQLConnection().abortInternal(); + } + + public void changeUser(String userName, String newPassword) throws SQLException { + getActiveMySQLConnection().changeUser(userName, newPassword); + } + + public void checkClosed() { + getActiveMySQLConnection().checkClosed(); + } + + @Deprecated + public void clearHasTriedMaster() { + getActiveMySQLConnection().clearHasTriedMaster(); + } + + public void clearWarnings() throws SQLException { + getActiveMySQLConnection().clearWarnings(); + } + + public PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return getActiveMySQLConnection().clientPrepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public PreparedStatement clientPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return getActiveMySQLConnection().clientPrepareStatement(sql, resultSetType, resultSetConcurrency); + } + + public PreparedStatement clientPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + return getActiveMySQLConnection().clientPrepareStatement(sql, autoGenKeyIndex); + } + + public PreparedStatement clientPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + return getActiveMySQLConnection().clientPrepareStatement(sql, autoGenKeyIndexes); + } + + public PreparedStatement clientPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + return getActiveMySQLConnection().clientPrepareStatement(sql, autoGenKeyColNames); + } + + public PreparedStatement clientPrepareStatement(String sql) throws SQLException { + return getActiveMySQLConnection().clientPrepareStatement(sql); + } + + public void close() throws SQLException { + getActiveMySQLConnection().close(); + } + + public void commit() throws SQLException { + getActiveMySQLConnection().commit(); + } + + public void createNewIO(boolean isForReconnect) { + getActiveMySQLConnection().createNewIO(isForReconnect); + } + + public Statement createStatement() throws SQLException { + return getActiveMySQLConnection().createStatement(); + } + + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return getActiveMySQLConnection().createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return getActiveMySQLConnection().createStatement(resultSetType, resultSetConcurrency); + } + + public int getActiveStatementCount() { + return getActiveMySQLConnection().getActiveStatementCount(); + } + + public boolean getAutoCommit() throws SQLException { + return getActiveMySQLConnection().getAutoCommit(); + } + + public int getAutoIncrementIncrement() { + return getActiveMySQLConnection().getAutoIncrementIncrement(); + } + + public CachedResultSetMetaData getCachedMetaData(String sql) { + return getActiveMySQLConnection().getCachedMetaData(sql); + } + + public String getCatalog() throws SQLException { + return getActiveMySQLConnection().getCatalog(); + } + + public String getCharacterSetMetadata() { + return getActiveMySQLConnection().getCharacterSetMetadata(); + } + + public ExceptionInterceptor getExceptionInterceptor() { + return getActiveMySQLConnection().getExceptionInterceptor(); + } + + public int getHoldability() throws SQLException { + return getActiveMySQLConnection().getHoldability(); + } + + public String getHost() { + return getActiveMySQLConnection().getHost(); + } + + public long getId() { + return getActiveMySQLConnection().getId(); + } + + public long getIdleFor() { + return getActiveMySQLConnection().getIdleFor(); + } + + public JdbcConnection getMultiHostSafeProxy() { + return getThisAsProxy().getProxy(); + } + + public DatabaseMetaData getMetaData() throws SQLException { + return getActiveMySQLConnection().getMetaData(); + } + + public Statement getMetadataSafeStatement() throws SQLException { + return getActiveMySQLConnection().getMetadataSafeStatement(); + } + + public Properties getProperties() { + return getActiveMySQLConnection().getProperties(); + } + + public ServerVersion getServerVersion() { + return getActiveMySQLConnection().getServerVersion(); + } + + public Session getSession() { + return getActiveMySQLConnection().getSession(); + } + + public String getStatementComment() { + return getActiveMySQLConnection().getStatementComment(); + } + + public List getQueryInterceptorsInstances() { + return getActiveMySQLConnection().getQueryInterceptorsInstances(); + } + + public int getTransactionIsolation() throws SQLException { + return getActiveMySQLConnection().getTransactionIsolation(); + } + + public Map> getTypeMap() throws SQLException { + return getActiveMySQLConnection().getTypeMap(); + } + + public String getURL() { + return getActiveMySQLConnection().getURL(); + } + + public String getUser() { + return getActiveMySQLConnection().getUser(); + } + + public SQLWarning getWarnings() throws SQLException { + return getActiveMySQLConnection().getWarnings(); + } + + public boolean hasSameProperties(JdbcConnection c) { + return getActiveMySQLConnection().hasSameProperties(c); + } + + @Deprecated + public boolean hasTriedMaster() { + return getActiveMySQLConnection().hasTriedMaster(); + } + + public void initializeResultsMetadataFromCache(String sql, CachedResultSetMetaData cachedMetaData, ResultSetInternalMethods resultSet) throws SQLException { + getActiveMySQLConnection().initializeResultsMetadataFromCache(sql, cachedMetaData, resultSet); + } + + public void initializeSafeQueryInterceptors() throws SQLException { + getActiveMySQLConnection().initializeSafeQueryInterceptors(); + } + + public boolean isInGlobalTx() { + return getActiveMySQLConnection().isInGlobalTx(); + } + + public boolean isMasterConnection() { + return getThisAsProxy().isMasterConnection(); + } + + public boolean isReadOnly() throws SQLException { + return getActiveMySQLConnection().isReadOnly(); + } + + public boolean isReadOnly(boolean useSessionStatus) throws SQLException { + return getActiveMySQLConnection().isReadOnly(useSessionStatus); + } + + public boolean isSameResource(JdbcConnection otherConnection) { + return getActiveMySQLConnection().isSameResource(otherConnection); + } + + public boolean lowerCaseTableNames() { + return getActiveMySQLConnection().lowerCaseTableNames(); + } + + public String nativeSQL(String sql) throws SQLException { + return getActiveMySQLConnection().nativeSQL(sql); + } + + public void ping() throws SQLException { + getActiveMySQLConnection().ping(); + } + + public void pingInternal(boolean checkForClosedConnection, int timeoutMillis) throws SQLException { + getActiveMySQLConnection().pingInternal(checkForClosedConnection, timeoutMillis); + } + + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return getActiveMySQLConnection().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return getActiveMySQLConnection().prepareCall(sql, resultSetType, resultSetConcurrency); + } + + public CallableStatement prepareCall(String sql) throws SQLException { + return getActiveMySQLConnection().prepareCall(sql); + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return getActiveMySQLConnection().prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return getActiveMySQLConnection().prepareStatement(sql, resultSetType, resultSetConcurrency); + } + + public PreparedStatement prepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + return getActiveMySQLConnection().prepareStatement(sql, autoGenKeyIndex); + } + + public PreparedStatement prepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + return getActiveMySQLConnection().prepareStatement(sql, autoGenKeyIndexes); + } + + public PreparedStatement prepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + return getActiveMySQLConnection().prepareStatement(sql, autoGenKeyColNames); + } + + public PreparedStatement prepareStatement(String sql) throws SQLException { + return getActiveMySQLConnection().prepareStatement(sql); + } + + public void realClose(boolean calledExplicitly, boolean issueRollback, boolean skipLocalTeardown, Throwable reason) throws SQLException { + getActiveMySQLConnection().realClose(calledExplicitly, issueRollback, skipLocalTeardown, reason); + } + + public void recachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException { + getActiveMySQLConnection().recachePreparedStatement(pstmt); + } + + public void decachePreparedStatement(JdbcPreparedStatement pstmt) throws SQLException { + getActiveMySQLConnection().decachePreparedStatement(pstmt); + } + + public void registerStatement(com.mysql.cj.jdbc.JdbcStatement stmt) { + getActiveMySQLConnection().registerStatement(stmt); + } + + public void releaseSavepoint(Savepoint arg0) throws SQLException { + getActiveMySQLConnection().releaseSavepoint(arg0); + } + + public void resetServerState() throws SQLException { + getActiveMySQLConnection().resetServerState(); + } + + public void rollback() throws SQLException { + getActiveMySQLConnection().rollback(); + } + + public void rollback(Savepoint savepoint) throws SQLException { + getActiveMySQLConnection().rollback(savepoint); + } + + public PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return getActiveMySQLConnection().serverPrepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + public PreparedStatement serverPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return getActiveMySQLConnection().serverPrepareStatement(sql, resultSetType, resultSetConcurrency); + } + + public PreparedStatement serverPrepareStatement(String sql, int autoGenKeyIndex) throws SQLException { + return getActiveMySQLConnection().serverPrepareStatement(sql, autoGenKeyIndex); + } + + public PreparedStatement serverPrepareStatement(String sql, int[] autoGenKeyIndexes) throws SQLException { + return getActiveMySQLConnection().serverPrepareStatement(sql, autoGenKeyIndexes); + } + + public PreparedStatement serverPrepareStatement(String sql, String[] autoGenKeyColNames) throws SQLException { + return getActiveMySQLConnection().serverPrepareStatement(sql, autoGenKeyColNames); + } + + public PreparedStatement serverPrepareStatement(String sql) throws SQLException { + return getActiveMySQLConnection().serverPrepareStatement(sql); + } + + public void setAutoCommit(boolean autoCommitFlag) throws SQLException { + getActiveMySQLConnection().setAutoCommit(autoCommitFlag); + } + + public void setCatalog(String catalog) throws SQLException { + getActiveMySQLConnection().setCatalog(catalog); + } + + public void setFailedOver(boolean flag) { + getActiveMySQLConnection().setFailedOver(flag); + } + + public void setHoldability(int arg0) throws SQLException { + getActiveMySQLConnection().setHoldability(arg0); + } + + public void setInGlobalTx(boolean flag) { + getActiveMySQLConnection().setInGlobalTx(flag); + } + + public void setProxy(JdbcConnection proxy) { + getThisAsProxy().setProxy(proxy); + } + + public void setReadOnly(boolean readOnlyFlag) throws SQLException { + getActiveMySQLConnection().setReadOnly(readOnlyFlag); + } + + public void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException { + getActiveMySQLConnection().setReadOnlyInternal(readOnlyFlag); + } + + public Savepoint setSavepoint() throws SQLException { + return getActiveMySQLConnection().setSavepoint(); + } + + public Savepoint setSavepoint(String name) throws SQLException { + return getActiveMySQLConnection().setSavepoint(name); + } + + public void setStatementComment(String comment) { + getActiveMySQLConnection().setStatementComment(comment); + } + + public void setTransactionIsolation(int level) throws SQLException { + getActiveMySQLConnection().setTransactionIsolation(level); + } + + public void shutdownServer() throws SQLException { + getActiveMySQLConnection().shutdownServer(); + } + + public boolean storesLowerCaseTableName() { + return getActiveMySQLConnection().storesLowerCaseTableName(); + } + + public void throwConnectionClosedException() throws SQLException { + getActiveMySQLConnection().throwConnectionClosedException(); + } + + public void transactionBegun() { + getActiveMySQLConnection().transactionBegun(); + } + + public void transactionCompleted() { + getActiveMySQLConnection().transactionCompleted(); + } + + public void unregisterStatement(com.mysql.cj.jdbc.JdbcStatement stmt) { + getActiveMySQLConnection().unregisterStatement(stmt); + } + + public void unSafeQueryInterceptors() throws SQLException { + getActiveMySQLConnection().unSafeQueryInterceptors(); + } + + public boolean isClosed() throws SQLException { + return getThisAsProxy().isClosed; + } + + public boolean isProxySet() { + return this.getActiveMySQLConnection().isProxySet(); + } + + public void setTypeMap(Map> map) throws SQLException { + getActiveMySQLConnection().setTypeMap(map); + } + + public boolean isServerLocal() throws SQLException { + return getActiveMySQLConnection().isServerLocal(); + } + + public void setSchema(String schema) throws SQLException { + getActiveMySQLConnection().setSchema(schema); + } + + public String getSchema() throws SQLException { + return getActiveMySQLConnection().getSchema(); + } + + public void abort(Executor executor) throws SQLException { + getActiveMySQLConnection().abort(executor); + } + + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + getActiveMySQLConnection().setNetworkTimeout(executor, milliseconds); + } + + public int getNetworkTimeout() throws SQLException { + return getActiveMySQLConnection().getNetworkTimeout(); + } + + public Object getConnectionMutex() { + return getActiveMySQLConnection().getConnectionMutex(); + } + + public int getSessionMaxRows() { + return getActiveMySQLConnection().getSessionMaxRows(); + } + + public void setSessionMaxRows(int max) throws SQLException { + getActiveMySQLConnection().setSessionMaxRows(max); + } + + public SQLXML createSQLXML() throws SQLException { + return getActiveMySQLConnection().createSQLXML(); + } + + public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return getActiveMySQLConnection().createArrayOf(typeName, elements); + } + + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return getActiveMySQLConnection().createStruct(typeName, attributes); + } + + public Properties getClientInfo() throws SQLException { + return getActiveMySQLConnection().getClientInfo(); + } + + public String getClientInfo(String name) throws SQLException { + return getActiveMySQLConnection().getClientInfo(name); + } + + public boolean isValid(int timeout) throws SQLException { + return getActiveMySQLConnection().isValid(timeout); + } + + public void setClientInfo(Properties properties) throws SQLClientInfoException { + getActiveMySQLConnection().setClientInfo(properties); + } + + public void setClientInfo(String name, String value) throws SQLClientInfoException { + getActiveMySQLConnection().setClientInfo(name, value); + } + + public boolean isWrapperFor(Class iface) throws SQLException { + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("Common.UnableToUnwrap", new Object[] { iface.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + /** + * @throws SQLException + * @see java.sql.Connection#createBlob() + */ + public Blob createBlob() throws SQLException { + return getActiveMySQLConnection().createBlob(); + } + + /** + * @throws SQLException + * @see java.sql.Connection#createClob() + */ + public Clob createClob() throws SQLException { + return getActiveMySQLConnection().createClob(); + } + + /** + * @throws SQLException + * @see java.sql.Connection#createNClob() + */ + public NClob createNClob() throws SQLException { + return getActiveMySQLConnection().createNClob(); + } + + public ClientInfoProvider getClientInfoProviderImpl() throws SQLException { + synchronized (getThisAsProxy()) { + return getActiveMySQLConnection().getClientInfoProviderImpl(); + } + } + + @Override + public JdbcPropertySet getPropertySet() { + return getActiveMySQLConnection().getPropertySet(); + } + + @Override + public String getHostPortPair() { + return getActiveMySQLConnection().getHostPortPair(); + } + + @Override + public void normalClose() { + getActiveMySQLConnection().normalClose(); + } + + @Override + public void cleanup(Throwable whyCleanedUp) { + getActiveMySQLConnection().cleanup(whyCleanedUp); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/NdbLoadBalanceExceptionChecker.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/NdbLoadBalanceExceptionChecker.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/NdbLoadBalanceExceptionChecker.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +public class NdbLoadBalanceExceptionChecker extends StandardLoadBalanceExceptionChecker { + + @Override + public boolean shouldExceptionTriggerFailover(Throwable ex) { + return super.shouldExceptionTriggerFailover(ex) || checkNdbException(ex); + } + + private boolean checkNdbException(Throwable ex) { + // Have to parse the message since most NDB errors are mapped to the same DEMC, sadly. + return (ex.getMessage().startsWith("Lock wait timeout exceeded") + || (ex.getMessage().startsWith("Got temporary error") && ex.getMessage().endsWith("from NDB"))); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/RandomBalanceStrategy.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/RandomBalanceStrategy.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/RandomBalanceStrategy.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.lang.reflect.InvocationHandler; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.mysql.cj.Messages; +import com.mysql.cj.jdbc.ConnectionImpl; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.exceptions.SQLError; + +public class RandomBalanceStrategy implements BalanceStrategy { + + public RandomBalanceStrategy() { + } + + public ConnectionImpl pickConnection(InvocationHandler proxy, List configuredHosts, Map liveConnections, + long[] responseTimes, int numRetries) throws SQLException { + int numHosts = configuredHosts.size(); + + SQLException ex = null; + + List whiteList = new ArrayList<>(numHosts); + whiteList.addAll(configuredHosts); + + Map blackList = ((LoadBalancedConnectionProxy) proxy).getGlobalBlacklist(); + + whiteList.removeAll(blackList.keySet()); + + Map whiteListMap = this.getArrayIndexMap(whiteList); + + for (int attempts = 0; attempts < numRetries;) { + int random = (int) Math.floor((Math.random() * whiteList.size())); + if (whiteList.size() == 0) { + throw SQLError.createSQLException(Messages.getString("RandomBalanceStrategy.0"), null); + } + + String hostPortSpec = whiteList.get(random); + + ConnectionImpl conn = (ConnectionImpl) liveConnections.get(hostPortSpec); + + if (conn == null) { + try { + conn = ((LoadBalancedConnectionProxy) proxy).createConnectionForHost(hostPortSpec); + } catch (SQLException sqlEx) { + ex = sqlEx; + + if (((LoadBalancedConnectionProxy) proxy).shouldExceptionTriggerConnectionSwitch(sqlEx)) { + + Integer whiteListIndex = whiteListMap.get(hostPortSpec); + + // exclude this host from being picked again + if (whiteListIndex != null) { + whiteList.remove(whiteListIndex.intValue()); + whiteListMap = this.getArrayIndexMap(whiteList); + } + ((LoadBalancedConnectionProxy) proxy).addToGlobalBlacklist(hostPortSpec); + + if (whiteList.size() == 0) { + attempts++; + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + + // start fresh + whiteListMap = new HashMap<>(numHosts); + whiteList.addAll(configuredHosts); + blackList = ((LoadBalancedConnectionProxy) proxy).getGlobalBlacklist(); + + whiteList.removeAll(blackList.keySet()); + whiteListMap = this.getArrayIndexMap(whiteList); + } + + continue; + } + + throw sqlEx; + } + } + + return conn; + } + + if (ex != null) { + throw ex; + } + + return null; // we won't get here, compiler can't tell + } + + private Map getArrayIndexMap(List l) { + Map m = new HashMap<>(l.size()); + for (int i = 0; i < l.size(); i++) { + m.put(l.get(i), Integer.valueOf(i)); + } + return m; + + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationConnection.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationConnection.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationConnection.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.sql.SQLException; + +import com.mysql.cj.jdbc.JdbcConnection; + +public interface ReplicationConnection extends JdbcConnection { + public long getConnectionGroupId(); + + public JdbcConnection getCurrentConnection(); + + public JdbcConnection getMasterConnection(); + + public void promoteSlaveToMaster(String host) throws SQLException; + + public void removeMasterHost(String host) throws SQLException; + + public void removeMasterHost(String host, boolean waitUntilNotInUse) throws SQLException; + + public boolean isHostMaster(String host); + + public JdbcConnection getSlavesConnection(); + + public void addSlaveHost(String host) throws SQLException; + + public void removeSlave(String host) throws SQLException; + + public void removeSlave(String host, boolean closeGently) throws SQLException; + + public boolean isHostSlave(String host); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationConnectionGroup.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationConnectionGroup.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationConnectionGroup.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * Group of connection objects which can be configured as a group. This is used for promotion/demotion of slaves and masters in a replication configuration, + * and for exposing metrics around replication-aware connections. + */ +public class ReplicationConnectionGroup { + private String groupName; + private long connections = 0; + private long slavesAdded = 0; + private long slavesRemoved = 0; + private long slavesPromoted = 0; + private long activeConnections = 0; + private HashMap replicationConnections = new HashMap<>(); + private Set slaveHostList = new CopyOnWriteArraySet<>(); + private boolean isInitialized = false; + private Set masterHostList = new CopyOnWriteArraySet<>(); + + ReplicationConnectionGroup(String groupName) { + this.groupName = groupName; + } + + public long getConnectionCount() { + return this.connections; + } + + public long registerReplicationConnection(ReplicationConnection conn, List localMasterList, List localSlaveList) { + long currentConnectionId; + + synchronized (this) { + if (!this.isInitialized) { + if (localMasterList != null) { + this.masterHostList.addAll(localMasterList); + } + if (localSlaveList != null) { + this.slaveHostList.addAll(localSlaveList); + } + this.isInitialized = true; + } + currentConnectionId = ++this.connections; + this.replicationConnections.put(Long.valueOf(currentConnectionId), conn); + } + this.activeConnections++; + + return currentConnectionId; + } + + public String getGroupName() { + return this.groupName; + } + + public Collection getMasterHosts() { + return this.masterHostList; + } + + public Collection getSlaveHosts() { + return this.slaveHostList; + } + + /** + * Adds a host to the slaves hosts list. + * + * We can safely assume that if this host was added to the slaves list, then it must be added to each one of the replication connections from this group as + * well. + * Unnecessary calls to {@link ReplicationConnection#addSlaveHost(String)} could result in undesirable locking issues, assuming that this method is + * synchronized by nature. + * + * This is a no-op if the group already has this host in a slave role. + * + * @param hostPortPair + * @throws SQLException + */ + public void addSlaveHost(String hostPortPair) throws SQLException { + // only add if it's not already a slave host + if (this.slaveHostList.add(hostPortPair)) { + this.slavesAdded++; + + // add the slave to all connections: + for (ReplicationConnection c : this.replicationConnections.values()) { + c.addSlaveHost(hostPortPair); + } + } + } + + public void handleCloseConnection(ReplicationConnection conn) { + this.replicationConnections.remove(conn.getConnectionGroupId()); + this.activeConnections--; + } + + /** + * Removes a host from the slaves hosts list. + * + * We can safely assume that if this host was removed from the slaves list, then it must be removed from each one of the replication connections from this + * group as well. + * Unnecessary calls to {@link ReplicationConnection#removeSlave(String, boolean)} could result in undesirable locking issues, assuming that this method is + * synchronized by nature. + * + * This is a no-op if the group doesn't have this host in a slave role. + * + * @param hostPortPair + * @param closeGently + * @throws SQLException + */ + public void removeSlaveHost(String hostPortPair, boolean closeGently) throws SQLException { + if (this.slaveHostList.remove(hostPortPair)) { + this.slavesRemoved++; + + // remove the slave from all connections: + for (ReplicationConnection c : this.replicationConnections.values()) { + c.removeSlave(hostPortPair, closeGently); + } + } + } + + /** + * Promotes a slave host to master. + * + * We can safely assume that if this host was removed from the slaves list or added to the masters list, then the same host promotion must happen in each + * one of the replication connections from this group as well. + * Unnecessary calls to {@link ReplicationConnection#promoteSlaveToMaster(String)} could result in undesirable locking issues, assuming that this method is + * synchronized by nature. + * + * This is a no-op if the group already has this host in a master role and not in slave role. + * + * @param hostPortPair + * @throws SQLException + */ + public void promoteSlaveToMaster(String hostPortPair) throws SQLException { + // remove host from slaves AND add host to masters, note that both operands need to be evaluated. + if (this.slaveHostList.remove(hostPortPair) | this.masterHostList.add(hostPortPair)) { + this.slavesPromoted++; + + for (ReplicationConnection c : this.replicationConnections.values()) { + c.promoteSlaveToMaster(hostPortPair); + } + } + } + + /** + * Removes a host from the masters hosts list. + * + * @see #removeMasterHost(String, boolean) + */ + public void removeMasterHost(String hostPortPair) throws SQLException { + this.removeMasterHost(hostPortPair, true); + } + + /** + * Removes a host from the masters hosts list. + * + * We can safely assume that if this host was removed from the masters list, then it must be removed from each one of the replication connections from this + * group as well. + * Unnecessary calls to {@link ReplicationConnection#removeMasterHost(String, boolean)} could result in undesirable locking issues, assuming that this + * method is synchronized by nature. + * + * This is a no-op if the group doesn't have this host in a master role. + * + * @param hostPortPair + * @param closeGently + * @throws SQLException + */ + public void removeMasterHost(String hostPortPair, boolean closeGently) throws SQLException { + if (this.masterHostList.remove(hostPortPair)) { + // remove the master from all connections: + for (ReplicationConnection c : this.replicationConnections.values()) { + c.removeMasterHost(hostPortPair, closeGently); + } + } + } + + public int getConnectionCountWithHostAsSlave(String hostPortPair) { + int matched = 0; + + for (ReplicationConnection c : this.replicationConnections.values()) { + if (c.isHostSlave(hostPortPair)) { + matched++; + } + } + return matched; + } + + public int getConnectionCountWithHostAsMaster(String hostPortPair) { + int matched = 0; + + for (ReplicationConnection c : this.replicationConnections.values()) { + if (c.isHostMaster(hostPortPair)) { + matched++; + } + } + return matched; + } + + public long getNumberOfSlavesAdded() { + return this.slavesAdded; + } + + public long getNumberOfSlavesRemoved() { + return this.slavesRemoved; + } + + public long getNumberOfSlavePromotions() { + return this.slavesPromoted; + } + + public long getTotalConnectionCount() { + return this.connections; + } + + public long getActiveConnectionCount() { + return this.activeConnections; + } + + @Override + public String toString() { + return "ReplicationConnectionGroup[groupName=" + this.groupName + ",masterHostList=" + this.masterHostList + ",slaveHostList=" + this.slaveHostList + + "]"; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationConnectionGroupManager.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationConnectionGroupManager.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationConnectionGroupManager.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +public class ReplicationConnectionGroupManager { + private static HashMap GROUP_MAP = new HashMap<>(); + + private static com.mysql.cj.jdbc.jmx.ReplicationGroupManager mbean = new com.mysql.cj.jdbc.jmx.ReplicationGroupManager(); + + private static boolean hasRegisteredJmx = false; + + public static synchronized ReplicationConnectionGroup getConnectionGroupInstance(String groupName) { + if (GROUP_MAP.containsKey(groupName)) { + return GROUP_MAP.get(groupName); + } + ReplicationConnectionGroup group = new ReplicationConnectionGroup(groupName); + GROUP_MAP.put(groupName, group); + return group; + } + + public static void registerJmx() throws SQLException { + if (hasRegisteredJmx) { + return; + } + + mbean.registerJmx(); + hasRegisteredJmx = true; + } + + public static ReplicationConnectionGroup getConnectionGroup(String groupName) { + return GROUP_MAP.get(groupName); + } + + public static Collection getGroupsMatching(String group) { + if (group == null || group.equals("")) { + Set s = new HashSet<>(); + + s.addAll(GROUP_MAP.values()); + return s; + } + Set s = new HashSet<>(); + ReplicationConnectionGroup o = GROUP_MAP.get(group); + if (o != null) { + s.add(o); + } + return s; + } + + public static void addSlaveHost(String group, String hostPortPair) throws SQLException { + Collection s = getGroupsMatching(group); + for (ReplicationConnectionGroup cg : s) { + cg.addSlaveHost(hostPortPair); + } + } + + public static void removeSlaveHost(String group, String hostPortPair) throws SQLException { + removeSlaveHost(group, hostPortPair, true); + } + + public static void removeSlaveHost(String group, String hostPortPair, boolean closeGently) throws SQLException { + Collection s = getGroupsMatching(group); + for (ReplicationConnectionGroup cg : s) { + cg.removeSlaveHost(hostPortPair, closeGently); + } + } + + public static void promoteSlaveToMaster(String group, String hostPortPair) throws SQLException { + Collection s = getGroupsMatching(group); + for (ReplicationConnectionGroup cg : s) { + cg.promoteSlaveToMaster(hostPortPair); + } + } + + public static long getSlavePromotionCount(String group) throws SQLException { + Collection s = getGroupsMatching(group); + long promoted = 0; + for (ReplicationConnectionGroup cg : s) { + long tmp = cg.getNumberOfSlavePromotions(); + if (tmp > promoted) { + promoted = tmp; + } + } + return promoted; + } + + public static void removeMasterHost(String group, String hostPortPair) throws SQLException { + removeMasterHost(group, hostPortPair, true); + } + + public static void removeMasterHost(String group, String hostPortPair, boolean closeGently) throws SQLException { + Collection s = getGroupsMatching(group); + for (ReplicationConnectionGroup cg : s) { + cg.removeMasterHost(hostPortPair, closeGently); + } + } + + public static String getRegisteredReplicationConnectionGroups() { + Collection s = getGroupsMatching(null); + StringBuilder sb = new StringBuilder(); + String sep = ""; + for (ReplicationConnectionGroup cg : s) { + String group = cg.getGroupName(); + sb.append(sep); + sb.append(group); + sep = ","; + } + return sb.toString(); + } + + public static int getNumberOfMasterPromotion(String groupFilter) { + int total = 0; + Collection s = getGroupsMatching(groupFilter); + for (ReplicationConnectionGroup cg : s) { + total += cg.getNumberOfSlavePromotions(); + } + return total; + } + + public static int getConnectionCountWithHostAsSlave(String groupFilter, String hostPortPair) { + int total = 0; + Collection s = getGroupsMatching(groupFilter); + for (ReplicationConnectionGroup cg : s) { + total += cg.getConnectionCountWithHostAsSlave(hostPortPair); + } + return total; + } + + public static int getConnectionCountWithHostAsMaster(String groupFilter, String hostPortPair) { + int total = 0; + Collection s = getGroupsMatching(groupFilter); + for (ReplicationConnectionGroup cg : s) { + total += cg.getConnectionCountWithHostAsMaster(hostPortPair); + } + return total; + } + + public static Collection getSlaveHosts(String groupFilter) { + Collection s = getGroupsMatching(groupFilter); + Collection hosts = new ArrayList<>(); + for (ReplicationConnectionGroup cg : s) { + hosts.addAll(cg.getSlaveHosts()); + } + return hosts; + } + + public static Collection getMasterHosts(String groupFilter) { + Collection s = getGroupsMatching(groupFilter); + Collection hosts = new ArrayList<>(); + for (ReplicationConnectionGroup cg : s) { + hosts.addAll(cg.getMasterHosts()); + } + return hosts; + } + + public static long getTotalConnectionCount(String group) { + long connections = 0; + Collection s = getGroupsMatching(group); + for (ReplicationConnectionGroup cg : s) { + connections += cg.getTotalConnectionCount(); + } + return connections; + } + + public static long getActiveConnectionCount(String group) { + long connections = 0; + Collection s = getGroupsMatching(group); + for (ReplicationConnectionGroup cg : s) { + connections += cg.getActiveConnectionCount(); + } + return connections; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationConnectionProxy.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationConnectionProxy.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationConnectionProxy.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,692 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.Executor; + +import com.mysql.cj.Messages; +import com.mysql.cj.PingTarget; +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.url.LoadbalanceConnectionUrl; +import com.mysql.cj.conf.url.ReplicationConnectionUrl; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.JdbcStatement; +import com.mysql.cj.jdbc.exceptions.SQLError; + +/** + * Connection that opens two connections, one two a replication master, and another to one or more slaves, and decides to use master when the connection is not + * read-only, and use slave(s) when the connection is read-only. + */ +public class ReplicationConnectionProxy extends MultiHostConnectionProxy implements PingTarget { + private ReplicationConnection thisAsReplicationConnection; + + protected boolean enableJMX = false; + protected boolean allowMasterDownConnections = false; + protected boolean allowSlaveDownConnections = false; + protected boolean readFromMasterWhenNoSlaves = false; + protected boolean readFromMasterWhenNoSlavesOriginal = false; + protected boolean readOnly = false; + + ReplicationConnectionGroup connectionGroup; + private long connectionGroupID = -1; + + private List masterHosts; + protected LoadBalancedConnection masterConnection; + + private List slaveHosts; + protected LoadBalancedConnection slavesConnection; + + /** + * Static factory to create {@link ReplicationConnection} instances. + * + * @param connectionUrl + * The connection URL containing the hosts in a replication setup. + * @return A {@link ReplicationConnection} proxy. + */ + public static ReplicationConnection createProxyInstance(ReplicationConnectionUrl connectionUrl) throws SQLException { + ReplicationConnectionProxy connProxy = new ReplicationConnectionProxy(connectionUrl); + return (ReplicationConnection) java.lang.reflect.Proxy.newProxyInstance(ReplicationConnection.class.getClassLoader(), + new Class[] { ReplicationConnection.class, JdbcConnection.class }, connProxy); + } + + /** + * Creates a proxy for java.sql.Connection that routes requests to a load-balanced connection of master servers or a load-balanced connection of slave + * servers. Each sub-connection is created with its own set of independent properties. + * + * @param connectionUrl + * The connection URL containing the hosts in a replication setup. + */ + private ReplicationConnectionProxy(ReplicationConnectionUrl connectionUrl) throws SQLException { + super(); + + Properties props = connectionUrl.getConnectionArgumentsAsProperties(); + + this.thisAsReplicationConnection = (ReplicationConnection) this.thisAsConnection; + + this.connectionUrl = connectionUrl; + + String enableJMXAsString = props.getProperty(PropertyDefinitions.PNAME_ha_enableJMX, "false"); + try { + this.enableJMX = Boolean.parseBoolean(enableJMXAsString); + } catch (Exception e) { + throw SQLError.createSQLException(Messages.getString("MultihostConnection.badValueForHaEnableJMX", new Object[] { enableJMXAsString }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String allowMasterDownConnectionsAsString = props.getProperty(PropertyDefinitions.PNAME_allowMasterDownConnections, "false"); + try { + this.allowMasterDownConnections = Boolean.parseBoolean(allowMasterDownConnectionsAsString); + } catch (Exception e) { + throw SQLError.createSQLException( + Messages.getString("ReplicationConnectionProxy.badValueForAllowMasterDownConnections", new Object[] { enableJMXAsString }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String allowSlaveDownConnectionsAsString = props.getProperty(PropertyDefinitions.PNAME_allowSlaveDownConnections, "false"); + try { + this.allowSlaveDownConnections = Boolean.parseBoolean(allowSlaveDownConnectionsAsString); + } catch (Exception e) { + throw SQLError.createSQLException( + Messages.getString("ReplicationConnectionProxy.badValueForAllowSlaveDownConnections", new Object[] { allowSlaveDownConnectionsAsString }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String readFromMasterWhenNoSlavesAsString = props.getProperty(PropertyDefinitions.PNAME_readFromMasterWhenNoSlaves); + try { + this.readFromMasterWhenNoSlavesOriginal = Boolean.parseBoolean(readFromMasterWhenNoSlavesAsString); + + } catch (Exception e) { + throw SQLError.createSQLException( + Messages.getString("ReplicationConnectionProxy.badValueForReadFromMasterWhenNoSlaves", new Object[] { readFromMasterWhenNoSlavesAsString }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + + String group = props.getProperty(PropertyDefinitions.PNAME_replicationConnectionGroup, null); + if (group != null) { + this.connectionGroup = ReplicationConnectionGroupManager.getConnectionGroupInstance(group); + if (this.enableJMX) { + ReplicationConnectionGroupManager.registerJmx(); + } + this.connectionGroupID = this.connectionGroup.registerReplicationConnection(this.thisAsReplicationConnection, + connectionUrl.getMastersListAsHostPortPairs(), connectionUrl.getSlavesListAsHostPortPairs()); + + this.masterHosts = connectionUrl.getMasterHostsListFromHostPortPairs(this.connectionGroup.getMasterHosts()); + this.slaveHosts = connectionUrl.getSlaveHostsListFromHostPortPairs(this.connectionGroup.getSlaveHosts()); + } else { + this.masterHosts = new ArrayList<>(connectionUrl.getMastersList()); + this.slaveHosts = new ArrayList<>(connectionUrl.getSlavesList()); + } + + resetReadFromMasterWhenNoSlaves(); + + // Initialize slaves connection first so that it is ready to be used in case the masters connection fails and 'allowMasterDownConnections=true'. + try { + initializeSlavesConnection(); + } catch (SQLException e) { + if (!this.allowSlaveDownConnections) { + if (this.connectionGroup != null) { + this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection); + } + throw e; + } // Else swallow this exception. + } + + SQLException exCaught = null; + try { + this.currentConnection = initializeMasterConnection(); + } catch (SQLException e) { + exCaught = e; + } + + if (this.currentConnection == null) { + if (this.allowMasterDownConnections && this.slavesConnection != null) { + // Set read-only and fail over to the slaves connection. + this.readOnly = true; + this.currentConnection = this.slavesConnection; + } else { + if (this.connectionGroup != null) { + this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection); + } + if (exCaught != null) { + throw exCaught; + } + throw SQLError.createSQLException(Messages.getString("ReplicationConnectionProxy.initializationWithEmptyHostsLists"), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, null); + } + } + } + + /** + * Wraps this object with a new replication Connection instance. + * + * @return + * The connection object instance that wraps 'this'. + */ + @Override + JdbcConnection getNewWrapperForThisAsConnection() throws SQLException { + return new ReplicationMySQLConnection(this); + } + + /** + * Propagates the connection proxy down through all live connections. + * + * @param proxyConn + * The top level connection in the multi-host connections chain. + */ + @Override + protected void propagateProxyDown(JdbcConnection proxyConn) { + if (this.masterConnection != null) { + this.masterConnection.setProxy(proxyConn); + } + if (this.slavesConnection != null) { + this.slavesConnection.setProxy(proxyConn); + } + } + + /** + * Has no use in replication connections. Always return false. + * + * @param ex + * The Exception instance to check. + */ + @Override + boolean shouldExceptionTriggerConnectionSwitch(Throwable t) { + return false; + } + + /** + * Checks if current connection is the masters l/b connection. + */ + @Override + public boolean isMasterConnection() { + return this.currentConnection != null && this.currentConnection == this.masterConnection; + } + + /** + * Checks if current connection is the slaves l/b connection. + */ + public boolean isSlavesConnection() { + return this.currentConnection != null && this.currentConnection == this.slavesConnection; + } + + @Override + void pickNewConnection() throws SQLException { + // no-op + } + + @Override + void syncSessionState(JdbcConnection source, JdbcConnection target, boolean readonly) throws SQLException { + try { + super.syncSessionState(source, target, readonly); + } catch (SQLException e1) { + try { + // Try again. It may happen that the connection had recovered in the meantime but the right syncing wasn't done yet. + super.syncSessionState(source, target, readonly); + } catch (SQLException e2) { + } + // Swallow both exceptions. Replication connections must continue to "work" after swapping between masters and slaves. + } + } + + @Override + void doClose() throws SQLException { + if (this.masterConnection != null) { + this.masterConnection.close(); + } + if (this.slavesConnection != null) { + this.slavesConnection.close(); + } + + if (this.connectionGroup != null) { + this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection); + } + } + + @Override + void doAbortInternal() throws SQLException { + this.masterConnection.abortInternal(); + this.slavesConnection.abortInternal(); + if (this.connectionGroup != null) { + this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection); + } + } + + @Override + void doAbort(Executor executor) throws SQLException { + this.masterConnection.abort(executor); + this.slavesConnection.abort(executor); + if (this.connectionGroup != null) { + this.connectionGroup.handleCloseConnection(this.thisAsReplicationConnection); + } + } + + /** + * Proxies method invocation on the java.sql.Connection interface. + * This is the continuation of MultiHostConnectionProxy#invoke(Object, Method, Object[]). + */ + @Override + Object invokeMore(Object proxy, Method method, Object[] args) throws Throwable { + checkConnectionCapabilityForMethod(method); + + boolean invokeAgain = false; + while (true) { + try { + Object result = method.invoke(this.thisAsConnection, args); + if (result != null && result instanceof JdbcStatement) { + ((JdbcStatement) result).setPingTarget(this); + } + return result; + } catch (InvocationTargetException e) { + if (invokeAgain) { + invokeAgain = false; + } else if (e.getCause() != null && e.getCause() instanceof SQLException + && ((SQLException) e.getCause()).getSQLState() == MysqlErrorNumbers.SQL_STATE_INVALID_TRANSACTION_STATE + && ((SQLException) e.getCause()).getErrorCode() == MysqlErrorNumbers.ERROR_CODE_NULL_LOAD_BALANCED_CONNECTION) { + try { + // Try to re-establish the connection with the last known read-only state. + setReadOnly(this.readOnly); + invokeAgain = true; + } catch (SQLException sqlEx) { + // Still not good. Swallow this exception. + } + } + if (!invokeAgain) { + throw e; + } + } + } + } + + /** + * Checks if this connection is in a state capable to invoke the provided method. If the connection is in an inconsistent state, i.e. it has no hosts for + * both sub-connections, then throw an invalid transaction state exception. Nevertheless, the methods defined in the ReplicationConnection interface will be + * allowed as they are the only way to leave from an empty hosts lists situation. + */ + private void checkConnectionCapabilityForMethod(Method method) throws Throwable { + if (this.masterHosts.isEmpty() && this.slaveHosts.isEmpty() && !ReplicationConnection.class.isAssignableFrom(method.getDeclaringClass())) { + throw SQLError.createSQLException(Messages.getString("ReplicationConnectionProxy.noHostsInconsistentState"), + MysqlErrorNumbers.SQL_STATE_INVALID_TRANSACTION_STATE, MysqlErrorNumbers.ERROR_CODE_REPLICATION_CONNECTION_WITH_NO_HOSTS, true, null); + } + } + + /** + * Pings both l/b connections. Switch to another connection in case of failure. + */ + public void doPing() throws SQLException { + boolean isMasterConn = isMasterConnection(); + + SQLException mastersPingException = null; + SQLException slavesPingException = null; + + if (this.masterConnection != null) { + try { + this.masterConnection.ping(); + } catch (SQLException e) { + mastersPingException = e; + } + } else { + initializeMasterConnection(); + } + + if (this.slavesConnection != null) { + try { + this.slavesConnection.ping(); + } catch (SQLException e) { + slavesPingException = e; + } + } else { + try { + initializeSlavesConnection(); + if (switchToSlavesConnectionIfNecessary()) { + isMasterConn = false; + } + } catch (SQLException e) { + if (this.masterConnection == null || !this.readFromMasterWhenNoSlaves) { + throw e; + } // Else swallow this exception. + } + } + + if (isMasterConn && mastersPingException != null) { + // Switch to slaves connection. + if (this.slavesConnection != null && slavesPingException == null) { + this.masterConnection = null; + this.currentConnection = this.slavesConnection; + this.readOnly = true; + } + throw mastersPingException; + + } else if (!isMasterConn && (slavesPingException != null || this.slavesConnection == null)) { + // Switch to masters connection, setting read-only state, if 'readFromMasterWhenNoSlaves=true'. + if (this.masterConnection != null && this.readFromMasterWhenNoSlaves && mastersPingException == null) { + this.slavesConnection = null; + this.currentConnection = this.masterConnection; + this.readOnly = true; + this.currentConnection.setReadOnly(true); + } + if (slavesPingException != null) { + throw slavesPingException; + } + } + } + + private JdbcConnection initializeMasterConnection() throws SQLException { + this.masterConnection = null; + + if (this.masterHosts.size() == 0) { + return null; + } + + LoadBalancedConnection newMasterConn = LoadBalancedConnectionProxy + .createProxyInstance(new LoadbalanceConnectionUrl(this.masterHosts, this.connectionUrl.getOriginalProperties())); + newMasterConn.setProxy(getProxy()); + + this.masterConnection = newMasterConn; + return this.masterConnection; + } + + private JdbcConnection initializeSlavesConnection() throws SQLException { + this.slavesConnection = null; + + if (this.slaveHosts.size() == 0) { + return null; + } + + LoadBalancedConnection newSlavesConn = LoadBalancedConnectionProxy + .createProxyInstance(new LoadbalanceConnectionUrl(this.slaveHosts, this.connectionUrl.getOriginalProperties())); + newSlavesConn.setProxy(getProxy()); + newSlavesConn.setReadOnly(true); + + this.slavesConnection = newSlavesConn; + return this.slavesConnection; + } + + private synchronized boolean switchToMasterConnection() throws SQLException { + if (this.masterConnection == null || this.masterConnection.isClosed()) { + try { + if (initializeMasterConnection() == null) { + return false; + } + } catch (SQLException e) { + this.currentConnection = null; + throw e; + } + } + if (!isMasterConnection() && this.masterConnection != null) { + syncSessionState(this.currentConnection, this.masterConnection, false); + this.currentConnection = this.masterConnection; + } + return true; + } + + private synchronized boolean switchToSlavesConnection() throws SQLException { + if (this.slavesConnection == null || this.slavesConnection.isClosed()) { + try { + if (initializeSlavesConnection() == null) { + return false; + } + } catch (SQLException e) { + this.currentConnection = null; + throw e; + } + } + if (!isSlavesConnection() && this.slavesConnection != null) { + syncSessionState(this.currentConnection, this.slavesConnection, true); + this.currentConnection = this.slavesConnection; + } + return true; + } + + private boolean switchToSlavesConnectionIfNecessary() throws SQLException { + // Switch to slaves connection: + // - If the current connection is null. Or, + // - If we're currently on the master and in read-only mode - we didn't have any slaves to use until now. Or, + // - If we're currently on a closed master connection and there are no masters to connect to. Or, + // - If we're currently not on a master connection that is closed - means that we were on a closed slaves connection before it was re-initialized. + if (this.currentConnection == null || isMasterConnection() && (this.readOnly || this.masterHosts.isEmpty() && this.currentConnection.isClosed()) + || !isMasterConnection() && this.currentConnection.isClosed()) { + return switchToSlavesConnection(); + } + return false; + } + + public synchronized JdbcConnection getCurrentConnection() { + return this.currentConnection == null ? LoadBalancedConnectionProxy.getNullLoadBalancedConnectionInstance() : this.currentConnection; + } + + public long getConnectionGroupId() { + return this.connectionGroupID; + } + + public synchronized JdbcConnection getMasterConnection() { + return this.masterConnection; + } + + public synchronized void promoteSlaveToMaster(String hostPortPair) throws SQLException { + HostInfo host = getSlaveHost(hostPortPair); + if (host == null) { + return; + } + this.masterHosts.add(host); + removeSlave(hostPortPair); + if (this.masterConnection != null) { + this.masterConnection.addHost(hostPortPair); + } + + // Switch back to the masters connection if this connection was running in fail-safe mode. + if (!this.readOnly && !isMasterConnection()) { + switchToMasterConnection(); + } + } + + public synchronized void removeMasterHost(String hostPortPair) throws SQLException { + this.removeMasterHost(hostPortPair, true); + } + + public synchronized void removeMasterHost(String hostPortPair, boolean waitUntilNotInUse) throws SQLException { + this.removeMasterHost(hostPortPair, waitUntilNotInUse, false); + } + + public synchronized void removeMasterHost(String hostPortPair, boolean waitUntilNotInUse, boolean isNowSlave) throws SQLException { + HostInfo host = getMasterHost(hostPortPair); + if (host == null) { + return; + } + if (isNowSlave) { + this.slaveHosts.add(host); + resetReadFromMasterWhenNoSlaves(); + } + this.masterHosts.remove(host); + + // The master connection may have been implicitly closed by a previous op., don't let it stop us. + if (this.masterConnection == null || this.masterConnection.isClosed()) { + this.masterConnection = null; + return; + } + + if (waitUntilNotInUse) { + this.masterConnection.removeHostWhenNotInUse(hostPortPair); + } else { + this.masterConnection.removeHost(hostPortPair); + } + + // Close the connection if that was the last master. + if (this.masterHosts.isEmpty()) { + this.masterConnection.close(); + this.masterConnection = null; + + // Default behavior, no need to check this.readFromMasterWhenNoSlaves. + switchToSlavesConnectionIfNecessary(); + } + } + + public boolean isHostMaster(String hostPortPair) { + if (hostPortPair == null) { + return false; + } + return this.masterHosts.stream().anyMatch(hi -> hostPortPair.equalsIgnoreCase(hi.getHostPortPair())); + } + + public synchronized JdbcConnection getSlavesConnection() { + return this.slavesConnection; + } + + public synchronized void addSlaveHost(String hostPortPair) throws SQLException { + if (this.isHostSlave(hostPortPair)) { + return; + } + this.slaveHosts.add(getConnectionUrl().getSlaveHostOrSpawnIsolated(hostPortPair)); + resetReadFromMasterWhenNoSlaves(); + if (this.slavesConnection == null) { + initializeSlavesConnection(); + switchToSlavesConnectionIfNecessary(); + } else { + this.slavesConnection.addHost(hostPortPair); + } + } + + public synchronized void removeSlave(String hostPortPair) throws SQLException { + removeSlave(hostPortPair, true); + } + + public synchronized void removeSlave(String hostPortPair, boolean closeGently) throws SQLException { + HostInfo host = getSlaveHost(hostPortPair); + if (host == null) { + return; + } + this.slaveHosts.remove(host); + resetReadFromMasterWhenNoSlaves(); + + if (this.slavesConnection == null || this.slavesConnection.isClosed()) { + this.slavesConnection = null; + return; + } + + if (closeGently) { + this.slavesConnection.removeHostWhenNotInUse(hostPortPair); + } else { + this.slavesConnection.removeHost(hostPortPair); + } + + // Close the connection if that was the last slave. + if (this.slaveHosts.isEmpty()) { + this.slavesConnection.close(); + this.slavesConnection = null; + + // Default behavior, no need to check this.readFromMasterWhenNoSlaves. + switchToMasterConnection(); + if (isMasterConnection()) { + this.currentConnection.setReadOnly(this.readOnly); // Maintain. + } + } + } + + public boolean isHostSlave(String hostPortPair) { + if (hostPortPair == null) { + return false; + } + return this.slaveHosts.stream().anyMatch(hi -> hostPortPair.equalsIgnoreCase(hi.getHostPortPair())); + } + + public synchronized void setReadOnly(boolean readOnly) throws SQLException { + if (readOnly) { + if (!isSlavesConnection() || this.currentConnection.isClosed()) { + boolean switched = true; + SQLException exceptionCaught = null; + try { + switched = switchToSlavesConnection(); + } catch (SQLException e) { + switched = false; + exceptionCaught = e; + } + if (!switched && this.readFromMasterWhenNoSlaves && switchToMasterConnection()) { + exceptionCaught = null; // The connection is OK. Cancel the exception, if any. + } + if (exceptionCaught != null) { + throw exceptionCaught; + } + } + } else { + if (!isMasterConnection() || this.currentConnection.isClosed()) { + boolean switched = true; + SQLException exceptionCaught = null; + try { + switched = switchToMasterConnection(); + } catch (SQLException e) { + switched = false; + exceptionCaught = e; + } + if (!switched && switchToSlavesConnectionIfNecessary()) { + exceptionCaught = null; // The connection is OK. Cancel the exception, if any. + } + if (exceptionCaught != null) { + throw exceptionCaught; + } + } + } + this.readOnly = readOnly; + + /* + * Reset masters connection read-only state if 'readFromMasterWhenNoSlaves=true'. If there are no slaves then the masters connection will be used with + * read-only state in its place. Even if not, it must be reset from a possible previous read-only state. + */ + if (this.readFromMasterWhenNoSlaves && isMasterConnection()) { + this.currentConnection.setReadOnly(this.readOnly); + } + } + + public boolean isReadOnly() throws SQLException { + return !isMasterConnection() || this.readOnly; + } + + private void resetReadFromMasterWhenNoSlaves() { + this.readFromMasterWhenNoSlaves = this.slaveHosts.isEmpty() || this.readFromMasterWhenNoSlavesOriginal; + } + + private HostInfo getMasterHost(String hostPortPair) { + return this.masterHosts.stream().filter(hi -> hostPortPair.equalsIgnoreCase(hi.getHostPortPair())).findFirst().orElse(null); + } + + private HostInfo getSlaveHost(String hostPortPair) { + return this.slaveHosts.stream().filter(hi -> hostPortPair.equalsIgnoreCase(hi.getHostPortPair())).findFirst().orElse(null); + } + + private ReplicationConnectionUrl getConnectionUrl() { + return (ReplicationConnectionUrl) this.connectionUrl; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationMySQLConnection.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationMySQLConnection.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ReplicationMySQLConnection.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.sql.SQLException; +import java.util.Properties; +import java.util.concurrent.Executor; + +import com.mysql.cj.Messages; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.exceptions.SQLError; + +public class ReplicationMySQLConnection extends MultiHostMySQLConnection implements ReplicationConnection { + public ReplicationMySQLConnection(MultiHostConnectionProxy proxy) { + super(proxy); + } + + @Override + public ReplicationConnectionProxy getThisAsProxy() { + return (ReplicationConnectionProxy) super.getThisAsProxy(); + } + + @Override + public JdbcConnection getActiveMySQLConnection() { + return getCurrentConnection(); + } + + public synchronized JdbcConnection getCurrentConnection() { + return getThisAsProxy().getCurrentConnection(); + } + + public long getConnectionGroupId() { + return getThisAsProxy().getConnectionGroupId(); + } + + public synchronized JdbcConnection getMasterConnection() { + return getThisAsProxy().getMasterConnection(); + } + + private JdbcConnection getValidatedMasterConnection() { + JdbcConnection conn = getThisAsProxy().masterConnection; + try { + return conn == null || conn.isClosed() ? null : conn; + } catch (SQLException e) { + return null; + } + } + + public void promoteSlaveToMaster(String host) throws SQLException { + getThisAsProxy().promoteSlaveToMaster(host); + } + + public void removeMasterHost(String host) throws SQLException { + getThisAsProxy().removeMasterHost(host); + } + + public void removeMasterHost(String host, boolean waitUntilNotInUse) throws SQLException { + getThisAsProxy().removeMasterHost(host, waitUntilNotInUse); + } + + public boolean isHostMaster(String host) { + return getThisAsProxy().isHostMaster(host); + } + + public synchronized JdbcConnection getSlavesConnection() { + return getThisAsProxy().getSlavesConnection(); + } + + private JdbcConnection getValidatedSlavesConnection() { + JdbcConnection conn = getThisAsProxy().slavesConnection; + try { + return conn == null || conn.isClosed() ? null : conn; + } catch (SQLException e) { + return null; + } + } + + public void addSlaveHost(String host) throws SQLException { + getThisAsProxy().addSlaveHost(host); + } + + public void removeSlave(String host) throws SQLException { + getThisAsProxy().removeSlave(host); + } + + public void removeSlave(String host, boolean closeGently) throws SQLException { + getThisAsProxy().removeSlave(host, closeGently); + } + + public boolean isHostSlave(String host) { + return getThisAsProxy().isHostSlave(host); + } + + @Override + public void setReadOnly(boolean readOnlyFlag) throws SQLException { + getThisAsProxy().setReadOnly(readOnlyFlag); + } + + @Override + public boolean isReadOnly() throws SQLException { + return getThisAsProxy().isReadOnly(); + } + + @Override + public synchronized void ping() throws SQLException { + JdbcConnection conn; + try { + if ((conn = getValidatedMasterConnection()) != null) { + conn.ping(); + } + } catch (SQLException e) { + if (isMasterConnection()) { + throw e; + } + } + try { + if ((conn = getValidatedSlavesConnection()) != null) { + conn.ping(); + } + } catch (SQLException e) { + if (!isMasterConnection()) { + throw e; + } + } + } + + @Override + public synchronized void changeUser(String userName, String newPassword) throws SQLException { + JdbcConnection conn; + if ((conn = getValidatedMasterConnection()) != null) { + conn.changeUser(userName, newPassword); + } + if ((conn = getValidatedSlavesConnection()) != null) { + conn.changeUser(userName, newPassword); + } + } + + @Override + public synchronized void setStatementComment(String comment) { + JdbcConnection conn; + if ((conn = getValidatedMasterConnection()) != null) { + conn.setStatementComment(comment); + } + if ((conn = getValidatedSlavesConnection()) != null) { + conn.setStatementComment(comment); + } + } + + @Override + public boolean hasSameProperties(JdbcConnection c) { + JdbcConnection connM = getValidatedMasterConnection(); + JdbcConnection connS = getValidatedSlavesConnection(); + if (connM == null && connS == null) { + return false; + } + return (connM == null || connM.hasSameProperties(c)) && (connS == null || connS.hasSameProperties(c)); + } + + @Override + public Properties getProperties() { + Properties props = new Properties(); + JdbcConnection conn; + if ((conn = getValidatedMasterConnection()) != null) { + props.putAll(conn.getProperties()); + } + if ((conn = getValidatedSlavesConnection()) != null) { + props.putAll(conn.getProperties()); + } + + return props; + } + + @Override + public void abort(Executor executor) throws SQLException { + getThisAsProxy().doAbort(executor); + } + + @Override + public void abortInternal() throws SQLException { + getThisAsProxy().doAbortInternal(); + } + + @Override + public void setProxy(JdbcConnection proxy) { + getThisAsProxy().setProxy(proxy); + } + + /* + * public void setProxy(JdbcConnection proxy) { + * this.proxy = proxy; + * if (this.masterConnection != null) { + * this.masterConnection.setProxy(proxy); + * } + * if (this.slavesConnection != null) { + * this.slavesConnection.setProxy(proxy); + * } + * } + */ + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + // This works for classes that aren't actually wrapping anything + return iface.isInstance(this); + } + + @Override + public T unwrap(java.lang.Class iface) throws java.sql.SQLException { + try { + // This works for classes that aren't actually wrapping anything + return iface.cast(this); + } catch (ClassCastException cce) { + throw SQLError.createSQLException(Messages.getString("Common.UnableToUnwrap", new Object[] { iface.toString() }), + MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); + } + } + + @Deprecated + @Override + public synchronized void clearHasTriedMaster() { + getThisAsProxy().masterConnection.clearHasTriedMaster(); + getThisAsProxy().slavesConnection.clearHasTriedMaster(); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/SequentialBalanceStrategy.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/SequentialBalanceStrategy.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/SequentialBalanceStrategy.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.lang.reflect.InvocationHandler; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +import com.mysql.cj.jdbc.ConnectionImpl; +import com.mysql.cj.jdbc.JdbcConnection; + +/** + * A balancing strategy that starts at a random point, and then advances in the list (wrapping around) for each new pickConnection() call. + * + * The initial point selection, and subsequent point selections are blacklist-aware. + */ +public class SequentialBalanceStrategy implements BalanceStrategy { + private int currentHostIndex = -1; + + public SequentialBalanceStrategy() { + } + + public ConnectionImpl pickConnection(InvocationHandler proxy, List configuredHosts, Map liveConnections, + long[] responseTimes, int numRetries) throws SQLException { + int numHosts = configuredHosts.size(); + + SQLException ex = null; + + Map blackList = ((LoadBalancedConnectionProxy) proxy).getGlobalBlacklist(); + + for (int attempts = 0; attempts < numRetries;) { + if (numHosts == 1) { + this.currentHostIndex = 0; // pathological case + } else if (this.currentHostIndex == -1) { + int random = (int) Math.floor((Math.random() * numHosts)); + + for (int i = random; i < numHosts; i++) { + if (!blackList.containsKey(configuredHosts.get(i))) { + this.currentHostIndex = i; + break; + } + } + + if (this.currentHostIndex == -1) { + for (int i = 0; i < random; i++) { + if (!blackList.containsKey(configuredHosts.get(i))) { + this.currentHostIndex = i; + break; + } + } + } + + if (this.currentHostIndex == -1) { + blackList = ((LoadBalancedConnectionProxy) proxy).getGlobalBlacklist(); // it may have changed + // and the proxy returns a copy + + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + + continue; // retry + } + } else { + + int i = this.currentHostIndex + 1; + boolean foundGoodHost = false; + + for (; i < numHosts; i++) { + if (!blackList.containsKey(configuredHosts.get(i))) { + this.currentHostIndex = i; + foundGoodHost = true; + break; + } + } + + if (!foundGoodHost) { + for (i = 0; i < this.currentHostIndex; i++) { + if (!blackList.containsKey(configuredHosts.get(i))) { + this.currentHostIndex = i; + foundGoodHost = true; + break; + } + } + } + + if (!foundGoodHost) { + blackList = ((LoadBalancedConnectionProxy) proxy).getGlobalBlacklist(); // it may have changed + // and the proxy returns a copy + + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + + continue; // retry + } + } + + String hostPortSpec = configuredHosts.get(this.currentHostIndex); + + ConnectionImpl conn = (ConnectionImpl) liveConnections.get(hostPortSpec); + + if (conn == null) { + try { + conn = ((LoadBalancedConnectionProxy) proxy).createConnectionForHost(hostPortSpec); + } catch (SQLException sqlEx) { + ex = sqlEx; + + if (((LoadBalancedConnectionProxy) proxy).shouldExceptionTriggerConnectionSwitch(sqlEx)) { + + ((LoadBalancedConnectionProxy) proxy).addToGlobalBlacklist(hostPortSpec); + + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + + continue; + } + throw sqlEx; + } + } + + return conn; + } + + if (ex != null) { + throw ex; + } + + return null; // we won't get here, compiler can't tell + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ServerAffinityStrategy.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ServerAffinityStrategy.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/ServerAffinityStrategy.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.lang.reflect.InvocationHandler; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +import com.mysql.cj.jdbc.ConnectionImpl; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.util.StringUtils; + +public class ServerAffinityStrategy extends RandomBalanceStrategy { + public String[] affinityOrderedServers = null; + + public ServerAffinityStrategy(String affinityOrdervers) { + super(); + if (!StringUtils.isNullOrEmpty(affinityOrdervers)) { + this.affinityOrderedServers = affinityOrdervers.split(","); + } + } + + @Override + public ConnectionImpl pickConnection(InvocationHandler proxy, List configuredHosts, Map liveConnections, + long[] responseTimes, int numRetries) throws SQLException { + if (this.affinityOrderedServers == null) { + return super.pickConnection(proxy, configuredHosts, liveConnections, responseTimes, numRetries); + } + Map blackList = ((LoadBalancedConnectionProxy) proxy).getGlobalBlacklist(); + + for (String host : this.affinityOrderedServers) { + if (configuredHosts.contains(host) && !blackList.containsKey(host)) { + ConnectionImpl conn = (ConnectionImpl) liveConnections.get(host); + if (conn != null) { + return conn; + } + try { + conn = ((LoadBalancedConnectionProxy) proxy).createConnectionForHost(host); + return conn; + } catch (SQLException sqlEx) { + if (((LoadBalancedConnectionProxy) proxy).shouldExceptionTriggerConnectionSwitch(sqlEx)) { + ((LoadBalancedConnectionProxy) proxy).addToGlobalBlacklist(host); + } + } + } + } + + // Failed to connect to all hosts in the affinity list. Delegate to RandomBalanceStrategy. + return super.pickConnection(proxy, configuredHosts, liveConnections, responseTimes, numRetries); + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/StandardLoadBalanceExceptionChecker.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/StandardLoadBalanceExceptionChecker.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/ha/StandardLoadBalanceExceptionChecker.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.ha; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.CJCommunicationsException; +import com.mysql.cj.jdbc.exceptions.CommunicationsException; +import com.mysql.cj.util.StringUtils; + +public class StandardLoadBalanceExceptionChecker implements LoadBalanceExceptionChecker { + + private List sqlStateList; + private List> sqlExClassList; + + public boolean shouldExceptionTriggerFailover(Throwable ex) { + String sqlState = ex instanceof SQLException ? ((SQLException) ex).getSQLState() : null; + + if (sqlState != null) { + if (sqlState.startsWith("08")) { + // connection error + return true; + } + if (this.sqlStateList != null) { + // check against SQLState list + for (Iterator i = this.sqlStateList.iterator(); i.hasNext();) { + if (sqlState.startsWith(i.next().toString())) { + return true; + } + } + } + } + + // always handle CommunicationException + if (ex instanceof CommunicationsException || ex instanceof CJCommunicationsException) { + return true; + } + + if (this.sqlExClassList != null) { + // check against configured class lists + for (Iterator> i = this.sqlExClassList.iterator(); i.hasNext();) { + if (i.next().isInstance(ex)) { + return true; + } + } + } + // no matches + return false; + } + + public void destroy() { + } + + public void init(Properties props) { + configureSQLStateList(props.getProperty(PropertyDefinitions.PNAME_loadBalanceSQLStateFailover, null)); + configureSQLExceptionSubclassList(props.getProperty(PropertyDefinitions.PNAME_loadBalanceSQLExceptionSubclassFailover, null)); + } + + private void configureSQLStateList(String sqlStates) { + if (sqlStates == null || "".equals(sqlStates)) { + return; + } + List states = StringUtils.split(sqlStates, ",", true); + List newStates = new ArrayList<>(); + + for (String state : states) { + if (state.length() > 0) { + newStates.add(state); + } + } + if (newStates.size() > 0) { + this.sqlStateList = newStates; + } + } + + private void configureSQLExceptionSubclassList(String sqlExClasses) { + if (sqlExClasses == null || "".equals(sqlExClasses)) { + return; + } + List classes = StringUtils.split(sqlExClasses, ",", true); + List> newClasses = new ArrayList<>(); + + for (String exClass : classes) { + try { + Class c = Class.forName(exClass); + newClasses.add(c); + } catch (Exception e) { + // ignore and don't check, class doesn't exist + } + } + if (newClasses.size() > 0) { + this.sqlExClassList = newClasses; + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/integration/c3p0/MysqlConnectionTester.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/integration/c3p0/MysqlConnectionTester.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/integration/c3p0/MysqlConnectionTester.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.integration.c3p0; + +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import com.mchange.v2.c3p0.C3P0ProxyConnection; +import com.mchange.v2.c3p0.QueryConnectionTester; +import com.mysql.cj.exceptions.CJCommunicationsException; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.exceptions.CommunicationsException; + +/** + * ConnectionTester for C3P0 connection pool that uses the more efficient COM_PING method of testing connection 'liveness' for MySQL, and 'sorts' exceptions + * based on SQLState or class of 'CommunicationsException' for handling exceptions. + */ +public final class MysqlConnectionTester implements QueryConnectionTester { + + private static final long serialVersionUID = 3256444690067896368L; + + private static final Object[] NO_ARGS_ARRAY = new Object[0]; + + private transient Method pingMethod; + + public MysqlConnectionTester() { + try { + this.pingMethod = JdbcConnection.class.getMethod("ping", (Class[]) null); + } catch (Exception ex) { + // punt, we have no way to recover, other than we now use 'SELECT 1' for handling the connection testing. + } + } + + public int activeCheckConnection(Connection con) { + try { + if (this.pingMethod != null) { + if (con instanceof JdbcConnection) { + // We've been passed an instance of a MySQL connection -- no need for reflection + ((JdbcConnection) con).ping(); + } else { + // Assume the connection is a C3P0 proxy + C3P0ProxyConnection castCon = (C3P0ProxyConnection) con; + castCon.rawConnectionOperation(this.pingMethod, C3P0ProxyConnection.RAW_CONNECTION, NO_ARGS_ARRAY); + } + } else { + Statement pingStatement = null; + + try { + pingStatement = con.createStatement(); + pingStatement.executeQuery("SELECT 1").close(); + } finally { + if (pingStatement != null) { + pingStatement.close(); + } + } + } + + return CONNECTION_IS_OKAY; + } catch (Exception ex) { + return CONNECTION_IS_INVALID; + } + } + + public int statusOnException(Connection arg0, Throwable throwable) { + if (throwable instanceof CommunicationsException || throwable instanceof CJCommunicationsException) { + return CONNECTION_IS_INVALID; + } + + if (throwable instanceof SQLException) { + String sqlState = ((SQLException) throwable).getSQLState(); + + if (sqlState != null && sqlState.startsWith("08")) { + return CONNECTION_IS_INVALID; + } + + return CONNECTION_IS_OKAY; + } + + // Runtime/Unchecked? + + return CONNECTION_IS_INVALID; + } + + public int activeCheckConnection(Connection arg0, String arg1) { + return CONNECTION_IS_OKAY; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.integration.jboss; + +import java.sql.SQLException; + +import org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter; + +/** + * Exception sorter used for JBoss to make recovery of downed/stale connections work more consistently. + */ +public final class ExtendedMysqlExceptionSorter extends MySQLExceptionSorter { + + static final long serialVersionUID = -2454582336945931069L; + + @Override + public boolean isExceptionFatal(SQLException ex) { + String sqlState = ex.getSQLState(); + + if (sqlState != null && sqlState.startsWith("08")) { + return true; + } + + return super.isExceptionFatal(ex); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/integration/jboss/MysqlValidConnectionChecker.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/integration/jboss/MysqlValidConnectionChecker.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/integration/jboss/MysqlValidConnectionChecker.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.integration.jboss; + +import java.io.Serializable; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import org.jboss.resource.adapter.jdbc.ValidConnectionChecker; + +/** + * A more efficient connection checker for JBoss. + */ +public final class MysqlValidConnectionChecker implements ValidConnectionChecker, Serializable { + + private static final long serialVersionUID = 8909421133577519177L; + + public MysqlValidConnectionChecker() { + } + + public SQLException isValidConnection(Connection conn) { + + // Use "/* ping */ SELECT 1" which will send pings across multi-connections too in case the connection was "wrapped" by Jboss in any way... + + Statement pingStatement = null; + + try { + pingStatement = conn.createStatement(); + + pingStatement.executeQuery("/* ping */ SELECT 1").close(); + + return null; + } catch (SQLException sqlEx) { + return sqlEx; + } finally { + if (pingStatement != null) { + try { + pingStatement.close(); + } catch (SQLException sqlEx) { + // can't do anything about it here + } + } + } + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/interceptors/ConnectionLifecycleInterceptor.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/interceptors/ConnectionLifecycleInterceptor.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/interceptors/ConnectionLifecycleInterceptor.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.interceptors; + +import java.sql.SQLException; +import java.sql.Savepoint; +import java.util.Properties; + +import com.mysql.cj.MysqlConnection; +import com.mysql.cj.log.Log; + +/** + * Implementors of this interface can be installed via the "connectionLifecycleInterceptors" configuration property and receive events and alter behavior of + * "lifecycle" methods on our connection implementation. + * + * The driver will create one instance of a given interceptor per-connection. + */ +public interface ConnectionLifecycleInterceptor { + /** + * Called once per connection that wants to use the extension + * + * The properties are the same ones passed in in the URL or arguments to + * Driver.connect() or DriverManager.getConnection(). + * + * @param conn + * the connection for which this extension is being created + * @param props + * configuration values as passed to the connection. Note that + * in order to support javax.sql.DataSources, configuration properties specific + * to an interceptor must be passed via setURL() on the + * DataSource. Extension properties are not exposed via + * accessor/mutator methods on DataSources. + * @param log + * logger instance + * @return interceptor + */ + ConnectionLifecycleInterceptor init(MysqlConnection conn, Properties props, Log log); + + /** + * Called by the driver when this extension should release any resources + * it is holding and cleanup internally before the connection is + * closed. + */ + void destroy(); + + /** + * Called when an application calls Connection.close(), before the driver + * processes its own internal logic for close. + * + * @throws SQLException + * if an error occurs + */ + void close() throws SQLException; + + /** + * Called when an application calls Connection.commit(), before the + * driver processes its own internal logic for commit(). + * + * Interceptors should return "true" if the driver should perform + * its own internal logic for commit(), or "false" if not. + * + * @return "true" if the driver should perform + * its own internal logic for commit(), or "false" if not. + * + * @throws SQLException + * if an error occurs + */ + boolean commit() throws SQLException; + + /** + * Called when an application calls Connection.rollback(), before the + * driver processes its own internal logic for rollback(). + * + * Interceptors should return "true" if the driver should perform + * its own internal logic for rollback(), or "false" if not. + * + * @return "true" if the driver should perform + * its own internal logic for rollback(), or "false" if not. + * + * @throws SQLException + * if an error occurs + */ + boolean rollback() throws SQLException; + + /** + * Called when an application calls Connection.rollback(), before the + * driver processes its own internal logic for rollback(). + * + * Interceptors should return "true" if the driver should perform + * its own internal logic for rollback(), or "false" if not. + * + * @param s + * savepoint + * @return "true" if the driver should perform + * its own internal logic for rollback(), or "false" if not. + * + * @throws SQLException + * if an error occurs + */ + boolean rollback(Savepoint s) throws SQLException; + + /** + * Called when an application calls Connection.setAutoCommit(), before the + * driver processes its own internal logic for setAutoCommit(). + * + * Interceptors should return "true" if the driver should perform + * its own internal logic for setAutoCommit(), or "false" if not. + * + * @param flag + * autocommit flag + * @return "true" if the driver should perform + * its own internal logic for setAutoCommit(), or "false" if not. + * + * @throws SQLException + * if an error occurs + */ + boolean setAutoCommit(boolean flag) throws SQLException; + + /** + * Called when an application calls Connection.setCatalog(), before the + * driver processes its own internal logic for setCatalog(). + * + * Interceptors should return "true" if the driver should perform + * its own internal logic for setCatalog(), or "false" if not. + * + * @param catalog + * catalog name + * @return "true" if the driver should perform + * its own internal logic for setCatalog(), or "false" if not. + * + * @throws SQLException + * if an error occurs + */ + boolean setCatalog(String catalog) throws SQLException; + + /** + * Called when the driver has been told by the server that a transaction + * is now in progress (when one has not been currently in progress). + * + * @return true if transaction is in progress + */ + boolean transactionBegun(); + + /** + * Called when the driver has been told by the server that a transaction + * has completed, and no transaction is currently in progress. + * + * @return true if transaction is completed + */ + boolean transactionCompleted(); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/interceptors/ResultSetScannerInterceptor.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/interceptors/ResultSetScannerInterceptor.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/interceptors/ResultSetScannerInterceptor.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.interceptors; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.SQLException; +import java.util.Properties; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.mysql.cj.Messages; +import com.mysql.cj.MysqlConnection; +import com.mysql.cj.Query; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.interceptors.QueryInterceptor; +import com.mysql.cj.jdbc.result.ResultSetInternalMethods; +import com.mysql.cj.log.Log; +import com.mysql.cj.protocol.Resultset; +import com.mysql.cj.protocol.ServerSession; + +public class ResultSetScannerInterceptor implements QueryInterceptor { + + protected Pattern regexP; + + public QueryInterceptor init(MysqlConnection conn, Properties props, Log log) { + String regexFromUser = props.getProperty(PropertyDefinitions.PNAME_resultSetScannerRegex); + + if (regexFromUser == null || regexFromUser.length() == 0) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ResultSetScannerInterceptor.0")); + } + + try { + this.regexP = Pattern.compile(regexFromUser); + } catch (Throwable t) { + throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("ResultSetScannerInterceptor.1"), t); + } + return this; + + } + + @SuppressWarnings("unchecked") + @Override + public T postProcess(Supplier sql, Query interceptedQuery, T originalResultSet, ServerSession serverSession) { + + // requirement of anonymous class + final T finalResultSet = originalResultSet; + + return (T) Proxy.newProxyInstance(originalResultSet.getClass().getClassLoader(), new Class[] { Resultset.class, ResultSetInternalMethods.class }, + new InvocationHandler() { + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + + if ("equals".equals(method.getName())) { + // Let args[0] "unwrap" to its InvocationHandler if it is a proxy. + return args[0].equals(this); + } + + Object invocationResult = method.invoke(finalResultSet, args); + + String methodName = method.getName(); + + if (invocationResult != null && invocationResult instanceof String || "getString".equals(methodName) || "getObject".equals(methodName) + || "getObjectStoredProc".equals(methodName)) { + Matcher matcher = ResultSetScannerInterceptor.this.regexP.matcher(invocationResult.toString()); + + if (matcher.matches()) { + throw new SQLException(Messages.getString("ResultSetScannerInterceptor.2")); + } + } + + return invocationResult; + } + }); + + } + + public T preProcess(Supplier sql, Query interceptedQuery) { + // we don't care about this event + + return null; + } + + // we don't issue queries, so it should be safe to intercept at any point + public boolean executeTopLevelOnly() { + return false; + } + + public void destroy() { + + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/interceptors/ServerStatusDiffInterceptor.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/interceptors/ServerStatusDiffInterceptor.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/interceptors/ServerStatusDiffInterceptor.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.interceptors; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.function.Supplier; + +import com.mysql.cj.MysqlConnection; +import com.mysql.cj.Query; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.interceptors.QueryInterceptor; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.util.ResultSetUtil; +import com.mysql.cj.log.Log; +import com.mysql.cj.protocol.Resultset; +import com.mysql.cj.protocol.ServerSession; +import com.mysql.cj.util.Util; + +public class ServerStatusDiffInterceptor implements QueryInterceptor { + + private Map preExecuteValues = new HashMap<>(); + + private Map postExecuteValues = new HashMap<>(); + + private JdbcConnection connection; + + private Log log; + + public QueryInterceptor init(MysqlConnection conn, Properties props, Log l) { + this.connection = (JdbcConnection) conn; + this.log = l; + return this; + } + + @Override + public T postProcess(Supplier sql, Query interceptedQuery, T originalResultSet, ServerSession serverSession) { + + populateMapWithSessionStatusValues(this.postExecuteValues); + + this.log.logInfo("Server status change for query:\n" + Util.calculateDifferences(this.preExecuteValues, this.postExecuteValues)); + + return null; // we don't actually modify a result set + + } + + private void populateMapWithSessionStatusValues(Map toPopulate) { + java.sql.Statement stmt = null; + java.sql.ResultSet rs = null; + + try { + try { + toPopulate.clear(); + + stmt = this.connection.createStatement(); + rs = stmt.executeQuery("SHOW SESSION STATUS"); + ResultSetUtil.resultSetToMap(toPopulate, rs); + } finally { + if (rs != null) { + rs.close(); + } + + if (stmt != null) { + stmt.close(); + } + } + } catch (SQLException ex) { + throw ExceptionFactory.createException(ex.getMessage(), ex); + } + } + + public T preProcess(Supplier sql, Query interceptedQuery) { + + populateMapWithSessionStatusValues(this.preExecuteValues); + + return null; // we don't actually modify a result set + } + + public boolean executeTopLevelOnly() { + return true; + } + + public void destroy() { + this.connection = null; + this.log = null; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/interceptors/SessionAssociationInterceptor.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/interceptors/SessionAssociationInterceptor.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/interceptors/SessionAssociationInterceptor.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.interceptors; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Properties; +import java.util.function.Supplier; + +import com.mysql.cj.MysqlConnection; +import com.mysql.cj.Query; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.interceptors.QueryInterceptor; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.log.Log; +import com.mysql.cj.protocol.Resultset; +import com.mysql.cj.protocol.ServerSession; + +public class SessionAssociationInterceptor implements QueryInterceptor { + + protected String currentSessionKey; + protected final static ThreadLocal sessionLocal = new ThreadLocal<>(); + private JdbcConnection connection; + + public static final void setSessionKey(String key) { + sessionLocal.set(key); + } + + public static final void resetSessionKey() { + sessionLocal.set(null); + } + + public static final String getSessionKey() { + return sessionLocal.get(); + } + + public boolean executeTopLevelOnly() { + return true; + } + + @Override + public QueryInterceptor init(MysqlConnection conn, Properties props, Log log) { + this.connection = (JdbcConnection) conn; + return this; + } + + @Override + public T postProcess(Supplier sql, Query interceptedQuery, T originalResultSet, ServerSession serverSession) { + return null; + } + + public T preProcess(Supplier sql, Query interceptedQuery) { + String key = getSessionKey(); + + if (key != null && !key.equals(this.currentSessionKey)) { + + try { + PreparedStatement pstmt = this.connection.clientPrepareStatement("SET @mysql_proxy_session=?"); + + try { + pstmt.setString(1, key); + pstmt.execute(); + } finally { + pstmt.close(); + } + } catch (SQLException ex) { + throw ExceptionFactory.createException(ex.getMessage(), ex); + } + + this.currentSessionKey = key; + } + + return null; + } + + public void destroy() { + this.connection = null; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/jmx/LoadBalanceConnectionGroupManager.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/jmx/LoadBalanceConnectionGroupManager.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/jmx/LoadBalanceConnectionGroupManager.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.jmx; + +import java.lang.management.ManagementFactory; +import java.sql.SQLException; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import com.mysql.cj.Messages; +import com.mysql.cj.jdbc.ConnectionGroupManager; +import com.mysql.cj.jdbc.exceptions.SQLError; + +public class LoadBalanceConnectionGroupManager implements LoadBalanceConnectionGroupManagerMBean { + + private boolean isJmxRegistered = false; + + public LoadBalanceConnectionGroupManager() { + + } + + public synchronized void registerJmx() throws SQLException { + if (this.isJmxRegistered) { + return; + } + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + try { + ObjectName name = new ObjectName("com.mysql.cj.jdbc.jmx:type=LoadBalanceConnectionGroupManager"); + mbs.registerMBean(this, name); + this.isJmxRegistered = true; + } catch (Exception e) { + throw SQLError.createSQLException(Messages.getString("LoadBalanceConnectionGroupManager.0"), null, e, null); + } + + } + + public void addHost(String group, String host, boolean forExisting) { + try { + ConnectionGroupManager.addHost(group, host, forExisting); + } catch (Exception e) { + e.printStackTrace(); // TODO log error normally instead of sysout + } + } + + public int getActiveHostCount(String group) { + return ConnectionGroupManager.getActiveHostCount(group); + } + + public long getActiveLogicalConnectionCount(String group) { + return ConnectionGroupManager.getActiveLogicalConnectionCount(group); + } + + public long getActivePhysicalConnectionCount(String group) { + return ConnectionGroupManager.getActivePhysicalConnectionCount(group); + } + + public int getTotalHostCount(String group) { + return ConnectionGroupManager.getTotalHostCount(group); + + } + + public long getTotalLogicalConnectionCount(String group) { + return ConnectionGroupManager.getTotalLogicalConnectionCount(group); + + } + + public long getTotalPhysicalConnectionCount(String group) { + return ConnectionGroupManager.getTotalPhysicalConnectionCount(group); + + } + + public long getTotalTransactionCount(String group) { + return ConnectionGroupManager.getTotalTransactionCount(group); + + } + + public void removeHost(String group, String host) throws SQLException { + ConnectionGroupManager.removeHost(group, host); + + } + + public String getActiveHostsList(String group) { + return ConnectionGroupManager.getActiveHostLists(group); + } + + public String getRegisteredConnectionGroups() { + return ConnectionGroupManager.getRegisteredConnectionGroups(); + } + + public void stopNewConnectionsToHost(String group, String host) throws SQLException { + ConnectionGroupManager.removeHost(group, host); + + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/jmx/LoadBalanceConnectionGroupManagerMBean.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/jmx/LoadBalanceConnectionGroupManagerMBean.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/jmx/LoadBalanceConnectionGroupManagerMBean.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.jmx; + +import java.sql.SQLException; + +public interface LoadBalanceConnectionGroupManagerMBean { + + int getActiveHostCount(String group); + + int getTotalHostCount(String group); + + long getTotalLogicalConnectionCount(String group); + + long getActiveLogicalConnectionCount(String group); + + long getActivePhysicalConnectionCount(String group); + + long getTotalPhysicalConnectionCount(String group); + + long getTotalTransactionCount(String group); + + void removeHost(String group, String host) throws SQLException; + + void stopNewConnectionsToHost(String group, String host) throws SQLException; + + void addHost(String group, String host, boolean forExisting); + + String getActiveHostsList(String group); + + String getRegisteredConnectionGroups(); + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/jmx/ReplicationGroupManager.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/jmx/ReplicationGroupManager.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/jmx/ReplicationGroupManager.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.jmx; + +import java.lang.management.ManagementFactory; +import java.sql.SQLException; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import com.mysql.cj.Messages; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.ha.ReplicationConnectionGroup; +import com.mysql.cj.jdbc.ha.ReplicationConnectionGroupManager; + +public class ReplicationGroupManager implements ReplicationGroupManagerMBean { + private boolean isJmxRegistered = false; + + public synchronized void registerJmx() throws SQLException { + if (this.isJmxRegistered) { + return; + } + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + try { + ObjectName name = new ObjectName("com.mysql.cj.jdbc.jmx:type=ReplicationGroupManager"); + mbs.registerMBean(this, name); + this.isJmxRegistered = true; + } catch (Exception e) { + throw SQLError.createSQLException(Messages.getString("ReplicationGroupManager.0"), null, e, null); + } + + } + + public void addSlaveHost(String groupFilter, String host) throws SQLException { + ReplicationConnectionGroupManager.addSlaveHost(groupFilter, host); + } + + public void removeSlaveHost(String groupFilter, String host) throws SQLException { + ReplicationConnectionGroupManager.removeSlaveHost(groupFilter, host); + } + + public void promoteSlaveToMaster(String groupFilter, String host) throws SQLException { + ReplicationConnectionGroupManager.promoteSlaveToMaster(groupFilter, host); + + } + + public void removeMasterHost(String groupFilter, String host) throws SQLException { + ReplicationConnectionGroupManager.removeMasterHost(groupFilter, host); + + } + + public String getMasterHostsList(String group) { + StringBuilder sb = new StringBuilder(""); + boolean found = false; + for (String host : ReplicationConnectionGroupManager.getMasterHosts(group)) { + if (found) { + sb.append(","); + } + found = true; + sb.append(host); + } + return sb.toString(); + } + + public String getSlaveHostsList(String group) { + StringBuilder sb = new StringBuilder(""); + boolean found = false; + for (String host : ReplicationConnectionGroupManager.getSlaveHosts(group)) { + if (found) { + sb.append(","); + } + found = true; + sb.append(host); + } + return sb.toString(); + + } + + public String getRegisteredConnectionGroups() { + StringBuilder sb = new StringBuilder(""); + boolean found = false; + for (ReplicationConnectionGroup group : ReplicationConnectionGroupManager.getGroupsMatching(null)) { + if (found) { + sb.append(","); + } + found = true; + sb.append(group.getGroupName()); + } + return sb.toString(); + } + + public int getActiveMasterHostCount(String group) { + return ReplicationConnectionGroupManager.getMasterHosts(group).size(); + } + + public int getActiveSlaveHostCount(String group) { + return ReplicationConnectionGroupManager.getSlaveHosts(group).size(); + } + + public int getSlavePromotionCount(String group) { + return ReplicationConnectionGroupManager.getNumberOfMasterPromotion(group); + } + + public long getTotalLogicalConnectionCount(String group) { + return ReplicationConnectionGroupManager.getTotalConnectionCount(group); + } + + public long getActiveLogicalConnectionCount(String group) { + return ReplicationConnectionGroupManager.getActiveConnectionCount(group); + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/jmx/ReplicationGroupManagerMBean.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/jmx/ReplicationGroupManagerMBean.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/jmx/ReplicationGroupManagerMBean.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.jmx; + +import java.sql.SQLException; + +public interface ReplicationGroupManagerMBean { + + void addSlaveHost(String groupFilter, String host) throws SQLException; + + void removeSlaveHost(String groupFilter, String host) throws SQLException; + + void promoteSlaveToMaster(String groupFilter, String host) throws SQLException; + + void removeMasterHost(String groupFilter, String host) throws SQLException; + + String getMasterHostsList(String group); + + String getSlaveHostsList(String group); + + String getRegisteredConnectionGroups(); + + int getActiveMasterHostCount(String group); + + int getActiveSlaveHostCount(String group); + + int getSlavePromotionCount(String group); + + long getTotalLogicalConnectionCount(String group); + + long getActiveLogicalConnectionCount(String group); + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/result/CachedResultSetMetaData.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/result/CachedResultSetMetaData.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/result/CachedResultSetMetaData.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.result; + +import java.sql.ResultSetMetaData; + +import com.mysql.cj.protocol.ColumnDefinition; + +public interface CachedResultSetMetaData extends ColumnDefinition { + + ResultSetMetaData getMetadata(); + + void setMetadata(java.sql.ResultSetMetaData metadata); +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/result/CachedResultSetMetaDataImpl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/result/CachedResultSetMetaDataImpl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/result/CachedResultSetMetaDataImpl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.result; + +import com.mysql.cj.result.DefaultColumnDefinition; + +public class CachedResultSetMetaDataImpl extends DefaultColumnDefinition implements CachedResultSetMetaData { + + /** Cached ResultSetMetaData */ + java.sql.ResultSetMetaData metadata; + + public java.sql.ResultSetMetaData getMetadata() { + return this.metadata; + } + + public void setMetadata(java.sql.ResultSetMetaData metadata) { + this.metadata = metadata; + } +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/result/ResultSetFactory.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/result/ResultSetFactory.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/result/ResultSetFactory.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.result; + +import java.sql.SQLException; + +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.WrongArgumentException; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.StatementImpl; +import com.mysql.cj.protocol.ProtocolEntity; +import com.mysql.cj.protocol.ProtocolEntityFactory; +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.ResultsetRows; +import com.mysql.cj.protocol.a.NativePacketPayload; +import com.mysql.cj.protocol.a.result.OkPacket; +import com.mysql.cj.protocol.a.result.ResultsetRowsCursor; + +public class ResultSetFactory implements ProtocolEntityFactory { + + private JdbcConnection conn; + private StatementImpl stmt; + + private Type type = Type.FORWARD_ONLY; + private Concurrency concurrency = Concurrency.READ_ONLY; + + public ResultSetFactory(JdbcConnection connection, StatementImpl creatorStmt) throws SQLException { + this.conn = connection; + this.stmt = creatorStmt; + + if (creatorStmt != null) { + this.type = Type.fromValue(creatorStmt.getResultSetType(), Type.FORWARD_ONLY); + this.concurrency = Concurrency.fromValue(creatorStmt.getResultSetConcurrency(), Concurrency.READ_ONLY); + } + } + + public Resultset.Type getResultSetType() { + return this.type; + } + + public Resultset.Concurrency getResultSetConcurrency() { + return this.concurrency; + } + + @Override + public int getFetchSize() { + try { + return this.stmt.getFetchSize(); + } catch (SQLException ex) { + throw ExceptionFactory.createException(ex.getMessage(), ex); + } + } + + @Override + public ResultSetImpl createFromProtocolEntity(ProtocolEntity protocolEntity) { + try { + if (protocolEntity instanceof OkPacket) { + return new ResultSetImpl((OkPacket) protocolEntity, this.conn, this.stmt); + + } else if (protocolEntity instanceof ResultsetRows) { + int resultSetConcurrency = getResultSetConcurrency().getIntValue(); + int resultSetType = getResultSetType().getIntValue(); + + return createFromResultsetRows(resultSetConcurrency, resultSetType, (ResultsetRows) protocolEntity); + + } + throw ExceptionFactory.createException(WrongArgumentException.class, "Unknown ProtocolEntity class " + protocolEntity); + + } catch (SQLException ex) { + throw ExceptionFactory.createException(ex.getMessage(), ex); + } + } + + /** + * Build ResultSet from ResultsetRows + * + * @param resultSetType + * scrollability (TYPE_FORWARD_ONLY, TYPE_SCROLL_????) + * @param resultSetConcurrency + * the type of result set (CONCUR_UPDATABLE or READ_ONLY) + */ + public ResultSetImpl createFromResultsetRows(int resultSetConcurrency, int resultSetType, ResultsetRows rows) throws SQLException { + + ResultSetImpl rs; + + StatementImpl st = this.stmt; + + if (rows.getOwner() != null) { + st = ((ResultSetImpl) rows.getOwner()).getOwningStatement(); + } + + switch (resultSetConcurrency) { + case java.sql.ResultSet.CONCUR_UPDATABLE: + rs = new UpdatableResultSet(rows, this.conn, st); + break; + + default: + // CONCUR_READ_ONLY + rs = new ResultSetImpl(rows, this.conn, st); + break; + } + + rs.setResultSetType(resultSetType); + rs.setResultSetConcurrency(resultSetConcurrency); + + if (rows instanceof ResultsetRowsCursor && st != null) { + rs.setFetchSize(st.getFetchSize()); + } + return rs; + } + +} Index: 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/result/ResultSetImpl.java =================================================================== diff -u --- 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/result/ResultSetImpl.java (revision 0) +++ 3rdParty_sources/mysql-connector/com/mysql/cj/jdbc/result/ResultSetImpl.java (revision f6a744fdbb85b13336b6dbcb58cd0c4466a4e465) @@ -0,0 +1,2596 @@ +/* + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * + * 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.jdbc.result; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.ObjectInputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URL; +import java.sql.Array; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Ref; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.SQLType; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Struct; +import java.sql.Time; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.format.DateTimeParseException; +import java.util.Calendar; +import java.util.HashSet; +import java.util.Set; +import java.util.TimeZone; + +import com.mysql.cj.Constants; +import com.mysql.cj.Messages; +import com.mysql.cj.MysqlType; +import com.mysql.cj.NativeSession; +import com.mysql.cj.Session; +import com.mysql.cj.WarningListener; +import com.mysql.cj.conf.PropertyDefinitions; +import com.mysql.cj.conf.ReadableProperty; +import com.mysql.cj.exceptions.CJException; +import com.mysql.cj.exceptions.ExceptionFactory; +import com.mysql.cj.exceptions.ExceptionInterceptor; +import com.mysql.cj.exceptions.MysqlErrorNumbers; +import com.mysql.cj.jdbc.Blob; +import com.mysql.cj.jdbc.BlobFromLocator; +import com.mysql.cj.jdbc.Clob; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.JdbcPreparedStatement; +import com.mysql.cj.jdbc.JdbcStatement; +import com.mysql.cj.jdbc.MysqlSQLXML; +import com.mysql.cj.jdbc.StatementImpl; +import com.mysql.cj.jdbc.exceptions.SQLError; +import com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping; +import com.mysql.cj.log.ProfilerEvent; +import com.mysql.cj.log.ProfilerEventHandler; +import com.mysql.cj.log.ProfilerEventHandlerFactory; +import com.mysql.cj.log.ProfilerEventImpl; +import com.mysql.cj.protocol.ColumnDefinition; +import com.mysql.cj.protocol.ResultsetRows; +import com.mysql.cj.protocol.a.result.NativeResultset; +import com.mysql.cj.protocol.a.result.OkPacket; +import com.mysql.cj.protocol.a.result.ResultsetRowsStatic; +import com.mysql.cj.result.BigDecimalValueFactory; +import com.mysql.cj.result.BinaryStreamValueFactory; +import com.mysql.cj.result.BooleanValueFactory; +import com.mysql.cj.result.ByteValueFactory; +import com.mysql.cj.result.DoubleValueFactory; +import com.mysql.cj.result.Field; +import com.mysql.cj.result.FloatValueFactory; +import com.mysql.cj.result.FloatingPointBoundsEnforcer; +import com.mysql.cj.result.IntegerBoundsEnforcer; +import com.mysql.cj.result.IntegerValueFactory; +import com.mysql.cj.result.LocalDateTimeValueFactory; +import com.mysql.cj.result.LocalDateValueFactory; +import com.mysql.cj.result.LocalTimeValueFactory; +import com.mysql.cj.result.LongValueFactory; +import com.mysql.cj.result.ShortValueFactory; +import com.mysql.cj.result.SqlDateValueFactory; +import com.mysql.cj.result.SqlTimeValueFactory; +import com.mysql.cj.result.SqlTimestampValueFactory; +import com.mysql.cj.result.StringConverter; +import com.mysql.cj.result.StringValueFactory; +import com.mysql.cj.result.ValueFactory; +import com.mysql.cj.result.YearToDateValueFactory; +import com.mysql.cj.result.ZeroDateTimeToDefaultValueFactory; +import com.mysql.cj.result.ZeroDateTimeToNullValueFactory; +import com.mysql.cj.util.LogUtils; +import com.mysql.cj.util.StringUtils; + +public class ResultSetImpl extends NativeResultset implements ResultSetInternalMethods, WarningListener { + + /** Counter used to generate IDs for profiling. */ + static int resultCounter = 1; + + /** The catalog that was in use when we were created */ + protected String catalog = null; + + /** Keep track of columns accessed */ + protected boolean[] columnUsed = null; + + /** The Connection instance that created us */ + protected volatile JdbcConnection connection; + + protected NativeSession session = null; + + private long connectionId = 0; + + /** The current row #, -1 == before start of result set */ + protected int currentRow = -1; // Cursor to current row; + + protected ProfilerEventHandler eventSink = null; + + Calendar fastDefaultCal = null; + Calendar fastClientCal = null; + + /** The direction to fetch rows (always FETCH_FORWARD) */ + protected int fetchDirection = FETCH_FORWARD; + + /** The number of rows to fetch in one go... */ + protected int fetchSize = 0; + + /** + * First character of the query that created this result set...Used to determine whether or not to parse server info messages in certain + * circumstances. + */ + protected char firstCharOfQuery; + + /** Has this result set been closed? */ + protected boolean isClosed = false; + + /** The statement that created us */ + private com.mysql.cj.jdbc.StatementImpl owningStatement; + + /** + * StackTrace generated where ResultSet was created... used when profiling + */ + private String pointOfOrigin; + + /** Are we tracking items for profileSQL? */ + protected boolean profileSQL = false; + + /** Are we read-only or updatable? */ + protected int resultSetConcurrency = 0; + + /** Are we scroll-sensitive/insensitive? */ + protected int resultSetType = 0; + + JdbcPreparedStatement statementUsedForFetchingRows; + + protected boolean useUsageAdvisor = false; + + /** The warning chain */ + protected java.sql.SQLWarning warningChain = null; + + protected java.sql.Statement wrapperStatement; + + private boolean padCharsWithSpace = false; + + private boolean useColumnNamesInFindColumn; + + private ExceptionInterceptor exceptionInterceptor; + + private ValueFactory booleanValueFactory; + private ValueFactory byteValueFactory; + private ValueFactory shortValueFactory; + private ValueFactory integerValueFactory; + private ValueFactory longValueFactory; + private ValueFactory floatValueFactory; + private ValueFactory doubleValueFactory; + private ValueFactory bigDecimalValueFactory; + private ValueFactory binaryStreamValueFactory; + // temporal values include the default conn TZ, can be overridden with cal param, e.g. getDate(1, calWithOtherTZ) + private ValueFactory defaultDateValueFactory; + private ValueFactory