/* * Licensed 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. */ package com.facebook.presto.plugin.mysql; import com.facebook.presto.plugin.jdbc.BaseJdbcClient; import com.facebook.presto.plugin.jdbc.BaseJdbcConfig; import com.facebook.presto.plugin.jdbc.JdbcConnectorId; import com.facebook.presto.spi.SchemaTableName; import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.VarcharType; import com.facebook.presto.spi.type.Varchars; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableSet; import com.mysql.jdbc.Driver; import com.mysql.jdbc.Statement; import javax.inject.Inject; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Set; import static java.util.Locale.ENGLISH; public class MySqlClient extends BaseJdbcClient { @Inject public MySqlClient(JdbcConnectorId connectorId, BaseJdbcConfig config, MySqlConfig mySqlConfig) throws SQLException { super(connectorId, config, "`", new Driver()); connectionProperties.setProperty("nullCatalogMeansCurrent", "false"); connectionProperties.setProperty("useUnicode", "true"); connectionProperties.setProperty("characterEncoding", "utf8"); connectionProperties.setProperty("tinyInt1isBit", "false"); if (mySqlConfig.isAutoReconnect()) { connectionProperties.setProperty("autoReconnect", String.valueOf(mySqlConfig.isAutoReconnect())); connectionProperties.setProperty("maxReconnects", String.valueOf(mySqlConfig.getMaxReconnects())); } if (mySqlConfig.getConnectionTimeout() != null) { connectionProperties.setProperty("connectTimeout", String.valueOf(mySqlConfig.getConnectionTimeout().toMillis())); } } @Override public Set<String> getSchemaNames() { // for MySQL, we need to list catalogs instead of schemas try (Connection connection = driver.connect(connectionUrl, connectionProperties); ResultSet resultSet = connection.getMetaData().getCatalogs()) { ImmutableSet.Builder<String> schemaNames = ImmutableSet.builder(); while (resultSet.next()) { String schemaName = resultSet.getString("TABLE_CAT").toLowerCase(ENGLISH); // skip internal schemas if (!schemaName.equals("information_schema") && !schemaName.equals("mysql")) { schemaNames.add(schemaName); } } return schemaNames.build(); } catch (SQLException e) { throw Throwables.propagate(e); } } @Override public PreparedStatement getPreparedStatement(Connection connection, String sql) throws SQLException { PreparedStatement statement = connection.prepareStatement(sql); if (statement.isWrapperFor(Statement.class)) { statement.unwrap(Statement.class).enableStreamingResults(); } return statement; } @Override protected ResultSet getTables(Connection connection, String schemaName, String tableName) throws SQLException { // MySQL maps their "database" to SQL catalogs and does not have schemas DatabaseMetaData metadata = connection.getMetaData(); String escape = metadata.getSearchStringEscape(); return metadata.getTables( schemaName, null, escapeNamePattern(tableName, escape), new String[] {"TABLE", "VIEW"}); } @Override protected SchemaTableName getSchemaTableName(ResultSet resultSet) throws SQLException { // MySQL uses catalogs instead of schemas return new SchemaTableName( resultSet.getString("TABLE_CAT").toLowerCase(ENGLISH), resultSet.getString("TABLE_NAME").toLowerCase(ENGLISH)); } @Override protected String toSqlType(Type type) { if (Varchars.isVarcharType(type)) { VarcharType varcharType = (VarcharType) type; if (varcharType.getLength() <= 255) { return "tinytext"; } if (varcharType.getLength() <= 65535) { return "text"; } if (varcharType.getLength() <= 16777215) { return "mediumtext"; } return "longtext"; } String sqlType = super.toSqlType(type); switch (sqlType) { case "varbinary": return "mediumblob"; case "time with timezone": return "time"; case "timestamp": case "timestamp with timezone": return "datetime"; } return sqlType; } }