diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/TFrontendService.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/TFrontendService.scala index d38c9bc81..9f97ff89a 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/TFrontendService.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/TFrontendService.scala @@ -537,7 +537,22 @@ abstract class TFrontendService(name: String) override def SetClientInfo(req: TSetClientInfoReq): TSetClientInfoResp = { debug(req.toString) val resp = new TSetClientInfoResp - resp.setStatus(KyuubiSQLException.featureNotSupported().toTStatus) + if (req.isSetConfiguration) { + val sessionHandle = SessionHandle(req.getSessionHandle) + val stringBuilder = new StringBuilder("Client information for ") + .append(sessionHandle) + .append(": ") + val entries = req.getConfiguration.entrySet.asScala.toSeq + entries.headOption.foreach(e => { + stringBuilder.append(e.getKey).append(" = ").append(e.getValue) + }) + entries.tail.foreach { e => + stringBuilder.append(", ") + stringBuilder.append(e.getKey).append(" = ").append(e.getValue) + } + info(stringBuilder.toString()) + } + resp.setStatus(OK_STATUS) resp } diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/HiveEngineTests.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/HiveEngineTests.scala index f48b97337..d61ea2f75 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/HiveEngineTests.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/HiveEngineTests.scala @@ -427,4 +427,19 @@ trait HiveEngineTests extends HiveJDBCTestHelper { assert(typeInfo.getInt(DATA_TYPE) === java.sql.Types.OTHER) } } + + test("test setClientInfo") { + assume(SystemUtils.isJavaVersionAtMost(JavaVersion.JAVA_1_8)) + withJdbcStatement() { statement => + val res = statement.getConnection.getMetaData.getClientInfoProperties + assert(res.next()) + assert(res.getString(1) === "ApplicationName") + assert(res.getInt("MAX_LEN") === 1000); + assert(!res.next()); + + val connection = statement.getConnection + connection.setClientInfo("ApplicationName", "test kyuubi hive jdbc") + assert(connection.getClientInfo("ApplicationName") == "test kyuubi hive jdbc") + } + } } diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/operation/SparkMetadataTests.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/operation/SparkMetadataTests.scala index 8ce1cb749..0c8ae299f 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/operation/SparkMetadataTests.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/operation/SparkMetadataTests.scala @@ -396,7 +396,6 @@ trait SparkMetadataTests extends HiveJDBCTestHelper { () => metaData.getRowIdLifetime, () => metaData.supportsStoredFunctionsUsingCallSyntax, () => metaData.autoCommitFailureClosesAllResultSets, - () => metaData.getClientInfoProperties, () => metaData.getFunctionColumns("", "%", "%", "%"), () => metaData.getPseudoColumns("", "%", "%", "%"), () => metaData.generatedKeyAlwaysReturned).foreach { func => @@ -405,6 +404,7 @@ trait SparkMetadataTests extends HiveJDBCTestHelper { } assert(metaData.allTablesAreSelectable) + assert(metaData.getClientInfoProperties.next) assert(metaData.getDatabaseProductName === "Apache Kyuubi (Incubating)") assert(metaData.getDatabaseProductVersion === KYUUBI_VERSION) assert(metaData.getDriverName === "Kyuubi Project Hive JDBC Shaded Client") diff --git a/kyuubi-hive-jdbc-shaded/pom.xml b/kyuubi-hive-jdbc-shaded/pom.xml index 11df23354..488043f69 100644 --- a/kyuubi-hive-jdbc-shaded/pom.xml +++ b/kyuubi-hive-jdbc-shaded/pom.xml @@ -148,6 +148,11 @@ commons-lang + + org.apache.commons + commons-lang3 + + org.apache.curator curator-framework @@ -291,6 +296,10 @@ org.apache.commons.lang ${kyuubi.shade.packageName}.org.apache.commons.lang + + org.apache.commons.lang3 + ${kyuubi.shade.packageName}.org.apache.commons.lang3 + org.apache.curator ${kyuubi.shade.packageName}.org.apache.curator diff --git a/kyuubi-hive-jdbc-shaded/src/main/resources/META-INF/LICENSE b/kyuubi-hive-jdbc-shaded/src/main/resources/META-INF/LICENSE index 769b516c3..ce3e2f808 100644 --- a/kyuubi-hive-jdbc-shaded/src/main/resources/META-INF/LICENSE +++ b/kyuubi-hive-jdbc-shaded/src/main/resources/META-INF/LICENSE @@ -219,6 +219,7 @@ com.google.guava:failureaccess com.google.guava:guava commons-codec:commons-codec commons-lang:commons-lang +org.apache.commons:commons-lang3 org.apache.curator:curator-framework org.apache.curator:curator-client org.apache.httpcomponents:httpclient diff --git a/kyuubi-hive-jdbc/pom.xml b/kyuubi-hive-jdbc/pom.xml index acad5aeb7..31334bce5 100644 --- a/kyuubi-hive-jdbc/pom.xml +++ b/kyuubi-hive-jdbc/pom.xml @@ -147,6 +147,11 @@ commons-lang + + org.apache.commons + commons-lang3 + + com.google.guava guava diff --git a/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiConnection.java b/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiConnection.java index c59e017cf..3cde68cf3 100644 --- a/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiConnection.java +++ b/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiConnection.java @@ -105,6 +105,7 @@ public class KyuubiConnection implements java.sql.Connection, KyuubiLoggable { private volatile boolean launchEngineOpCompleted = false; private boolean isBeeLineMode; + private Properties clientInfo; public KyuubiConnection(String uri, Properties info) throws SQLException { setupLoginTimeout(); @@ -1119,8 +1120,7 @@ public class KyuubiConnection implements java.sql.Connection, KyuubiLoggable { @Override public Properties getClientInfo() throws SQLException { - // TODO Auto-generated method stub - throw new SQLFeatureNotSupportedException("Method not supported"); + return clientInfo == null ? new Properties() : clientInfo; } /* @@ -1131,8 +1131,8 @@ public class KyuubiConnection implements java.sql.Connection, KyuubiLoggable { @Override public String getClientInfo(String name) throws SQLException { - // TODO Auto-generated method stub - throw new SQLFeatureNotSupportedException("Method not supported"); + if (clientInfo == null) return null; + return clientInfo.getProperty(name); } /* @@ -1463,8 +1463,8 @@ public class KyuubiConnection implements java.sql.Connection, KyuubiLoggable { @Override public void setClientInfo(Properties properties) throws SQLClientInfoException { - // TODO Auto-generated method stub - throw new SQLClientInfoException("Method not supported", null); + clientInfo = properties; + setClientInfo(); } /* @@ -1475,8 +1475,30 @@ public class KyuubiConnection implements java.sql.Connection, KyuubiLoggable { @Override public void setClientInfo(String name, String value) throws SQLClientInfoException { - // TODO Auto-generated method stub - throw new SQLClientInfoException("Method not supported", null); + if (clientInfo == null) { + clientInfo = new Properties(); + } + clientInfo.put(name, value); + setClientInfo(); + } + + private void setClientInfo() throws SQLClientInfoException { + TSetClientInfoReq req = new TSetClientInfoReq(sessHandle); + Map map = new HashMap<>(); + if (clientInfo != null) { + for (Entry e : clientInfo.entrySet()) { + if (e.getKey() == null || e.getValue() == null) continue; + map.put(e.getKey().toString(), e.getValue().toString()); + } + } + req.setConfiguration(map); + try { + TSetClientInfoResp openResp = client.SetClientInfo(req); + Utils.verifySuccess(openResp.getStatus()); + } catch (TException | SQLException e) { + LOG.error("Error setting client info", e); + throw new SQLClientInfoException("Error setting client info", null, e); + } } /* diff --git a/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiDatabaseMetaData.java b/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiDatabaseMetaData.java index 9ef9eeff0..501bcb6e9 100644 --- a/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiDatabaseMetaData.java +++ b/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiDatabaseMetaData.java @@ -23,12 +23,16 @@ import java.sql.ResultSet; import java.sql.RowIdLifetime; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.List; import java.util.jar.Attributes; import org.apache.hadoop.hive.metastore.TableType; +import org.apache.hadoop.hive.metastore.api.FieldSchema; import org.apache.hive.service.cli.GetInfoType; import org.apache.hive.service.cli.HiveSQLException; +import org.apache.hive.service.cli.TableSchema; import org.apache.hive.service.rpc.thrift.*; import org.apache.kyuubi.jdbc.KyuubiHiveDriver; import org.apache.thrift.TException; @@ -122,8 +126,46 @@ public class KyuubiDatabaseMetaData implements DatabaseMetaData { .build(); } + private static final class ClientInfoPropertiesResultSet extends KyuubiMetaDataResultSet { + private static final String[] COLUMNS = {"NAME", "MAX_LEN", "DEFAULT_VALUE", "DESCRIPTION"}; + private static final String[] COLUMN_TYPES = {"STRING", "INT", "STRING", "STRING"}; + + private static final Object[][] DATA = { + {"ApplicationName", 1000, null, null}, + }; + private int index = -1; + + public ClientInfoPropertiesResultSet() throws SQLException { + super(Arrays.asList(COLUMNS), Arrays.asList(COLUMN_TYPES), null); + List fieldSchemas = new ArrayList<>(COLUMNS.length); + for (int i = 0; i < COLUMNS.length; ++i) { + fieldSchemas.add(new FieldSchema(COLUMNS[i], COLUMN_TYPES[i], null)); + } + setSchema(new TableSchema(fieldSchemas)); + } + + @Override + public boolean next() throws SQLException { + if ((++index) >= DATA.length) return false; + row = Arrays.copyOf(DATA[index], DATA[index].length); + return true; + } + + public T getObject(String columnLabel, Class type) throws SQLException { + for (int i = 0; i < COLUMNS.length; ++i) { + if (COLUMNS[i].equalsIgnoreCase(columnLabel)) return getObject(i, type); + } + throw new SQLException("No column " + columnLabel); + } + + @SuppressWarnings("unchecked") + public T getObject(int columnIndex, Class type) throws SQLException { + return (T) super.getObject(columnIndex); + } + } + public ResultSet getClientInfoProperties() throws SQLException { - throw new SQLFeatureNotSupportedException("Method not supported"); + return new ClientInfoPropertiesResultSet(); } public ResultSet getColumnPrivileges(