You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ti...@apache.org on 2024/12/13 12:02:17 UTC
(ignite) branch application_context updated: IGNITE-23722 Map JDBC ClientInfo to ApplicationAttributes (#11724)
This is an automated email from the ASF dual-hosted git repository.
timoninmaxim pushed a commit to branch application_context
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/application_context by this push:
new ba33a25948d IGNITE-23722 Map JDBC ClientInfo to ApplicationAttributes (#11724)
ba33a25948d is described below
commit ba33a25948d9d8c5a8cec9db0eed25ec12f83b94
Author: Maksim Timonin <ti...@apache.org>
AuthorDate: Fri Dec 13 15:02:08 2024 +0300
IGNITE-23722 Map JDBC ClientInfo to ApplicationAttributes (#11724)
---
.../query/calcite/jdbc/JdbcSetClientInfoTest.java | 291 +++++++++++++++++++++
.../ignite/testsuites/IgniteCalciteTestSuite.java | 2 +
.../jdbc/thin/JdbcThinConnectionSelfTest.java | 2 +-
.../internal/jdbc/thin/JdbcThinConnection.java | 28 +-
.../odbc/jdbc/JdbcBatchExecuteRequest.java | 2 +-
.../odbc/jdbc/JdbcClientInfoAwareRequest.java | 70 +++++
.../odbc/jdbc/JdbcQueryExecuteRequest.java | 2 +-
.../processors/odbc/jdbc/JdbcRequestHandler.java | 3 +
.../processors/odbc/jdbc/JdbcThinFeature.java | 6 +-
.../processors/query/GridQueryProcessor.java | 2 +
.../processors/query/SqlClientContext.java | 17 ++
11 files changed, 418 insertions(+), 7 deletions(-)
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcSetClientInfoTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcSetClientInfoTest.java
new file mode 100644
index 00000000000..6b47c5ce7d6
--- /dev/null
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcSetClientInfoTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.query.calcite.jdbc;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+import java.util.Properties;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlFunction;
+import org.apache.ignite.calcite.CalciteQueryEngineConfiguration;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.SqlConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.resources.SessionContextProviderResource;
+import org.apache.ignite.session.SessionContext;
+import org.apache.ignite.session.SessionContextProvider;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.jetbrains.annotations.Nullable;
+import org.junit.Test;
+
+/** */
+public class JdbcSetClientInfoTest extends GridCommonAbstractTest {
+ /** */
+ private static final String SESSION_ID = "sessionId";
+
+ /** */
+ private static final String URL = "jdbc:ignite:thin://127.0.0.1";
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String instanceName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(instanceName);
+
+ cfg.setSqlConfiguration(new SqlConfiguration()
+ .setQueryEnginesConfiguration(new CalciteQueryEngineConfiguration().setDefault(true)));
+
+ cfg.setCacheConfiguration(new CacheConfiguration<>()
+ .setName(DEFAULT_CACHE_NAME)
+ .setSqlSchema("PUBLIC")
+ .setSqlFunctionClasses(SessionContextFunctions.class));
+
+ return cfg;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ IgniteEx ign = startGrids(3);
+
+ query(ign, "create table PUBLIC.MYTABLE(id int primary key, sessionId varchar);");
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() {
+ stopAllGrids();
+ }
+
+ /** */
+ @Test
+ public void testSetClientInfo() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ checkSessionId(conn, null);
+ checkSessionId(conn, "1234");
+ }
+ }
+
+ /** */
+ @Test
+ public void testResetClientInfo() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ checkSessionId(conn, "1234");
+ checkSessionId(conn, "4567");
+ checkSessionId(conn, null);
+ }
+ }
+
+ /** */
+ @Test
+ public void testWhereClause() throws Exception {
+ for (int i = 0; i < 100; i++) {
+ String sesId = i % 2 == 0 ? "1" : "2";
+
+ query(grid(0), "insert into PUBLIC.MYTABLE(id, sessionId) values (?, ?);", i, sesId);
+ }
+
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ for (String sesId: F.asList("1", "2")) {
+ setClientInfo(conn, sesId);
+
+ ResultSet set = jdbcQuery(conn, "select * from PUBLIC.MYTABLE where sessionId = sessionId();");
+
+ int size = 0;
+
+ while (set.next()) {
+ String actSesId = set.getString("sessionId");
+
+ assertEquals(sesId, actSesId);
+
+ size++;
+ }
+
+ assertEquals(50, size);
+ }
+ }
+ }
+
+ /** */
+ @Test
+ public void testInsertClause() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ for (int i = 0; i < 100; i++) {
+ String sesId = i % 2 == 0 ? "1" : "2";
+
+ setClientInfo(conn, sesId);
+
+ jdbcQuery(conn, "insert into PUBLIC.MYTABLE(id, sessionId) values (" + i + ", sessionId());");
+ }
+ }
+
+ List<List<?>> res = query(grid(0), "select * from PUBLIC.MYTABLE where sessionId = 1");
+
+ assertEquals(50, res.size());
+
+ res = query(grid(0), "select * from PUBLIC.MYTABLE where sessionId = 2");
+
+ assertEquals(50, res.size());
+ }
+
+ /** */
+ @Test
+ public void testNestedQuery() throws Exception {
+ for (int i = 0; i < 100; i++) {
+ String sesId = i % 2 == 0 ? "1" : "2";
+
+ query(grid(0), "insert into PUBLIC.MYTABLE(id, sessionId) values (?, ?);", i, sesId);
+ }
+
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ String sesId = "1";
+
+ setClientInfo(conn, sesId);
+
+ ResultSet set = jdbcQuery(conn, "select * from PUBLIC.MYTABLE where sessionId = (select sessionId());");
+
+ int size = 0;
+
+ while (set.next()) {
+ String actSesId = set.getString("sessionId");
+
+ assertEquals(sesId, actSesId);
+
+ size++;
+ }
+
+ assertEquals(50, size);
+ }
+ }
+
+ /** */
+ @Test
+ public void testMultipleQueries() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ setClientInfo(conn, "1");
+
+ jdbcQuery(conn,
+ "insert into PUBLIC.MYTABLE(id, sessionId) values (0, sessionId());" +
+ "insert into PUBLIC.MYTABLE(id, sessionId) values (1, sessionId());" +
+ "insert into PUBLIC.MYTABLE(id, sessionId) values (2, sessionId());" +
+ "insert into PUBLIC.MYTABLE(id, sessionId) values (3, sessionId());" +
+ "insert into PUBLIC.MYTABLE(id, sessionId) values (4, sessionId())");
+ }
+
+ List<List<?>> res = query(grid(0), "select * from PUBLIC.MYTABLE where sessionId = 1");
+
+ assertEquals(5, res.size());
+ }
+
+ /** */
+ @Test
+ public void testUpdateStatement() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ setClientInfo(conn, "1");
+
+ Statement statement = conn.createStatement();
+
+ statement.executeUpdate(
+ "insert into PUBLIC.MYTABLE(id, sessionId) values (0, sessionId());" +
+ "insert into PUBLIC.MYTABLE(id, sessionId) values (1, sessionId());" +
+ "insert into PUBLIC.MYTABLE(id, sessionId) values (2, sessionId());" +
+ "insert into PUBLIC.MYTABLE(id, sessionId) values (3, sessionId());" +
+ "insert into PUBLIC.MYTABLE(id, sessionId) values (4, sessionId())");
+ }
+
+ List<List<?>> res = query(grid(0), "select * from PUBLIC.MYTABLE where sessionId = 1");
+
+ assertEquals(5, res.size());
+ }
+
+ /** */
+ @Test
+ public void testBatchStatement() throws Exception {
+ try (Connection conn = DriverManager.getConnection(URL)) {
+ setClientInfo(conn, "1");
+
+ Statement statement = conn.createStatement();
+
+ for (int i = 0; i < 5; i++)
+ statement.addBatch("insert into PUBLIC.MYTABLE(id, sessionId) values (" + i + ", sessionId());");
+
+ statement.executeBatch();
+ }
+
+ List<List<?>> res = query(grid(0), "select * from PUBLIC.MYTABLE where sessionId = 1");
+
+ assertEquals(5, res.size());
+ }
+
+ /** */
+ private void setClientInfo(Connection conn, @Nullable String sesId) throws Exception {
+ Properties props = new Properties();
+
+ if (sesId != null)
+ props.put(SESSION_ID, sesId);
+
+ conn.setClientInfo(props);
+ }
+
+ /** */
+ private List<List<?>> query(IgniteEx ign, String sql, Object... args) {
+ return ign.context().query().querySqlFields(new SqlFieldsQuery(sql).setArgs(args), false).getAll();
+ }
+
+ /** */
+ private @Nullable ResultSet jdbcQuery(Connection conn, String sql) throws SQLException {
+ Statement statement = conn.createStatement();
+
+ boolean res = statement.execute(sql);
+
+ if (res)
+ return statement.getResultSet();
+
+ return null;
+ }
+
+ /** */
+ private void checkSessionId(Connection conn, @Nullable String sesId) throws Exception {
+ setClientInfo(conn, sesId);
+
+ ResultSet set = jdbcQuery(conn, "select sessionId() as SESSION_ID;");
+
+ set.next();
+
+ String actSesId = set.getString("SESSION_ID");
+
+ assertEquals(sesId, actSesId);
+ }
+
+ /** */
+ public static class SessionContextFunctions {
+ /** */
+ @SessionContextProviderResource
+ public SessionContextProvider sesCtxProv;
+
+ /** */
+ @QuerySqlFunction
+ public String sessionId() {
+ SessionContext sesCtx = sesCtxProv.getSessionContext();
+
+ return sesCtx == null ? null : sesCtx.getAttribute(SESSION_ID);
+ }
+ }
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
index df909b46f17..4a0cfa7e0cb 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java
@@ -24,6 +24,7 @@ import org.apache.ignite.internal.processors.query.calcite.exec.LogicalRelImplem
import org.apache.ignite.internal.processors.query.calcite.exec.NumericTypesPrecisionsTest;
import org.apache.ignite.internal.processors.query.calcite.exec.exp.IgniteSqlFunctionsTest;
import org.apache.ignite.internal.processors.query.calcite.exec.tracker.MemoryTrackerTest;
+import org.apache.ignite.internal.processors.query.calcite.jdbc.JdbcSetClientInfoTest;
import org.apache.ignite.internal.processors.query.calcite.jdbc.JdbcThinTransactionalSelfTest;
import org.apache.ignite.internal.processors.query.calcite.message.CalciteCommunicationMessageSerializationTest;
import org.apache.ignite.internal.processors.query.calcite.sql.SqlCustomParserTest;
@@ -60,6 +61,7 @@ import org.junit.runners.Suite;
SqlTransactionsUnsupportedModesTest.class,
JdbcThinTransactionalSelfTest.class,
+ JdbcSetClientInfoTest.class,
})
public class IgniteCalciteTestSuite {
}
diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
index ce06c333928..9a448d42d49 100644
--- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinConnectionSelfTest.java
@@ -1893,7 +1893,7 @@ public class JdbcThinConnectionSelfTest extends JdbcThinAbstractSelfTest {
assertNotNull(propsResult);
- assertTrue(propsResult.isEmpty());
+ assertFalse(propsResult.isEmpty());
conn.close();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
index 0de9affbc1f..bdb9cecfad7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc/thin/JdbcThinConnection.java
@@ -100,6 +100,7 @@ import org.apache.ignite.internal.processors.odbc.jdbc.JdbcBinaryTypePutRequest;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcBulkLoadBatchRequest;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcCachePartitionsRequest;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcCachePartitionsResult;
+import org.apache.ignite.internal.processors.odbc.jdbc.JdbcClientInfoAwareRequest;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcOrderedBatchExecuteRequest;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcOrderedBatchExecuteResult;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcQuery;
@@ -277,6 +278,9 @@ public class JdbcThinConnection implements Connection {
/** Marshaller context. */
private final JdbcMarshallerContext marshCtx;
+ /** Client info. */
+ private @Nullable Map<String, String> clientInfo;
+
/**
* Creates new connection.
*
@@ -321,7 +325,7 @@ public class JdbcThinConnection implements Connection {
marsh.setContext(marshCtx);
BinaryConfiguration binCfg = new BinaryConfiguration().setCompactFooter(true);
-
+
BinaryContext ctx = new BinaryContext(metaHnd, new IgniteConfiguration(), new NullLogger());
ctx.configure(marsh, binCfg);
@@ -905,26 +909,41 @@ public class JdbcThinConnection implements Connection {
@Override public void setClientInfo(String name, String val) throws SQLClientInfoException {
if (closed)
throw new SQLClientInfoException("Connection is closed.", null);
+
+ if (clientInfo == null)
+ clientInfo = new HashMap<>();
+
+ clientInfo.put(name, val);
}
/** {@inheritDoc} */
@Override public void setClientInfo(Properties props) throws SQLClientInfoException {
if (closed)
throw new SQLClientInfoException("Connection is closed.", null);
+
+ clientInfo = new HashMap<>();
+
+ for (String propName : props.stringPropertyNames())
+ clientInfo.put(propName, props.getProperty(propName));
}
/** {@inheritDoc} */
@Override public String getClientInfo(String name) throws SQLException {
ensureNotClosed();
- return null;
+ return clientInfo.get(name);
}
/** {@inheritDoc} */
@Override public Properties getClientInfo() throws SQLException {
ensureNotClosed();
- return new Properties();
+ Properties ret = new Properties();
+
+ if (clientInfo != null)
+ ret.putAll(clientInfo);
+
+ return ret;
}
/** {@inheritDoc} */
@@ -1122,6 +1141,9 @@ public class JdbcThinConnection implements Connection {
if (req instanceof JdbcQueryExecuteRequest)
qryReq = (JdbcQueryExecuteRequest)req;
+ if (req instanceof JdbcClientInfoAwareRequest)
+ ((JdbcClientInfoAwareRequest)req).clientInfo(clientInfo);
+
JdbcResponse res = cliIo.sendRequest(req, stmt);
if (res.status() == IgniteQueryErrorCode.QUERY_CANCELED && stmt != null &&
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteRequest.java
index fe6f43b975b..12f9314f09a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcBatchExecuteRequest.java
@@ -30,7 +30,7 @@ import org.jetbrains.annotations.Nullable;
/**
* JDBC batch execute request.
*/
-public class JdbcBatchExecuteRequest extends JdbcRequest {
+public class JdbcBatchExecuteRequest extends JdbcClientInfoAwareRequest {
/** Schema name. */
private String schemaName;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcClientInfoAwareRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcClientInfoAwareRequest.java
new file mode 100644
index 00000000000..98e1f8f5fdc
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcClientInfoAwareRequest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.internal.processors.odbc.jdbc;
+
+import java.util.Map;
+import org.apache.ignite.binary.BinaryObjectException;
+import org.apache.ignite.internal.binary.BinaryReaderExImpl;
+import org.apache.ignite.internal.binary.BinaryWriterExImpl;
+import org.apache.ignite.internal.jdbc.thin.JdbcThinConnection;
+import org.jetbrains.annotations.Nullable;
+
+/** Request with client info set with {@link java.sql.Connection#setClientInfo}. */
+public class JdbcClientInfoAwareRequest extends JdbcRequest {
+ /** Client info set with {@link JdbcThinConnection#setClientInfo} methods. */
+ private @Nullable Map<String, String> clientInfo;
+
+ /**
+ * @param type Command type.
+ */
+ public JdbcClientInfoAwareRequest(byte type) {
+ super(type);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeBinary(
+ BinaryWriterExImpl writer,
+ JdbcProtocolContext protoCtx
+ ) throws BinaryObjectException {
+ super.writeBinary(writer, protoCtx);
+
+ if (protoCtx.isFeatureSupported(JdbcThinFeature.CLIENT_INFO))
+ writer.writeMap(clientInfo);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readBinary(
+ BinaryReaderExImpl reader,
+ JdbcProtocolContext protoCtx
+ ) throws BinaryObjectException {
+ super.readBinary(reader, protoCtx);
+
+ if (protoCtx.isFeatureSupported(JdbcThinFeature.CLIENT_INFO))
+ clientInfo = reader.readMap();
+ }
+
+ /** @return Client info. */
+ public @Nullable Map<String, String> clientInfo() {
+ return clientInfo;
+ }
+
+ /** @param clientInfo Client info. */
+ public void clientInfo(@Nullable Map<String, String> clientInfo) {
+ this.clientInfo = clientInfo;
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java
index 52a839071d1..dc3d346a249 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcQueryExecuteRequest.java
@@ -30,7 +30,7 @@ import org.jetbrains.annotations.Nullable;
/**
* JDBC query execute request.
*/
-public class JdbcQueryExecuteRequest extends JdbcRequest {
+public class JdbcQueryExecuteRequest extends JdbcClientInfoAwareRequest {
/** Schema name. */
private String schemaName;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
index bbcd750f587..c8754fa88ce 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java
@@ -306,6 +306,9 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler, ClientT
return new JdbcResponse(IgniteQueryErrorCode.UNKNOWN,
"Failed to handle JDBC request because node is stopping.");
+ if (req instanceof JdbcClientInfoAwareRequest)
+ cliCtx.applicationAttributes(((JdbcClientInfoAwareRequest)req).clientInfo());
+
JdbcResponse resp;
try {
switch (req.type()) {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcThinFeature.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcThinFeature.java
index a51c4374cd1..f4cd046fbd6 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcThinFeature.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcThinFeature.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.processors.odbc.jdbc;
+import java.sql.Connection;
import java.util.EnumSet;
import org.apache.ignite.internal.ThinProtocolFeature;
@@ -36,7 +37,10 @@ public enum JdbcThinFeature implements ThinProtocolFeature {
QUERY_TIMEOUT(3),
/** Transaction aware queries. */
- TX_AWARE_QUERIES(4);
+ TX_AWARE_QUERIES(4),
+
+ /** Send {@link Connection#setClientInfo(String, String)} with messages. */
+ CLIENT_INFO(5);
/** */
private static final EnumSet<JdbcThinFeature> ALL_FEATURES_AS_ENUM_SET = EnumSet.allOf(JdbcThinFeature.class);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index 4955418e5f7..78b47d4f0fd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -3080,6 +3080,8 @@ public class GridQueryProcessor extends GridProcessorAdapter {
if (opCtx != null && opCtx.applicationAttributes() != null)
sesCtx = new SessionContextImpl(opCtx.applicationAttributes());
}
+ else if (cliCtx != null && cliCtx.applicationAttributes() != null)
+ sesCtx = new SessionContextImpl(cliCtx.applicationAttributes());
QueryContext qryCtx = QueryContext.of(
qry,
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/SqlClientContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/SqlClientContext.java
index e8b581e0bde..3d8a33dfabc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/SqlClientContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/SqlClientContext.java
@@ -112,6 +112,9 @@ public class SqlClientContext implements AutoCloseable {
/** Transaction label. */
private String lb;
+ /** Application attributes. */
+ private @Nullable Map<String, String> appAtts;
+
/** Logger. */
private final IgniteLogger log;
@@ -398,6 +401,20 @@ public class SqlClientContext implements AutoCloseable {
return lb;
}
+ /**
+ * @param appAtts Application attributes.
+ */
+ public void applicationAttributes(Map<String, String> appAtts) {
+ this.appAtts = appAtts;
+ }
+
+ /**
+ * @return Application attributes.
+ */
+ public Map<String, String> applicationAttributes() {
+ return appAtts;
+ }
+
/** {@inheritDoc} */
@Override public void close() throws Exception {
if (streamers == null)