diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/FieldWithMetadata.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/FieldWithMetadata.java index 808af359418..060dffd5930 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/FieldWithMetadata.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/FieldWithMetadata.java @@ -4,6 +4,7 @@ import com.flipkart.vitess.util.MysqlDefs; import com.flipkart.vitess.util.StringUtils; import com.flipkart.vitess.util.charset.CharsetMapping; +import com.google.common.annotations.VisibleForTesting; import com.youtube.vitess.proto.Query; import java.sql.SQLException; @@ -12,7 +13,7 @@ public class FieldWithMetadata { - private final VitessConnection connection; + private final ConnectionProperties connectionProperties; private final Query.Field field; private final Query.Type vitessType; private final boolean isImplicitTempTable; @@ -26,8 +27,8 @@ public class FieldWithMetadata { private int collationIndex; private int maxBytesPerChar; - public FieldWithMetadata(VitessConnection connection, Query.Field field) throws SQLException { - this.connection = connection; + public FieldWithMetadata(ConnectionProperties connectionProperties, Query.Field field) throws SQLException { + this.connectionProperties = connectionProperties; this.field = field; this.colFlag = field.getFlags(); this.vitessType = field.getType(); @@ -46,15 +47,15 @@ public FieldWithMetadata(VitessConnection connection, Query.Field field) throws // All of the below remapping and metadata fields require the extra // fields included when includeFields=IncludedFields.ALL - if (connection != null && connection.isIncludeAllFields()) { + if (connectionProperties != null && connectionProperties.isIncludeAllFields()) { this.isImplicitTempTable = checkForImplicitTemporaryTable(); // Re-map BLOB to 'real' blob type if (this.javaType == Types.BLOB) { boolean isFromFunction = field.getOrgTable().isEmpty(); - if (connection.getBlobsAreStrings() || (connection.getFunctionsNeverReturnBlobs() && isFromFunction)) { + if (connectionProperties.getBlobsAreStrings() || (connectionProperties.getFunctionsNeverReturnBlobs() && isFromFunction)) { this.javaType = Types.VARCHAR; } else if (collationIndex == CharsetMapping.MYSQL_COLLATION_INDEX_binary) { - if (connection.getUseBlobToStoreUTF8OutsideBMP() && shouldSetupForUtf8StringInBlob()) { + if (connectionProperties.getUseBlobToStoreUTF8OutsideBMP() && shouldSetupForUtf8StringInBlob()) { if (this.getColumnLength() == MysqlDefs.LENGTH_TINYBLOB || this.getColumnLength() == MysqlDefs.LENGTH_BLOB) { this.javaType = Types.VARCHAR; } else { @@ -76,14 +77,14 @@ public FieldWithMetadata(VitessConnection connection, Query.Field field) throws } // Re-map TINYINT(1) as bit or pseudo-boolean - if (this.javaType == Types.TINYINT && this.field.getColumnLength() == 1 && connection.getTinyInt1isBit()) { + if (this.javaType == Types.TINYINT && this.field.getColumnLength() == 1 && connectionProperties.getTinyInt1isBit()) { this.javaType = Types.BIT; } if (!isNativeNumericType() && !isNativeDateTimeType()) { // For non-numeric types, try to pull the encoding from the passed collationIndex // We will do some fixup afterwards - this.encoding = connection.getEncodingForIndex(this.collationIndex); + this.encoding = getEncodingForIndex(this.collationIndex); // ucs2, utf16, and utf32 cannot be used as a client character set, but if it was received from server // under some circumstances we can parse them as utf16 if ("UnicodeBig".equals(this.encoding)) { @@ -182,24 +183,37 @@ private boolean isNativeDateTimeType() { } } - public VitessConnection getConnection() throws SQLException { - checkConnection(); - return connection; + @VisibleForTesting + String getEncodingForIndex(int charsetIndex) { + String javaEncoding = null; + if (charsetIndex != MysqlDefs.NO_CHARSET_INFO) { + javaEncoding = CharsetMapping.getJavaEncodingForCollationIndex(charsetIndex, connectionProperties.getEncoding()); + } + // If nothing, get default based on configuration, may still be null + if (javaEncoding == null) { + javaEncoding = connectionProperties.getEncoding(); + } + return javaEncoding; + } + + public ConnectionProperties getConnectionProperties() throws SQLException { + checkConnectionProperties(); + return connectionProperties; } - public boolean hasConnection() { - return connection != null; + public boolean hasConnectionProperties() { + return connectionProperties != null; } - private void checkConnection() throws SQLException { - if (!hasConnection()) { + private void checkConnectionProperties() throws SQLException { + if (!hasConnectionProperties()) { throw new SQLException(Constants.SQLExceptionMessages.CONN_UNAVAILABLE); } } private boolean shouldSetupForUtf8StringInBlob() throws SQLException { - String includePattern = connection.getUtf8OutsideBmpIncludedColumnNamePattern(); - String excludePattern = connection.getUtf8OutsideBmpExcludedColumnNamePattern(); + String includePattern = connectionProperties.getUtf8OutsideBmpIncludedColumnNamePattern(); + String excludePattern = connectionProperties.getUtf8OutsideBmpExcludedColumnNamePattern(); // When UseBlobToStoreUTF8OutsideBMP is set, we by default set blobs to UTF-8. So we first // look for fields to exclude from that remapping (blacklist) @@ -228,85 +242,85 @@ private boolean shouldSetupForUtf8StringInBlob() throws SQLException { } public boolean isAutoIncrement() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.AUTO_INCREMENT_FLAG_VALUE) > 0); } public boolean isBinary() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.BINARY_FLAG_VALUE) > 0); } public boolean isBlob() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.BLOB_FLAG_VALUE) > 0); } public boolean isMultipleKey() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.MULTIPLE_KEY_FLAG_VALUE) > 0); } boolean isNotNull() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return true; } return ((this.colFlag & Query.MySqlFlag.NOT_NULL_FLAG_VALUE) > 0); } public boolean isZeroFill() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.ZEROFILL_FLAG_VALUE) > 0); } public boolean isPrimaryKey() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.PRI_KEY_FLAG_VALUE) > 0); } public boolean isUniqueKey() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return ((this.colFlag & Query.MySqlFlag.UNIQUE_KEY_FLAG_VALUE) > 0); } public boolean isUnsigned() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return true; } return ((this.colFlag & Query.MySqlFlag.UNSIGNED_FLAG_VALUE) > 0); } public boolean isSigned() throws SQLException { - checkConnection(); + checkConnectionProperties(); return !isUnsigned(); } boolean isOpaqueBinary() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } @@ -329,8 +343,8 @@ boolean isOpaqueBinary() throws SQLException { * statement. */ boolean isReadOnly() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } String orgColumnName = getOrgName(); @@ -339,7 +353,7 @@ boolean isReadOnly() throws SQLException { } public synchronized String getCollation() throws SQLException { - if (!connection.isIncludeAllFields()) { + if (!connectionProperties.isIncludeAllFields()) { return null; } @@ -356,63 +370,75 @@ public synchronized String getCollation() throws SQLException { public synchronized int getMaxBytesPerCharacter() { - if (!connection.isIncludeAllFields()) { + if (!connectionProperties.isIncludeAllFields()) { return 0; } if (this.maxBytesPerChar == 0) { - this.maxBytesPerChar = this.connection.getMaxBytesPerChar(getCollationIndex(), getEncoding()); + this.maxBytesPerChar = getMaxBytesPerChar(getCollationIndex(), getEncoding()); } return this.maxBytesPerChar; } + @VisibleForTesting + int getMaxBytesPerChar(Integer charsetIndex, String javaCharsetName) { + // if we can get it by charsetIndex just doing it + String charset = CharsetMapping.getMysqlCharsetNameForCollationIndex(charsetIndex); + // if we didn't find charset name by its full name + if (charset == null) { + charset = CharsetMapping.getMysqlCharsetForJavaEncoding(javaCharsetName); + } + // checking against static maps + return CharsetMapping.getMblen(charset); + } + public String getName() { return field.getName(); } public String getTable() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return null; } return field.getTable(); } public String getOrgTable() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return null; } return field.getOrgTable(); } public String getDatabase() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return null; } return field.getDatabase(); } public String getOrgName() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return null; } return field.getOrgName(); } public int getColumnLength() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return 0; } return field.getColumnLength(); } public int getDecimals() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return 0; } return field.getDecimals(); @@ -431,14 +457,14 @@ public int getVitessTypeValue() { } boolean isImplicitTemporaryTable() { - if (!connection.isIncludeAllFields()) { + if (!connectionProperties.isIncludeAllFields()) { return false; } return isImplicitTempTable; } public String getEncoding() { - if (!connection.isIncludeAllFields()) { + if (!connectionProperties.isIncludeAllFields()) { return null; } return encoding; @@ -450,16 +476,16 @@ public String getEncoding() { * numeric types */ public int getPrecisionAdjustFactor() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return 0; } return precisionAdjustFactor; } public boolean isSingleBit() throws SQLException { - checkConnection(); - if (!connection.isIncludeAllFields()) { + checkConnectionProperties(); + if (!connectionProperties.isIncludeAllFields()) { return false; } return isSingleBit; diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessConnection.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessConnection.java index 9f2814f11a7..0424a029262 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessConnection.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessConnection.java @@ -3,7 +3,6 @@ import com.flipkart.vitess.util.CommonUtils; import com.flipkart.vitess.util.Constants; import com.flipkart.vitess.util.MysqlDefs; -import com.flipkart.vitess.util.charset.CharsetMapping; import com.youtube.vitess.client.Context; import com.youtube.vitess.client.VTGateConn; import com.youtube.vitess.client.VTGateTx; @@ -846,27 +845,4 @@ public Context createContext(long deadlineAfter) { public String getUsername() { return this.vitessJDBCUrl.getUsername(); } - - public String getEncodingForIndex(int charsetIndex) { - String javaEncoding = null; - if (charsetIndex != MysqlDefs.NO_CHARSET_INFO) { - javaEncoding = CharsetMapping.getJavaEncodingForCollationIndex(charsetIndex, getEncoding()); - } - // If nothing, get default based on configuration, may still be null - if (javaEncoding == null) { - javaEncoding = getEncoding(); - } - return javaEncoding; - } - - public int getMaxBytesPerChar(Integer charsetIndex, String javaCharsetName) { - // if we can get it by charsetIndex just doing it - String charset = CharsetMapping.getMysqlCharsetNameForCollationIndex(charsetIndex); - // if we didn't find charset name by its full name - if (charset == null) { - charset = CharsetMapping.getMysqlCharsetForJavaEncoding(javaCharsetName); - } - // checking against static maps - return CharsetMapping.getMblen(charset); - } } diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMariaDBDatabaseMetadata.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMariaDBDatabaseMetadata.java index 5420a3567fe..fda293436e7 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMariaDBDatabaseMetadata.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMariaDBDatabaseMetadata.java @@ -418,7 +418,7 @@ public ResultSet getTypeInfo() throws SQLException { {"TIMESTAMP", "93", "27", "'", "'", "[(M)]", "1", "0", "3", "0", "0", "0", "TIMESTAMP", "0", "0", "0", "0", "10"}}; - return new VitessResultSet(columnNames, columnTypes, data); + return new VitessResultSet(columnNames, columnTypes, data, this.connection); } public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMySQLDatabaseMetadata.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMySQLDatabaseMetadata.java index 06333fe7d4a..6f149154fad 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMySQLDatabaseMetadata.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessMySQLDatabaseMetadata.java @@ -454,13 +454,13 @@ public boolean supportsTransactionIsolationLevel(int level) throws SQLException Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR}; - return new VitessResultSet(columnNames, columnTypes, data); + return new VitessResultSet(columnNames, columnTypes, data, this.connection); } public ResultSet getSchemas() throws SQLException { String[] columnNames = {"TABLE_SCHEM", "TABLE_CATALOG"}; Query.Type[] columnType = {Query.Type.CHAR, Query.Type.CHAR}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public ResultSet getCatalogs() throws SQLException { @@ -487,7 +487,7 @@ public ResultSet getCatalogs() throws SQLException { vitessStatement.close(); String[] columnName = new String[] {"TABLE_CAT"}; Query.Type[] columntype = new Query.Type[] {Query.Type.CHAR}; - return new VitessResultSet(columnName, columntype, data); + return new VitessResultSet(columnName, columntype, data, this.connection); } public ResultSet getTableTypes() throws SQLException { @@ -496,7 +496,7 @@ public ResultSet getTableTypes() throws SQLException { String[][] data = new String[][] {{"LOCAL TEMPORARY"}, {"SYSTEM TABLES"}, {"SYSTEM VIEW"}, {"TABLE"}, {"VIEW"}}; - return new VitessResultSet(columnNames, columnType, data); + return new VitessResultSet(columnNames, columnType, data, this.connection); } @SuppressWarnings("StringBufferReplaceableByString") public ResultSet getColumns(String catalog, @@ -700,7 +700,7 @@ public ResultSet getTableTypes() throws SQLException { Query.Type.INT32, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.INT16, Query.Type.CHAR, Query.Type.CHAR}; - return new VitessResultSet(columnNames, columnType, data); + return new VitessResultSet(columnNames, columnType, data, this.connection); } public ResultSet getColumnPrivileges(String catalog, String schema, String table, @@ -791,7 +791,7 @@ public ResultSet getTablePrivileges(String catalog, String schemaPattern, } vitessStatement.close(); } - return new VitessResultSet(columnName, columnType, data); + return new VitessResultSet(columnName, columnType, data, this.connection); } public ResultSet getVersionColumns(String catalog, String schema, String table) @@ -846,7 +846,7 @@ public ResultSet getVersionColumns(String catalog, String schema, String table) Query.Type[] columnType = new Query.Type[] {Query.Type.INT16, Query.Type.CHAR, Query.Type.INT32, Query.Type.CHAR, Query.Type.INT32, Query.Type.INT32, Query.Type.INT16, Query.Type.INT16}; - return new VitessResultSet(columnNames, columnType, data); + return new VitessResultSet(columnNames, columnType, data, this.connection); } @SuppressWarnings("StringBufferReplaceableByString") public ResultSet getPrimaryKeys( @@ -902,7 +902,7 @@ public ResultSet getVersionColumns(String catalog, String schema, String table) new Query.Type[] {Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.INT16, Query.Type.CHAR}; - return new VitessResultSet(columnNames, columnType, sortedData); + return new VitessResultSet(columnNames, columnType, sortedData, this.connection); } public ResultSet getImportedKeys(String catalog, String schema, String table) @@ -1019,7 +1019,7 @@ public ResultSet getTypeInfo() throws SQLException { {"TIMESTAMP", "93", "0", "'", "'", "[(M)]", "1", "false", "3", "false", "false", "false", "TIMESTAMP", "0", "0", "0", "0", "10"}}; - return new VitessResultSet(columnNames, columnTypes, data); + return new VitessResultSet(columnNames, columnTypes, data, this.connection); } @SuppressWarnings("StringBufferReplaceableByString") public ResultSet getIndexInfo( @@ -1088,7 +1088,7 @@ public ResultSet getTypeInfo() throws SQLException { Query.Type.CHAR, Query.Type.CHAR, Query.Type.INT32, Query.Type.INT32, Query.Type.CHAR}; - return new VitessResultSet(columnName, columnType, data); + return new VitessResultSet(columnName, columnType, data, this.connection); } public boolean ownUpdatesAreVisible(int type) throws SQLException { @@ -1131,7 +1131,7 @@ public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePa Query.Type[] columnType = {Query.Type.VARCHAR, Query.Type.INT32, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.INT32, Query.Type.VARCHAR, Query.Type.INT16}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) @@ -1142,7 +1142,7 @@ public ResultSet getSuperTypes(String catalog, String schemaPattern, String type Query.Type[] columnType = {Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) @@ -1150,7 +1150,7 @@ public ResultSet getSuperTables(String catalog, String schemaPattern, String tab String[] columnNames = {"TABLE_CAT", "TYPE_SCHEM", "TABLE_NAME", "SUPERTABLE_NAME"}; Query.Type[] columnType = {Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, @@ -1167,7 +1167,7 @@ public ResultSet getAttributes(String catalog, String schemaPattern, String type Query.Type.INT32, Query.Type.CHAR, Query.Type.CHAR, Query.Type.INT32, Query.Type.INT32, Query.Type.INT32, Query.Type.INT32, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.CHAR, Query.Type.INT16}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public int getSQLStateType() throws SQLException { @@ -1186,14 +1186,14 @@ public RowIdLifetime getRowIdLifetime() throws SQLException { public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { String[] columnNames = {"TABLE_CAT", "TABLE_CATALOG"}; Query.Type[] columnType = {Query.Type.CHAR, Query.Type.CHAR}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public ResultSet getClientInfoProperties() throws SQLException { String[] columnNames = {"NAME", "MAX_LEN", "DEFAULT_VALUE", "DESCRIPTION"}; Query.Type[] columnType = {Query.Type.VARCHAR, Query.Type.INT32, Query.Type.VARCHAR, Query.Type.VARCHAR}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) @@ -1218,7 +1218,7 @@ public ResultSet getPseudoColumns(String catalog, String schemaPattern, String t {Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.INT32, Query.Type.INT32, Query.Type.INT32, Query.Type.INT32, Query.Type.VARCHAR, Query.Type.VARCHAR, Query.Type.INT32, Query.Type.VARCHAR}; - return new VitessResultSet(columnNames, columnType, new String[][] {}); + return new VitessResultSet(columnNames, columnType, new String[][] {}, this.connection); } public T unwrap(Class iface) throws SQLException { diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSet.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSet.java index 81f8f69fed7..288a4ccfd0f 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSet.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSet.java @@ -71,7 +71,7 @@ public VitessResultSet(Cursor cursor, VitessStatement vitessStatement) throws SQ this.cursor = cursor; this.vitessStatement = vitessStatement; try { - this.fields = enhancedFieldsFromCursor(); + this.fields = enhancedFieldsFromCursor(vitessStatement == null ? null : vitessStatement.getConnection()); } catch (SQLException e) { throw new SQLException(Constants.SQLExceptionMessages.RESULT_SET_INIT_ERROR, e); } @@ -82,7 +82,7 @@ public VitessResultSet(Cursor cursor, VitessStatement vitessStatement) throws SQ } } - public VitessResultSet(String[] columnNames, Query.Type[] columnTypes, String[][] data) + public VitessResultSet(String[] columnNames, Query.Type[] columnTypes, String[][] data, ConnectionProperties connection) throws SQLException { if (columnNames.length != columnTypes.length) { @@ -112,7 +112,7 @@ public VitessResultSet(String[] columnNames, Query.Type[] columnTypes, String[][ this.cursor = new SimpleCursor(queryResultBuilder.build()); this.vitessStatement = null; try { - this.fields = enhancedFieldsFromCursor(); + this.fields = enhancedFieldsFromCursor(connection); } catch (SQLException e) { throw new SQLException(Constants.SQLExceptionMessages.RESULT_SET_INIT_ERROR, e); } @@ -120,7 +120,7 @@ public VitessResultSet(String[] columnNames, Query.Type[] columnTypes, String[][ } public VitessResultSet(String[] columnNames, Query.Type[] columnTypes, - ArrayList> data) throws SQLException { + ArrayList> data, VitessConnection connection) throws SQLException { if (columnNames.length != columnTypes.length) { throw new SQLException(Constants.SQLExceptionMessages.INVALID_RESULT_SET); @@ -151,18 +151,17 @@ public VitessResultSet(String[] columnNames, Query.Type[] columnTypes, this.cursor = new SimpleCursor(queryResultBuilder.build()); this.vitessStatement = null; try { - this.fields = enhancedFieldsFromCursor(); + this.fields = enhancedFieldsFromCursor(connection); } catch (SQLException e) { throw new SQLException(Constants.SQLExceptionMessages.RESULT_SET_INIT_ERROR, e); } this.currentRow = 0; } - private List enhancedFieldsFromCursor() throws SQLException { + private List enhancedFieldsFromCursor(ConnectionProperties connection) throws SQLException { if (cursor == null|| cursor.getFields() == null) { throw new SQLException(Constants.SQLExceptionMessages.CURSOR_NULL); } - VitessConnection connection = vitessStatement == null ? null : vitessStatement.getConnection(); List rawFields = cursor.getFields(); List fields = new ArrayList<>(rawFields.size()); for (Query.Field field : rawFields) { @@ -222,7 +221,7 @@ public String getString(int columnIndex) throws SQLException { object = this.row.getObject(columnIndex); if (object instanceof byte[]) { FieldWithMetadata field = this.fields.get(columnIndex - 1); - if (field.hasConnection() && field.getConnection().isIncludeAllFields()) { + if (field.hasConnectionProperties() && field.getConnectionProperties().isIncludeAllFields()) { columnValue = convertBytesToString((byte[]) object, field.getEncoding()); } else { columnValue = new String((byte[]) object); @@ -558,7 +557,7 @@ public Object getObject(int columnIndex) throws SQLException { Object retVal = this.row.getObject(columnIndex); FieldWithMetadata field = this.fields.get(columnIndex - 1); - if (field.hasConnection() && field.getConnection().isIncludeAllFields() && retVal instanceof byte[]) { + if (field.hasConnectionProperties() && field.getConnectionProperties().isIncludeAllFields() && retVal instanceof byte[]) { retVal = convertBytesIfPossible((byte[]) retVal, field); } diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSetMetaData.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSetMetaData.java index 8355c222f64..eb02fbfddde 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSetMetaData.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessResultSetMetaData.java @@ -52,7 +52,7 @@ public boolean isCaseSensitive(int column) throws SQLException { case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: - if (field.isBinary() || !field.getConnection().isIncludeAllFields()) { + if (field.isBinary() || !field.getConnectionProperties().isIncludeAllFields()) { return true; } try { @@ -88,7 +88,7 @@ public boolean isCurrency(int column) throws SQLException { */ public int isNullable(int column) throws SQLException { FieldWithMetadata field = getField(column); - if (!field.getConnection().isIncludeAllFields()) { + if (!field.getConnectionProperties().isIncludeAllFields()) { return ResultSetMetaData.columnNullableUnknown; } return field.isNotNull() ? ResultSetMetaData.columnNoNulls : ResultSetMetaData.columnNullable; @@ -100,7 +100,7 @@ public boolean isSigned(int column) throws SQLException { public int getColumnDisplaySize(int column) throws SQLException { FieldWithMetadata field = getField(column); - if (!field.getConnection().isIncludeAllFields()) { + if (!field.getConnectionProperties().isIncludeAllFields()) { return 0; } // If we can't find a charset, we'll return 0. In that case assume 1 byte per char @@ -121,7 +121,7 @@ public String getSchemaName(int column) throws SQLException { public int getPrecision(int column) throws SQLException { FieldWithMetadata field = getField(column); - if (!field.getConnection().isIncludeAllFields()) { + if (!field.getConnectionProperties().isIncludeAllFields()) { return 0; } if (isDecimalType(field.getJavaType(), field.getVitessTypeValue())) { @@ -289,11 +289,11 @@ public boolean isDefinitelyWritable(int column) throws SQLException { public String getColumnClassName(int column) throws SQLException { FieldWithMetadata field = getField(column); - if (!field.getConnection().isIncludeAllFields()) { + if (!field.getConnectionProperties().isIncludeAllFields()) { return null; } return getClassNameForJavaType(field.getJavaType(), field.getVitessTypeValue(), field.isUnsigned(), - field.isBinary() || field.isBlob(), field.isOpaqueBinary(), field.getConnection().getYearIsDateType()); + field.isBinary() || field.isBlob(), field.isOpaqueBinary(), field.getConnectionProperties().getYearIsDateType()); } public T unwrap(Class iface) throws SQLException { diff --git a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessStatement.java b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessStatement.java index 6ae6302c18c..e0d463ab5ca 100644 --- a/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessStatement.java +++ b/java/jdbc/src/main/java/com/flipkart/vitess/jdbc/VitessStatement.java @@ -386,7 +386,7 @@ public ResultSet getGeneratedKeys() throws SQLException { } } - return new VitessResultSet(columnNames, columnTypes, data); + return new VitessResultSet(columnNames, columnTypes, data, this.vitessConnection); } /** diff --git a/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/FieldWithMetadataTest.java b/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/FieldWithMetadataTest.java index 4667b5445e5..4d25a0057fb 100644 --- a/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/FieldWithMetadataTest.java +++ b/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/FieldWithMetadataTest.java @@ -7,6 +7,7 @@ import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import org.mockito.internal.verification.VerificationModeFactory; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -610,20 +611,75 @@ public void testMaxBytesPerChar() throws Exception { int second = fieldWithMetadata.getMaxBytesPerCharacter(); Assert.assertEquals("cached response is same as first", first, second); + // We called getMaxBytesPerCharacter 2 times above, but should only have made 1 call to fieldWithMetadata.getMaxBytesPerChar: + // first - call conn + // second - return cached + Mockito.verify(fieldWithMetadata, VerificationModeFactory.times(1)).getMaxBytesPerChar(33, "UTF-8"); PowerMockito.verifyPrivate(fieldWithMetadata, VerificationModeFactory.times(1)).invoke("getCollationIndex"); conn.setIncludedFields(Query.ExecuteOptions.IncludedFields.TYPE_AND_NAME); fieldWithMetadata = PowerMockito.spy(new FieldWithMetadata(conn, raw)); Assert.assertEquals("0 return value when not including all fields", 0, fieldWithMetadata.getMaxBytesPerCharacter()); - // We called getMaxBytesPerCharacter 3 times above, but should only have made 1 call to conn.getMaxBytesPerChar: - // first - call conn - // second - returne cached - // third - short circuit because not including all fields - // Will test the actual implementation/return value in VitessConnection - PowerMockito.verifyPrivate(conn, VerificationModeFactory.times(1)).invoke("getMaxBytesPerChar", 33, "UTF-8"); - + // We should not call this function because we short circuited due to not including all fields. + Mockito.verify(fieldWithMetadata, VerificationModeFactory.times(0)).getMaxBytesPerChar(33, "UTF-8"); // Should not be called at all, because it's new for just this test PowerMockito.verifyPrivate(fieldWithMetadata, VerificationModeFactory.times(0)).invoke("getCollationIndex"); } + + @Test public void testGetEncodingForIndex() throws SQLException { + Query.Field raw = Query.Field.newBuilder() + .setTable("foo") + .setType(Query.Type.CHAR) + .setName("foo") + .setOrgName("foo") + .setCharset(33) + .build(); + FieldWithMetadata field = new FieldWithMetadata(getVitessConnection(), raw); + + // No default encoding configured, and passing NO_CHARSET_INFO basically says "mysql doesn't know" + // which means don't try looking it up + Assert.assertEquals(null, field.getEncodingForIndex(MysqlDefs.NO_CHARSET_INFO)); + // Similarly, a null index or one landing out of bounds for the charset index should return null + Assert.assertEquals(null, field.getEncodingForIndex(Integer.MAX_VALUE)); + Assert.assertEquals(null, field.getEncodingForIndex(-123)); + + // charsetIndex 25 is MYSQL_CHARSET_NAME_greek, which is a charset with multiple names, ISO8859_7 and greek + // Without an encoding configured in the connection, we should return the first (default) encoding for a charset, + // in this case ISO8859_7 + Assert.assertEquals("ISO-8859-7", field.getEncodingForIndex(25)); + field.getConnectionProperties().setEncoding("greek"); + // With an encoding configured, we should return that because it matches one of the names for the charset + Assert.assertEquals("greek", field.getEncodingForIndex(25)); + + field.getConnectionProperties().setEncoding(null); + Assert.assertEquals("UTF-8", field.getEncodingForIndex(33)); + Assert.assertEquals("ISO-8859-1", field.getEncodingForIndex(63)); + + field.getConnectionProperties().setEncoding("NOT_REAL"); + // Same tests as the first one, but testing that when there is a default configured, it falls back to that regardless + Assert.assertEquals("NOT_REAL", field.getEncodingForIndex(MysqlDefs.NO_CHARSET_INFO)); + Assert.assertEquals("NOT_REAL", field.getEncodingForIndex(Integer.MAX_VALUE)); + Assert.assertEquals("NOT_REAL", field.getEncodingForIndex(-123)); + } + + @Test public void testGetMaxBytesPerChar() throws SQLException { + Query.Field raw = Query.Field.newBuilder() + .setTable("foo") + .setType(Query.Type.CHAR) + .setName("foo") + .setOrgName("foo") + .setCharset(33) + .build(); + FieldWithMetadata field = new FieldWithMetadata(getVitessConnection(), raw); + + // Default state when no good info is passed in + Assert.assertEquals(0, field.getMaxBytesPerChar(MysqlDefs.NO_CHARSET_INFO, null)); + // use passed collation index + Assert.assertEquals(3, field.getMaxBytesPerChar(CharsetMapping.MYSQL_COLLATION_INDEX_utf8, null)); + // use first, if both are passed and valid + Assert.assertEquals(3, field.getMaxBytesPerChar(CharsetMapping.MYSQL_COLLATION_INDEX_utf8, "UnicodeBig")); + // use passed default charset + Assert.assertEquals(2, field.getMaxBytesPerChar(MysqlDefs.NO_CHARSET_INFO, "UnicodeBig")); + } } diff --git a/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessConnectionTest.java b/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessConnectionTest.java index 02ac1338050..91f25bd5b1c 100644 --- a/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessConnectionTest.java +++ b/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessConnectionTest.java @@ -1,8 +1,6 @@ package com.flipkart.vitess.jdbc; import com.flipkart.vitess.util.Constants; -import com.flipkart.vitess.util.MysqlDefs; -import com.flipkart.vitess.util.charset.CharsetMapping; import com.google.common.util.concurrent.Futures; import com.youtube.vitess.client.Context; import com.youtube.vitess.client.SQLFuture; @@ -205,46 +203,4 @@ public class VitessConnectionTest extends BaseTest { Assert.assertEquals(Topodata.TabletType.REPLICA, conn.getTabletType()); Assert.assertEquals(true, conn.getBlobsAreStrings()); } - - @Test public void testGetEncodingForIndex() throws SQLException { - VitessConnection conn = getVitessConnection(); - - // No default encoding configured, and passing NO_CHARSET_INFO basically says "mysql doesn't know" - // which means don't try looking it up - Assert.assertEquals(null, conn.getEncodingForIndex(MysqlDefs.NO_CHARSET_INFO)); - // Similarly, a null index or one landing out of bounds for the charset index should return null - Assert.assertEquals(null, conn.getEncodingForIndex(Integer.MAX_VALUE)); - Assert.assertEquals(null, conn.getEncodingForIndex(-123)); - - // charsetIndex 25 is MYSQL_CHARSET_NAME_greek, which is a charset with multiple names, ISO8859_7 and greek - // Without an encoding configured in the connection, we should return the first (default) encoding for a charset, - // in this case ISO8859_7 - Assert.assertEquals("ISO-8859-7", conn.getEncodingForIndex(25)); - conn.setEncoding("greek"); - // With an encoding configured, we should return that because it matches one of the names for the charset - Assert.assertEquals("greek", conn.getEncodingForIndex(25)); - - conn.setEncoding(null); - Assert.assertEquals("UTF-8", conn.getEncodingForIndex(33)); - Assert.assertEquals("ISO-8859-1", conn.getEncodingForIndex(63)); - - conn.setEncoding("NOT_REAL"); - // Same tests as the first one, but testing that when there is a default configured, it falls back to that regardless - Assert.assertEquals("NOT_REAL", conn.getEncodingForIndex(MysqlDefs.NO_CHARSET_INFO)); - Assert.assertEquals("NOT_REAL", conn.getEncodingForIndex(Integer.MAX_VALUE)); - Assert.assertEquals("NOT_REAL", conn.getEncodingForIndex(-123)); - } - - @Test public void testGetMaxBytesPerChar() throws SQLException { - VitessConnection conn = getVitessConnection(); - - // Default state when no good info is passed in - Assert.assertEquals(0, conn.getMaxBytesPerChar(MysqlDefs.NO_CHARSET_INFO, null)); - // use passed collation index - Assert.assertEquals(3, conn.getMaxBytesPerChar(CharsetMapping.MYSQL_COLLATION_INDEX_utf8, null)); - // use first, if both are passed and valid - Assert.assertEquals(3, conn.getMaxBytesPerChar(CharsetMapping.MYSQL_COLLATION_INDEX_utf8, "UnicodeBig")); - // use passed default charset - Assert.assertEquals(2, conn.getMaxBytesPerChar(MysqlDefs.NO_CHARSET_INFO, "UnicodeBig")); - } } diff --git a/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessDatabaseMetadataTest.java b/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessDatabaseMetadataTest.java index 6f3bf170d85..c228305ca0f 100644 --- a/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessDatabaseMetadataTest.java +++ b/java/jdbc/src/test/java/com/flipkart/vitess/jdbc/VitessDatabaseMetadataTest.java @@ -1,11 +1,11 @@ package com.flipkart.vitess.jdbc; -import com.flipkart.vitess.jdbc.*; import com.flipkart.vitess.util.Constants; import com.google.protobuf.ByteString; import com.youtube.vitess.client.cursor.Cursor; import com.youtube.vitess.client.cursor.SimpleCursor; import com.youtube.vitess.proto.Query; + import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -13,14 +13,18 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import java.sql.*; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Types; import java.util.ArrayList; import java.util.Properties; /** * Created by ashudeep.sharma on 08/03/16. */ -@RunWith(PowerMockRunner.class) @PrepareForTest(VitessMySQLDatabaseMetadata.class) public class VitessDatabaseMetadataTest { +@RunWith(PowerMockRunner.class) @PrepareForTest(VitessMySQLDatabaseMetadata.class) public class VitessDatabaseMetadataTest extends BaseTest { private ResultSet resultSet; @@ -936,7 +940,38 @@ @Test public void getTablesTest() throws SQLException, Exception { String sql = "SHOW FULL TABLES FROM `vt` LIKE '%'"; - Cursor mockedCursor = new SimpleCursor(Query.QueryResult.newBuilder() + Cursor mockedCursor = getTablesCursor(); + + VitessStatement vitessStatement = PowerMockito.mock(VitessStatement.class); + PowerMockito.whenNew(VitessStatement.class).withAnyArguments().thenReturn(vitessStatement); + PowerMockito.when(vitessStatement.executeQuery(sql)) + .thenReturn(new VitessResultSet(mockedCursor)); + + VitessDatabaseMetaData vitessDatabaseMetaData = new VitessMySQLDatabaseMetadata(getVitessConnection()); + ResultSet actualResultSet = vitessDatabaseMetaData.getTables("vt", null, null, null); + ResultSet expectedResultSet = new VitessResultSet(mockedCursor); + + assertResultSetEquals(actualResultSet, expectedResultSet); + } + + @Test public void getTablesProperResultTypeTest() throws SQLException, Exception { + + String sql = "SHOW FULL TABLES FROM `vt` LIKE '%'"; + Cursor mockedCursor = getTablesCursor(); + + VitessStatement vitessStatement = PowerMockito.mock(VitessStatement.class); + PowerMockito.whenNew(VitessStatement.class).withAnyArguments().thenReturn(vitessStatement); + PowerMockito.when(vitessStatement.executeQuery(sql)) + .thenReturn(new VitessResultSet(mockedCursor)); + + VitessDatabaseMetaData vitessDatabaseMetaData = new VitessMySQLDatabaseMetadata(getVitessConnection()); + ResultSet actualResultSet = vitessDatabaseMetaData.getTables("vt", null, null, null); + actualResultSet.next(); + Assert.assertEquals(String.class, actualResultSet.getObject("TABLE_CAT").getClass()); + } + + private Cursor getTablesCursor() throws Exception { + return new SimpleCursor(Query.QueryResult.newBuilder() .addFields(Query.Field.newBuilder().setName("TABLE_CAT").setType(Query.Type.VARCHAR)) .addFields(Query.Field.newBuilder().setName("TABLE_SCHEM").setType(Query.Type.VARCHAR)) .addFields(Query.Field.newBuilder().setName("TABLE_NAME").setType(Query.Type.VARCHAR)) @@ -974,17 +1009,6 @@ .addLengths("".length()).addLengths("".length()).addLengths("".length()) .setValues(ByteString.copyFromUtf8("TestDB2SampleLocalTemporaryLOCAL TEMPORARY"))) .build()); - - VitessStatement vitessStatement = PowerMockito.mock(VitessStatement.class); - PowerMockito.whenNew(VitessStatement.class).withAnyArguments().thenReturn(vitessStatement); - PowerMockito.when(vitessStatement.executeQuery(sql)) - .thenReturn(new VitessResultSet(mockedCursor)); - - VitessDatabaseMetaData vitessDatabaseMetaData = new VitessMySQLDatabaseMetadata(null); - ResultSet actualResultSet = vitessDatabaseMetaData.getTables("vt", null, null, null); - ResultSet expectedResultSet = new VitessResultSet(mockedCursor); - - assertResultSetEquals(actualResultSet, expectedResultSet); } @Test public void getColumnsTest() throws SQLException, Exception {