This is an automated email from the ASF dual-hosted git repository.

amashenkov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new e91a86fcfaa IGNITE-25588 Sql. Support CURRENT_USER function (#6314)
e91a86fcfaa is described below

commit e91a86fcfaac4aad5ca2e225415552085aacdfde
Author: Andrew V. Mashenkov <[email protected]>
AuthorDate: Mon Aug 4 11:06:42 2025 +0300

    IGNITE-25588 Sql. Support CURRENT_USER function (#6314)
---
 .../internal/jdbc/proto/JdbcQueryEventHandler.java |  4 +-
 .../handler/ClientInboundMessageHandler.java       | 18 +++++--
 .../client/handler/JdbcConnectionContext.java      | 10 +++-
 .../client/handler/JdbcQueryEventHandlerImpl.java  | 17 ++++---
 .../requests/jdbc/ClientJdbcConnectRequest.java    |  6 ++-
 .../requests/sql/ClientSqlExecuteBatchRequest.java |  6 ++-
 .../requests/sql/ClientSqlExecuteRequest.java      |  6 ++-
 .../sql/ClientSqlExecuteScriptRequest.java         |  9 ++--
 .../handler/JdbcQueryEventHandlerImplTest.java     |  4 +-
 .../ignite/jdbc/ItJdbcAuthenticationTest.java      | 58 +++++++++++++++++++++
 .../ignite/jdbc/ItJdbcConnectionSelfTest.java      | 14 +++++
 .../internal/jdbc/JdbcClientQueryEventHandler.java |  5 +-
 .../ignite/internal/jdbc/JdbcConnection.java       |  2 +-
 .../app/client/ItThinClientAuthenticationTest.java | 59 ++++++++++++++++++++++
 .../runner/app/client/ItThinClientSqlTest.java     | 23 +++++++++
 .../internal/sql/engine/ItFunctionsTest.java       | 10 ++++
 .../internal/sql/engine/ItSqlOperatorsTest.java    | 29 +++++++++++
 .../ignite/internal/sql/api/IgniteSqlImpl.java     |  6 ++-
 .../internal/sql/engine/SqlOperationContext.java   | 19 ++++++-
 .../ignite/internal/sql/engine/SqlProperties.java  | 12 +++++
 .../internal/sql/engine/exec/ExecutionContext.java | 10 +++-
 .../sql/engine/exec/ExecutionServiceImpl.java      | 12 +++--
 .../engine/exec/fsm/OptimizingPhaseHandler.java    |  2 +
 .../sql/engine/message/QueryStartRequest.java      |  5 ++
 .../sql/engine/sql/fun/IgniteSqlOperatorTable.java |  3 ++
 .../ignite/internal/sql/engine/util/Commons.java   |  2 +
 .../ignite/internal/sql/docs/OperatorListTest.java |  4 +-
 .../sql/engine/exec/RuntimeSortedIndexTest.java    |  3 +-
 .../sql/engine/exec/rel/AbstractExecutionTest.java |  3 +-
 .../sql/engine/framework/TestBuilders.java         |  3 +-
 .../src/test/resources/docs/operator_list.txt      |  3 ++
 .../internal/sql/engine/util/QueryCheckerImpl.java |  3 +-
 32 files changed, 330 insertions(+), 40 deletions(-)

diff --git 
a/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/JdbcQueryEventHandler.java
 
b/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/JdbcQueryEventHandler.java
index e3a0f68d3aa..778f20d3384 100644
--- 
a/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/JdbcQueryEventHandler.java
+++ 
b/modules/client-common/src/main/java/org/apache/ignite/internal/jdbc/proto/JdbcQueryEventHandler.java
@@ -45,10 +45,10 @@ public interface JdbcQueryEventHandler {
      * Create connection context on a server and returns connection identity.
      *
      * @param timeZoneId Client time-zone ID.
-     *
+     * @param username Current user name.
      * @return A future representing result of the operation.
      */
-    CompletableFuture<JdbcConnectResult> connect(ZoneId timeZoneId);
+    CompletableFuture<JdbcConnectResult> connect(ZoneId timeZoneId, String 
username);
 
     /**
      * {@link JdbcQueryExecuteRequest} command handler.
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
index 98496108d81..99bbbeb7fb1 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
@@ -847,7 +847,7 @@ public class ClientInboundMessageHandler
                 return ClientTupleContainsAllKeysRequest.process(in, 
igniteTables, resources, txManager, clockService, tsTracker);
 
             case ClientOp.JDBC_CONNECT:
-                return ClientJdbcConnectRequest.execute(in, 
jdbcQueryEventHandler);
+                return ClientJdbcConnectRequest.execute(in, 
jdbcQueryEventHandler, resolveCurrentUsername());
 
             case ClientOp.JDBC_EXEC:
                 return ClientJdbcExecuteRequest.execute(in, 
jdbcQueryEventHandler, tsTracker);
@@ -939,7 +939,7 @@ public class ClientInboundMessageHandler
                 return ClientSqlExecuteRequest.process(
                         partitionOperationsExecutor, in, requestId, 
cancelHandles, queryProcessor, resources, metrics, tsTracker,
                         clientContext.hasFeature(SQL_PARTITION_AWARENESS), 
clientContext.hasFeature(SQL_DIRECT_TX_MAPPING), txManager,
-                        clockService, notificationSender(requestId)
+                        clockService, notificationSender(requestId), 
resolveCurrentUsername()
                 );
 
             case ClientOp.OPERATION_CANCEL:
@@ -959,7 +959,7 @@ public class ClientInboundMessageHandler
 
             case ClientOp.SQL_EXEC_SCRIPT:
                 return ClientSqlExecuteScriptRequest.process(
-                        partitionOperationsExecutor, in, queryProcessor, 
requestId, cancelHandles, tsTracker
+                        partitionOperationsExecutor, in, queryProcessor, 
requestId, cancelHandles, tsTracker, resolveCurrentUsername()
                 );
 
             case ClientOp.SQL_QUERY_META:
@@ -969,7 +969,8 @@ public class ClientInboundMessageHandler
 
             case ClientOp.SQL_EXEC_BATCH:
                 return ClientSqlExecuteBatchRequest.process(
-                        partitionOperationsExecutor, in, queryProcessor, 
resources, requestId, cancelHandles, tsTracker
+                        partitionOperationsExecutor, in, queryProcessor, 
resources, requestId, cancelHandles, tsTracker,
+                        resolveCurrentUsername()
                 );
 
             case ClientOp.STREAMER_BATCH_SEND:
@@ -999,6 +1000,15 @@ public class ClientInboundMessageHandler
         }
     }
 
+    /**
+     * Return authenticated user name or {@code unknown} if not authorized.
+     *
+     * @see UserDetails#UNKNOWN
+     */
+    private String resolveCurrentUsername() {
+        return clientContext.userDetails().username();
+    }
+
     private void processOperationInternal(
             ChannelHandlerContext ctx,
             ClientMessageUnpacker in,
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcConnectionContext.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcConnectionContext.java
index 16a03727cc5..5830e557241 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcConnectionContext.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcConnectionContext.java
@@ -45,22 +45,30 @@ class JdbcConnectionContext {
 
     private final ZoneId timeZoneId;
 
+    private final String userName;
+
     private final ConcurrentMap<Long, CancelHandle> cancelHandles = new 
ConcurrentHashMap<>();
 
     private @Nullable TxWithTimeTracker txWithTimeTracker;
 
     JdbcConnectionContext(
             TxManager txManager,
-            ZoneId timeZoneId
+            ZoneId timeZoneId,
+            String userName
     ) {
         this.txManager = txManager;
         this.timeZoneId = timeZoneId;
+        this.userName = userName;
     }
 
     ZoneId timeZoneId() {
         return timeZoneId;
     }
 
+    String userName() {
+        return userName;
+    }
+
     /**
      * Gets the transaction associated with the current connection, starts a 
new one if it doesn't already exist.
      *
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImpl.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImpl.java
index d2456b87d0c..75cb103bd2f 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImpl.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImpl.java
@@ -108,11 +108,12 @@ public class JdbcQueryEventHandlerImpl extends 
JdbcHandlerBase implements JdbcQu
 
     /** {@inheritDoc} */
     @Override
-    public CompletableFuture<JdbcConnectResult> connect(ZoneId timeZoneId) {
+    public CompletableFuture<JdbcConnectResult> connect(ZoneId timeZoneId, 
String username) {
         try {
             JdbcConnectionContext connectionContext = new 
JdbcConnectionContext(
                     txManager,
-                    timeZoneId
+                    timeZoneId,
+                    username
             );
 
             long connectionId = resources.put(new ClientResource(
@@ -153,9 +154,10 @@ public class JdbcQueryEventHandlerImpl extends 
JdbcHandlerBase implements JdbcQu
         boolean multiStatement = req.multiStatement();
         ZoneId timeZoneId = connectionContext.timeZoneId();
         long timeoutMillis = req.queryTimeoutMillis();
+        String userName = connectionContext.userName();
 
         InternalTransaction tx = req.autoCommit() ? null : 
connectionContext.getOrStartTransaction(timeTracker);
-        SqlProperties properties = createProperties(reqStmtType, 
defaultSchemaName, multiStatement, timeZoneId, timeoutMillis);
+        SqlProperties properties = createProperties(reqStmtType, 
defaultSchemaName, multiStatement, timeZoneId, timeoutMillis, userName);
 
         CompletableFuture<AsyncSqlCursor<InternalSqlRow>> result = 
processor.queryAsync(
                 properties,
@@ -177,7 +179,8 @@ public class JdbcQueryEventHandlerImpl extends 
JdbcHandlerBase implements JdbcQu
             String defaultSchemaName,
             boolean multiStatement,
             ZoneId timeZoneId,
-            long queryTimeoutMillis
+            long queryTimeoutMillis,
+            @Nullable String userName
     ) {
         Set<SqlQueryType> allowedTypes;
 
@@ -201,7 +204,8 @@ public class JdbcQueryEventHandlerImpl extends 
JdbcHandlerBase implements JdbcQu
                 .allowedQueryTypes(allowedTypes)
                 .timeZoneId(timeZoneId)
                 .defaultSchema(schemaNameInCanonicalForm)
-                .queryTimeout(queryTimeoutMillis);
+                .queryTimeout(queryTimeoutMillis)
+                .userName(userName);
     }
 
     /** {@inheritDoc} */
@@ -309,7 +313,8 @@ public class JdbcQueryEventHandlerImpl extends 
JdbcHandlerBase implements JdbcQu
                 defaultSchemaName,
                 false,
                 context.timeZoneId(),
-                timeoutMillis
+                timeoutMillis,
+                context.userName()
         );
 
         CompletableFuture<AsyncSqlCursor<InternalSqlRow>> result = 
processor.queryAsync(
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/jdbc/ClientJdbcConnectRequest.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/jdbc/ClientJdbcConnectRequest.java
index 002408043c9..9de8b58bf2c 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/jdbc/ClientJdbcConnectRequest.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/jdbc/ClientJdbcConnectRequest.java
@@ -32,14 +32,16 @@ public class ClientJdbcConnectRequest {
      *
      * @param in Client message unpacker.
      * @param handler Query event handler.
+     * @param username Current user name.
      * @return Operation future.
      */
     public static CompletableFuture<ResponseWriter> execute(
             ClientMessageUnpacker in,
-            JdbcQueryEventHandler handler
+            JdbcQueryEventHandler handler,
+            String username
     ) {
         String timeZoneIdString = in.unpackString();
 
-        return handler.connect(ZoneId.of(timeZoneIdString)).thenApply(res -> 
res::writeBinary);
+        return handler.connect(ZoneId.of(timeZoneIdString), 
username).thenApply(res -> res::writeBinary);
     }
 }
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteBatchRequest.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteBatchRequest.java
index 2b7d560f339..5d00b67ac9f 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteBatchRequest.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteBatchRequest.java
@@ -49,6 +49,7 @@ public class ClientSqlExecuteBatchRequest {
      * @param requestId Id of the request.
      * @param cancelHandleMap Registry of handlers. Request must register 
itself in this registry before switching to another
      *         thread.
+     * @param username Authenticated user name.
      * @return Future representing result of operation.
      */
     public static CompletableFuture<ResponseWriter> process(
@@ -58,7 +59,8 @@ public class ClientSqlExecuteBatchRequest {
             ClientResourceRegistry resources,
             long requestId,
             Map<Long, CancelHandle> cancelHandleMap,
-            HybridTimestampTracker tsTracker
+            HybridTimestampTracker tsTracker,
+            String username
     ) {
         CancelHandle cancelHandle = CancelHandle.create();
         cancelHandleMap.put(requestId, cancelHandle);
@@ -79,7 +81,7 @@ public class ClientSqlExecuteBatchRequest {
                             cancelHandle.token(),
                             statement,
                             arguments,
-                            props.toSqlProps(),
+                            props.toSqlProps().userName(username),
                             () -> true,
                             () -> {},
                             cursor -> 0,
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteRequest.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteRequest.java
index ccb45018979..6fe275e5e8c 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteRequest.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteRequest.java
@@ -80,6 +80,7 @@ public class ClientSqlExecuteRequest {
      *         transaction.
      * @param notificationSender Notification sender is required to send 
acknowledge for underlying write operation within a remote
      *         transaction.
+     * @param username Authenticated user name or {@code null} for unknown 
user.
      * @return Future representing result of operation.
      */
     public static CompletableFuture<ResponseWriter> process(
@@ -95,7 +96,8 @@ public class ClientSqlExecuteRequest {
             boolean sqlDirectTxMappingSupported,
             TxManager txManager,
             ClockService clockService,
-            NotificationSender notificationSender
+            NotificationSender notificationSender,
+            @Nullable String username
     ) {
         CancelHandle cancelHandle = CancelHandle.create();
         cancelHandles.put(requestId, cancelHandle);
@@ -122,7 +124,7 @@ public class ClientSqlExecuteRequest {
                 statement,
                 cancelHandle.token(),
                 props.pageSize(),
-                props.toSqlProps(),
+                props.toSqlProps().userName(username),
                 () -> cancelHandles.remove(requestId),
                 arguments
         ).thenCompose(asyncResultSet ->
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteScriptRequest.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteScriptRequest.java
index dbbaf634acc..23bfc2fdb1e 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteScriptRequest.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteScriptRequest.java
@@ -42,7 +42,9 @@ public class ClientSqlExecuteScriptRequest {
      * @param in Unpacker.
      * @param sql SQL API.
      * @param requestId Id of the request.
-     * @param cancelHandleMap Registry of handlers. Request must register 
itself in this registry before switching to another thread.
+     * @param cancelHandleMap Registry of handlers. Request must register 
itself in this registry before switching to another
+     *         thread.
+     * @param username Authenticated user name.
      * @return Future representing result of operation.
      */
     public static CompletableFuture<ResponseWriter> process(
@@ -51,7 +53,8 @@ public class ClientSqlExecuteScriptRequest {
             QueryProcessor sql,
             long requestId,
             Map<Long, CancelHandle> cancelHandleMap,
-            HybridTimestampTracker tsTracker
+            HybridTimestampTracker tsTracker,
+            String username
     ) {
         CancelHandle cancelHandle = CancelHandle.create();
         cancelHandleMap.put(requestId, cancelHandle);
@@ -72,7 +75,7 @@ public class ClientSqlExecuteScriptRequest {
                     script,
                     cancelHandle.token(),
                     arguments,
-                    props.toSqlProps(),
+                    props.toSqlProps().userName(username),
                     operationExecutor
             ).handle((none2, error) -> {
                 cancelHandleMap.remove(requestId);
diff --git 
a/modules/client-handler/src/test/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImplTest.java
 
b/modules/client-handler/src/test/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImplTest.java
index dfa45b2bc8a..69f4491ce23 100644
--- 
a/modules/client-handler/src/test/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImplTest.java
+++ 
b/modules/client-handler/src/test/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImplTest.java
@@ -126,7 +126,7 @@ class JdbcQueryEventHandlerImplTest extends 
BaseIgniteAbstractTest {
     void connectOnStoppingNode() {
         resourceRegistry.close();
 
-        JdbcConnectResult result = 
await(eventHandler.connect(ZoneId.systemDefault()));
+        JdbcConnectResult result = 
await(eventHandler.connect(ZoneId.systemDefault(), null));
 
         assertThat(result, notNullValue());
         assertThat(result.status(), is(STATUS_FAILED));
@@ -387,7 +387,7 @@ class JdbcQueryEventHandlerImplTest extends 
BaseIgniteAbstractTest {
     }
 
     private long acquireConnectionId() {
-        JdbcConnectResult result = 
await(eventHandler.connect(ZoneId.systemDefault()));
+        JdbcConnectResult result = 
await(eventHandler.connect(ZoneId.systemDefault(), null));
 
         assertThat(result, notNullValue());
         assertThat(result.status(), is(STATUS_SUCCESS));
diff --git 
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcAuthenticationTest.java
 
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcAuthenticationTest.java
index 1b4d0b1c8c6..6cba4917ef7 100644
--- 
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcAuthenticationTest.java
+++ 
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcAuthenticationTest.java
@@ -17,12 +17,21 @@
 
 package org.apache.ignite.jdbc;
 
+import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 import java.sql.Connection;
 import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 import org.apache.ignite.InitParametersBuilder;
 import org.apache.ignite.internal.ClusterPerClassIntegrationTest;
 import org.apache.ignite.jdbc.util.JdbcTestUtils;
+import org.apache.ignite.table.Tuple;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -69,6 +78,10 @@ class ItJdbcAuthenticationTest {
                             + "            {\n"
                             + "              \"username\": \"usr\",\n"
                             + "              \"password\": \"pwd\"\n"
+                            + "            },\n"
+                            + "            {\n"
+                            + "              \"username\": \"admin\",\n"
+                            + "              \"password\": \"adm\"\n"
                             + "            }\n"
                             + "          ]\n"
                             + "        }\n"
@@ -96,5 +109,50 @@ class ItJdbcAuthenticationTest {
                 // No-op.
             }
         }
+
+        /**
+         * Tests that the current user can be retrieved correctly for 
different authenticated users.
+         */
+        @Test
+        void jdbcCurrentUser() throws SQLException {
+            CLUSTER.aliveNode().sql().execute(null, "CREATE TABLE t1 (id INT 
PRIMARY KEY, val VARCHAR)").close();
+
+            // TODO https://issues.apache.org/jira/browse/IGNITE-25283 Remove 
next line.
+            CLUSTER.aliveNode().tables().table("t1").keyValueView().get(null, 
Tuple.create().set("id", 1));
+
+            String connString = 
"jdbc:ignite:thin://127.0.0.1:10800?username={}&password={}";
+            String user1 = "usr";
+            String user2 = "admin";
+
+            try (
+                    Connection conn1 = 
DriverManager.getConnection(format(connString, user1, "pwd"));
+                    Connection conn2 = 
DriverManager.getConnection(format(connString, user2, "adm"))
+            ) {
+                validateUsername(conn1, user1);
+                validateUsername(conn2, user2);
+            }
+        }
+    }
+
+    private static void validateUsername(Connection conn, String expectedUser) 
throws SQLException {
+        try (PreparedStatement stmt = conn.prepareStatement("SELECT 
CURRENT_USER")) {
+            try (ResultSet rs = stmt.executeQuery()) {
+                assertTrue(rs.next());
+                assertEquals(expectedUser, rs.getString(1));
+                assertFalse(rs.next());
+            }
+        }
+
+        try (Statement stmt = conn.createStatement()) {
+            stmt.execute(format("INSERT INTO t1 (id, val) VALUES ({}, 
CURRENT_USER)", expectedUser.hashCode()));
+        }
+
+        try (PreparedStatement stmt = conn.prepareStatement("SELECT val FROM 
t1 WHERE val = CURRENT_USER")) {
+            try (ResultSet rs = stmt.executeQuery()) {
+                assertTrue(rs.next());
+                assertEquals(expectedUser, rs.getString(1));
+                assertFalse(rs.next());
+            }
+        }
     }
 }
diff --git 
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
 
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
index 1ab19b760fa..f7e43563475 100644
--- 
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
+++ 
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
@@ -958,4 +958,18 @@ public class ItJdbcConnectionSelfTest extends 
AbstractJdbcSelfTest {
             checkConnectionClosed(() -> conn.setNetworkTimeout(executor, 
timeout));
         }
     }
+
+    @Test
+    public void testCurrentUser() throws Exception {
+        var url = "jdbc:ignite:thin://127.0.0.1:10800";
+
+        try (Connection conn = DriverManager.getConnection(url)) {
+            try (PreparedStatement stmt = conn.prepareStatement("SELECT 
CURRENT_USER")) {
+                try (ResultSet rs = stmt.executeQuery()) {
+                    assertTrue(rs.next());
+                    assertEquals("unknown", rs.getString(1));
+                }
+            }
+        }
+    }
 }
diff --git 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcClientQueryEventHandler.java
 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcClientQueryEventHandler.java
index bea33a48926..e5d07d9691c 100644
--- 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcClientQueryEventHandler.java
+++ 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcClientQueryEventHandler.java
@@ -38,6 +38,7 @@ import 
org.apache.ignite.internal.jdbc.proto.event.JdbcMetaTablesResult;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryCancelResult;
 import org.apache.ignite.internal.jdbc.proto.event.JdbcQueryExecuteRequest;
 import org.apache.ignite.internal.jdbc.proto.event.Response;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Jdbc query network event handler implementation.
@@ -57,7 +58,9 @@ public class JdbcClientQueryEventHandler implements 
JdbcQueryEventHandler {
 
     /** {@inheritDoc} */
     @Override
-    public CompletableFuture<JdbcConnectResult> connect(ZoneId timeZoneId) {
+    public CompletableFuture<JdbcConnectResult> connect(ZoneId timeZoneId, 
@Nullable String username) {
+        assert username == null : "Username should be passed via client 
handshake";
+
         return client.sendRequestAsync(ClientOp.JDBC_CONNECT, w -> {
             w.out().packString(timeZoneId.getId());
         }, r -> {
diff --git 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
index 0b196291955..c0b405275ed 100644
--- 
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
+++ 
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
@@ -150,7 +150,7 @@ public class JdbcConnection implements Connection {
         this.handler = new JdbcClientQueryEventHandler(client);
 
         try {
-            JdbcConnectResult result = 
handler.connect(connProps.getConnectionTimeZone()).get();
+            JdbcConnectResult result = 
handler.connect(connProps.getConnectionTimeZone(), null).get();
 
             if (!result.success()) {
                 throw 
IgniteQueryErrorCode.createJdbcSqlException(result.err(), result.status());
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientAuthenticationTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientAuthenticationTest.java
index e86e3b48b55..c1216f9448d 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientAuthenticationTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientAuthenticationTest.java
@@ -19,15 +19,20 @@ package org.apache.ignite.internal.runner.app.client;
 
 import static org.apache.ignite.internal.TestWrappers.unwrapIgniteImpl;
 import static 
org.apache.ignite.internal.configuration.hocon.HoconConverter.hoconSource;
+import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureExceptionMatcher.willThrowWithCauseOrSuppressed;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
 import static org.apache.ignite.internal.util.IgniteUtils.closeAll;
 import static org.awaitility.Awaitility.await;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import com.typesafe.config.ConfigFactory;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 import org.apache.ignite.client.BasicAuthenticator;
 import org.apache.ignite.client.IgniteClient;
 import org.apache.ignite.internal.app.IgniteImpl;
@@ -36,6 +41,11 @@ import 
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticat
 import org.apache.ignite.internal.security.configuration.SecurityConfiguration;
 import 
org.apache.ignite.internal.security.configuration.SecurityExtensionConfiguration;
 import org.apache.ignite.security.exception.InvalidCredentialsException;
+import org.apache.ignite.sql.ColumnType;
+import org.apache.ignite.sql.ResultSet;
+import org.apache.ignite.sql.SqlRow;
+import org.apache.ignite.sql.async.AsyncResultSet;
+import org.apache.ignite.table.Table;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -98,6 +108,15 @@ public class ItThinClientAuthenticationTest extends 
ItAbstractThinClientTest {
 
     @AfterEach
     void tearDown() throws Exception {
+        String dropTablesScript = server().tables().tables().stream()
+                .map(Table::name)
+                .map(name -> "DROP TABLE " + name)
+                .collect(Collectors.joining(";\n"));
+
+        if (!dropTablesScript.isEmpty()) {
+            server().sql().executeScript(dropTablesScript);
+        }
+
         closeAll(clientWithAuth);
     }
 
@@ -182,6 +201,46 @@ public class ItThinClientAuthenticationTest extends 
ItAbstractThinClientTest {
         }
     }
 
+    /**
+     * Tests that the current user can be retrieved correctly for different 
authenticated users.
+     */
+    @Test
+    public void testCurrentUser() {
+        server().sql().execute(null, "CREATE TABLE t1 (id INT PRIMARY KEY, val 
VARCHAR)").close();
+
+        IgniteClient client2WithAuth = IgniteClient.builder()
+                .authenticator(BasicAuthenticator.builder()
+                        .username(USERNAME_2)
+                        .password(PASSWORD_2)
+                        .build())
+                .addresses(getClientAddresses().toArray(new String[0]))
+                .build();
+
+        validateCurrentUser(clientWithAuth, USERNAME_1);
+        validateCurrentUser(client2WithAuth, USERNAME_2);
+    }
+
+    private static void validateCurrentUser(IgniteClient client, String 
expectedUsername) {
+        AsyncResultSet<SqlRow> resultSet = client.sql()
+                .executeAsync(null, "SELECT CURRENT_USER")
+                .join();
+
+        SqlRow row = resultSet.currentPage().iterator().next();
+
+        assertEquals(1, row.columnCount());
+        assertEquals(ColumnType.STRING, 
resultSet.metadata().columns().get(0).type());
+        assertEquals(expectedUsername, row.stringValue(0));
+
+        client.sql().execute(null, format("INSERT INTO t1 (id, val) VALUES 
({}, CURRENT_USER)", expectedUsername.hashCode())).close();
+
+        try (ResultSet<SqlRow> rs = client.sql().execute(null, "SELECT val 
FROM t1 WHERE val = CURRENT_USER")) {
+            assertTrue(rs.hasNext());
+            assertEquals(expectedUsername, rs.next().stringValue(0));
+            assertFalse(rs.hasNext());
+        }
+    }
+
+
     private static CompletableFuture<Void> checkConnection(IgniteClient 
client) {
         return client.sql().executeAsync(null, "select 1 as num, 'hello' as 
str")
                 .thenApply(ignored -> null);
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
index 03259db3100..a98589d7ec9 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
@@ -37,6 +37,7 @@ import java.util.concurrent.CompletionException;
 import java.util.stream.Collectors;
 import org.apache.ignite.client.IgniteClient;
 import org.apache.ignite.internal.catalog.commands.CatalogUtils;
+import org.apache.ignite.internal.security.authentication.UserDetails;
 import org.apache.ignite.lang.IgniteException;
 import org.apache.ignite.sql.ColumnMetadata;
 import org.apache.ignite.sql.ColumnType;
@@ -660,6 +661,28 @@ public class ItThinClientSqlTest extends 
ItAbstractThinClientTest {
         assertEquals("ClientSqlRow [NUM=1, STR=hello]", row.toString());
     }
 
+    @Test
+    public void testCurrentUser() {
+        String expectedUsername = UserDetails.UNKNOWN.username();
+        IgniteSql sql = client().sql();
+
+        try (ResultSet<SqlRow> rs = sql.execute(null, "SELECT CURRENT_USER")) {
+            assertEquals(ColumnType.STRING, 
rs.metadata().columns().get(0).type());
+            assertTrue(rs.hasNext());
+            assertEquals(expectedUsername, rs.next().stringValue(0));
+            assertFalse(rs.hasNext());
+        }
+
+        sql.execute(null, "CREATE TABLE t1 (id INT PRIMARY KEY, val 
VARCHAR)").close();
+        sql.execute(null, "INSERT INTO t1 (id, val) VALUES (1, 
CURRENT_USER)").close();
+
+        try (ResultSet<SqlRow> rs = sql.execute(null, "SELECT val FROM t1 
WHERE val = CURRENT_USER")) {
+            assertTrue(rs.hasNext());
+            assertEquals(expectedUsername, rs.next().stringValue(0));
+            assertFalse(rs.hasNext());
+        }
+    }
+
     private static class Pojo {
         public int num;
 
diff --git 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFunctionsTest.java
 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFunctionsTest.java
index 4bbd9263b6d..dcdec7f643d 100644
--- 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFunctionsTest.java
+++ 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItFunctionsTest.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.sql.engine;
 
 import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
+import static 
org.apache.ignite.internal.sql.engine.util.Commons.SYSTEM_USER_NAME;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.instanceOf;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -39,6 +40,7 @@ import org.apache.ignite.internal.sql.BaseSqlIntegrationTest;
 import org.apache.ignite.internal.sql.engine.util.MetadataMatcher;
 import org.apache.ignite.internal.util.ArrayUtils;
 import org.apache.ignite.sql.ColumnType;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -249,6 +251,14 @@ public class ItFunctionsTest extends 
BaseSqlIntegrationTest {
         );
     }
 
+    @Test
+    public void testCurrentUser() {
+        assertQuery("SELECT CURRENT_USER")
+                .returns(SYSTEM_USER_NAME)
+                .columnMetadata(new MetadataMatcher().type(ColumnType.STRING))
+                .check();
+    }
+
     /** Numeric type parser. */
     public static final class ParseNum {
 
diff --git 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java
 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java
index c598efe1ccd..21ed6e5afe1 100644
--- 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java
+++ 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java
@@ -19,6 +19,8 @@ package org.apache.ignite.internal.sql.engine;
 
 import static 
org.apache.ignite.internal.sql.engine.util.SqlTestUtils.assertThrowsSqlException;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import java.io.IOException;
@@ -33,9 +35,14 @@ import java.util.List;
 import java.util.Map;
 import org.apache.ignite.internal.sql.BaseSqlIntegrationTest;
 import org.apache.ignite.internal.sql.engine.sql.fun.IgniteSqlOperatorTable;
+import org.apache.ignite.internal.sql.engine.util.Commons;
 import org.apache.ignite.internal.sql.engine.util.QueryChecker;
 import org.apache.ignite.lang.ErrorGroups.Sql;
+import org.apache.ignite.sql.ColumnType;
+import org.apache.ignite.sql.IgniteSql;
+import org.apache.ignite.sql.ResultSet;
 import org.apache.ignite.sql.SqlException;
+import org.apache.ignite.sql.SqlRow;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -400,6 +407,28 @@ public class ItSqlOperatorsTest extends 
BaseSqlIntegrationTest {
         assertExpression("LOCALTIMESTAMP").check();
     }
 
+    @Test
+    public void testCurrentUser() {
+        IgniteSql sql = igniteSql();
+
+        try (ResultSet<SqlRow> rs = sql.execute(null, "SELECT CURRENT_USER")) {
+            assertEquals(ColumnType.STRING, 
rs.metadata().columns().get(0).type());
+            assertTrue(rs.hasNext());
+            assertEquals(Commons.SYSTEM_USER_NAME, rs.next().stringValue(0));
+            assertFalse(rs.hasNext());
+        }
+
+        sql("CREATE TABLE t1 (id INT PRIMARY KEY, val VARCHAR)");
+        sql("INSERT INTO t1 (id, val) VALUES (1, CURRENT_USER)");
+
+        try (ResultSet<SqlRow> rs = sql.execute(null, "SELECT val FROM t1 
WHERE val = CURRENT_USER")) {
+            assertEquals(ColumnType.STRING, 
rs.metadata().columns().get(0).type());
+            assertTrue(rs.hasNext());
+            assertEquals(Commons.SYSTEM_USER_NAME, rs.next().stringValue(0));
+            assertFalse(rs.hasNext());
+        }
+    }
+
     private QueryChecker assertExpression(String qry) {
         // Select expressions from table to test plan serialization containing 
these expressions.
         return assertQuery("SELECT " + qry + " FROM t");
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java
index ca7cbfea373..54986e9bb49 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java
@@ -55,6 +55,7 @@ import org.apache.ignite.internal.sql.engine.InternalSqlRow;
 import org.apache.ignite.internal.sql.engine.QueryProcessor;
 import org.apache.ignite.internal.sql.engine.SqlProperties;
 import org.apache.ignite.internal.sql.engine.SqlQueryType;
+import org.apache.ignite.internal.sql.engine.util.Commons;
 import org.apache.ignite.internal.tx.InternalTransaction;
 import org.apache.ignite.internal.util.ArrayUtils;
 import org.apache.ignite.internal.util.AsyncCursor;
@@ -589,7 +590,7 @@ public class IgniteSqlImpl implements IgniteSql, 
IgniteComponent {
                     query,
                     cancellationToken,
                     arguments,
-                    new SqlProperties(),
+                    new SqlProperties().userName(Commons.SYSTEM_USER_NAME),
                     commonExecutor
             );
         } finally {
@@ -660,7 +661,8 @@ public class IgniteSqlImpl implements IgniteSql, 
IgniteComponent {
         return new SqlProperties()
                 .timeZoneId(statement.timeZoneId())
                 
.defaultSchema(IgniteNameUtils.parseIdentifier(statement.defaultSchema()))
-                .queryTimeout(statement.queryTimeout(TimeUnit.MILLISECONDS));
+                .queryTimeout(statement.queryTimeout(TimeUnit.MILLISECONDS))
+                .userName(Commons.SYSTEM_USER_NAME);
     }
 
     private int registerCursor(AsyncSqlCursor<?> cursor) {
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlOperationContext.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlOperationContext.java
index 8710e7779b0..ac0e697ebbf 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlOperationContext.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlOperationContext.java
@@ -50,6 +50,7 @@ public final class SqlOperationContext {
     private final @Nullable String defaultSchemaName;
     private final @Nullable Consumer<QueryTransactionWrapper> txUsedListener;
     private final @Nullable Consumer<Throwable> errorListener;
+    private final @Nullable String userName;
 
     /**
      * Private constructor, used by a builder.
@@ -63,7 +64,8 @@ public final class SqlOperationContext {
             @Nullable QueryCancel cancel,
             @Nullable String defaultSchemaName,
             @Nullable Consumer<QueryTransactionWrapper> txUsedListener,
-            @Nullable Consumer<Throwable> errorListener
+            @Nullable Consumer<Throwable> errorListener,
+            @Nullable String userName
     ) {
         this.queryId = queryId;
         this.timeZoneId = timeZoneId;
@@ -74,6 +76,7 @@ public final class SqlOperationContext {
         this.defaultSchemaName = defaultSchemaName;
         this.txUsedListener = txUsedListener;
         this.errorListener = errorListener;
+        this.userName = userName;
     }
 
     public static Builder builder() {
@@ -104,6 +107,11 @@ public final class SqlOperationContext {
         return timeZoneId;
     }
 
+    /** Returns current user name or {@code null} if unknown. */
+    public @Nullable String userName() {
+        return userName;
+    }
+
     /**
      * Returns name of the schema to use to resolve schema objects, like 
tables or system views, for which name of the schema was omitted.
      *
@@ -177,6 +185,7 @@ public final class SqlOperationContext {
         private @Nullable Consumer<Throwable> errorListener;
         private @Nullable QueryCancel cancel;
         private @Nullable String defaultSchemaName;
+        private @Nullable String userName;
 
         public Builder cancel(@Nullable QueryCancel cancel) {
             this.cancel = requireNonNull(cancel);
@@ -223,6 +232,11 @@ public final class SqlOperationContext {
             return this;
         }
 
+        public Builder userName(@Nullable String userName) {
+            this.userName = userName;
+            return this;
+        }
+
         /** Creates new context. */
         public SqlOperationContext build() {
             return new SqlOperationContext(
@@ -234,7 +248,8 @@ public final class SqlOperationContext {
                     cancel,
                     defaultSchemaName,
                     txUsedListener,
-                    errorListener
+                    errorListener,
+                    userName
             );
         }
     }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlProperties.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlProperties.java
index 01022e91e99..8fec17248a9 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlProperties.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlProperties.java
@@ -21,6 +21,7 @@ import static 
org.apache.ignite.internal.sql.engine.SqlQueryProcessor.DEFAULT_TI
 
 import java.time.ZoneId;
 import java.util.Set;
+import javax.annotation.Nullable;
 import org.apache.ignite.internal.sql.SqlCommon;
 
 /**
@@ -31,6 +32,7 @@ public class SqlProperties {
     private Set<SqlQueryType> allowedQueryTypes = SqlQueryType.ALL;
     private String defaultSchema = SqlCommon.DEFAULT_SCHEMA_NAME;
     private ZoneId timeZoneId = DEFAULT_TIME_ZONE_ID;
+    private @Nullable String userName;
 
     public SqlProperties() {
     }
@@ -41,6 +43,7 @@ public class SqlProperties {
         allowedQueryTypes = other.allowedQueryTypes;
         defaultSchema = other.defaultSchema;
         timeZoneId = other.timeZoneId;
+        userName = other.userName;
     }
 
     public SqlProperties queryTimeout(long queryTimeout) {
@@ -78,4 +81,13 @@ public class SqlProperties {
     public ZoneId timeZoneId() {
         return timeZoneId;
     }
+
+    public SqlProperties userName(@Nullable String userName) {
+        this.userName = userName;
+        return this;
+    }
+
+    public @Nullable String userName() {
+        return userName;
+    }
 }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
index 76454b00d8d..080d2805f2e 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
@@ -106,6 +106,8 @@ public class ExecutionContext<RowT> implements DataContext {
 
     private final ZoneId timeZoneId;
 
+    private final String currentUser;
+
     private SharedState sharedState = new SharedState();
 
     /**
@@ -123,6 +125,7 @@ public class ExecutionContext<RowT> implements DataContext {
      * @param timeZoneId Session time-zone ID.
      * @param inBufSize Default execution nodes' internal buffer size. 
Negative value means default value.
      * @param clock The clock to use to get the system time.
+     * @param username Authenticated user name or {@code null} for unknown 
user.
      */
     @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
     public ExecutionContext(
@@ -138,7 +141,8 @@ public class ExecutionContext<RowT> implements DataContext {
             TxAttributes txAttributes,
             ZoneId timeZoneId,
             int inBufSize,
-            Clock clock
+            Clock clock,
+            @Nullable String username
     ) {
         this.expressionFactory = expressionFactory;
         this.executor = executor;
@@ -152,6 +156,7 @@ public class ExecutionContext<RowT> implements DataContext {
         this.txAttributes = txAttributes;
         this.timeZoneId = timeZoneId;
         this.inBufSize = inBufSize < 0 ? Commons.IN_BUFFER_SIZE : inBufSize;
+        this.currentUser = username;
 
         assert this.inBufSize > 0 : this.inBufSize;
 
@@ -298,6 +303,9 @@ public class ExecutionContext<RowT> implements DataContext {
         if (Variable.TIME_ZONE.camelName.equals(name)) {
             return TimeZone.getTimeZone(timeZoneId);
         }
+        if (Variable.USER.camelName.equals(name)) {
+            return currentUser;
+        }
 
         if (name.startsWith("?")) {
             return getParameter(name);
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImpl.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImpl.java
index 5d0078506e5..6ae10d7fec1 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImpl.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionServiceImpl.java
@@ -370,13 +370,14 @@ public class ExecutionServiceImpl<RowT> implements 
ExecutionService, TopologyEve
     }
 
     private static SqlOperationContext createOperationContext(
-            UUID queryId, ZoneId timeZoneId, Object[] params, HybridTimestamp 
operationTime
+            UUID queryId, ZoneId timeZoneId, Object[] params, HybridTimestamp 
operationTime, @Nullable String username
     ) {
         return SqlOperationContext.builder()
                 .queryId(queryId)
                 .parameters(params)
                 .timeZoneId(timeZoneId)
                 .operationTime(operationTime)
+                .userName(username)
                 .build();
     }
 
@@ -461,7 +462,8 @@ public class ExecutionServiceImpl<RowT> implements 
ExecutionService, TopologyEve
                 TxAttributes.dummy(),
                 operationContext.timeZoneId(),
                 -1,
-                Clock.systemUTC()
+                Clock.systemUTC(),
+                operationContext.userName()
         );
 
         QueryTransactionContext txContext = operationContext.txContext();
@@ -697,7 +699,7 @@ public class ExecutionServiceImpl<RowT> implements 
ExecutionService, TopologyEve
     private DistributedQueryManager getOrCreateQueryManager(String 
coordinatorNodeName, QueryStartRequest msg) {
         return queryManagerMap.computeIfAbsent(new ExecutionId(msg.queryId(), 
msg.executionToken()), key -> {
             SqlOperationContext operationContext = createOperationContext(
-                    key.queryId(), ZoneId.of(msg.timeZoneId()), 
msg.parameters(), msg.operationTime()
+                    key.queryId(), ZoneId.of(msg.timeZoneId()), 
msg.parameters(), msg.operationTime(), msg.username()
             );
 
             return new DistributedQueryManager(key, coordinatorNodeName, 
operationContext);
@@ -919,6 +921,7 @@ public class ExecutionServiceImpl<RowT> implements 
ExecutionService, TopologyEve
                     .timeZoneId(ctx.timeZoneId().getId())
                     .operationTime(ctx.operationTime())
                     .timestamp(clockService.now())
+                    .username(ctx.userName())
                     .build();
 
             return messageService.send(targetNodeName, request);
@@ -1010,7 +1013,8 @@ public class ExecutionServiceImpl<RowT> implements 
ExecutionService, TopologyEve
                     txAttributes,
                     ctx.timeZoneId(),
                     -1,
-                    Clock.systemUTC()
+                    Clock.systemUTC(),
+                    ctx.userName()
             );
         }
 
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/fsm/OptimizingPhaseHandler.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/fsm/OptimizingPhaseHandler.java
index e063f6a6496..fd57453007a 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/fsm/OptimizingPhaseHandler.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/fsm/OptimizingPhaseHandler.java
@@ -53,6 +53,7 @@ class OptimizingPhaseHandler implements ExecutionPhaseHandler 
{
 
         String schemaName = query.properties.defaultSchema();
         ZoneId timeZoneId = query.properties.timeZoneId();
+        String userName = query.properties.userName();
 
         SqlOperationContext operationContext = SqlOperationContext.builder()
                 .queryId(query.id)
@@ -64,6 +65,7 @@ class OptimizingPhaseHandler implements ExecutionPhaseHandler 
{
                 .txContext(query.txContext)
                 .txUsedListener(tx -> query.usedTransaction = tx)
                 .errorHandler(query::setError)
+                .userName(userName)
                 .build();
 
         query.operationContext = operationContext;
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/message/QueryStartRequest.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/message/QueryStartRequest.java
index 862885fe686..a4d058529f2 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/message/QueryStartRequest.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/message/QueryStartRequest.java
@@ -23,6 +23,7 @@ import 
org.apache.ignite.internal.network.annotations.Transferable;
 import org.apache.ignite.internal.replicator.message.TimestampAware;
 import org.apache.ignite.internal.sql.engine.exec.TxAttributes;
 import org.apache.ignite.internal.sql.engine.exec.mapping.FragmentDescription;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * QueryStartRequest interface.
@@ -65,4 +66,8 @@ public interface QueryStartRequest extends TimestampAware, 
ExecutionContextAware
 
     /** Time of the operation. */
     HybridTimestamp operationTime();
+
+    /** Name of user who starts the query. */
+    @Nullable
+    String username();
 }
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/fun/IgniteSqlOperatorTable.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/fun/IgniteSqlOperatorTable.java
index 20ab9013a17..f88f433de2a 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/fun/IgniteSqlOperatorTable.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/sql/fun/IgniteSqlOperatorTable.java
@@ -761,6 +761,9 @@ public class IgniteSqlOperatorTable extends 
ReflectiveSqlOperatorTable {
         definedOperatorsBuilder.add(SqlStdOperatorTable.LOCALTIME);
         definedOperatorsBuilder.add(SqlStdOperatorTable.LOCALTIMESTAMP);
 
+        // Context variable functions
+        definedOperatorsBuilder.add(SqlStdOperatorTable.CURRENT_USER);
+
         // Ignite specific operators
         definedOperatorsBuilder.add(LENGTH);
         definedOperatorsBuilder.add(SYSTEM_RANGE);
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
index b276a5432e5..a4da39d4c5f 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
@@ -123,6 +123,8 @@ public final class Commons {
     // Old name for partition column. Kept for backward compatibility.
     public static final String PART_COL_NAME_LEGACY = "__part";
 
+    public static final String SYSTEM_USER_NAME = "SYSTEM";
+
     public static final int IN_BUFFER_SIZE = 512;
 
     public static final int IO_BATCH_SIZE = 256;
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/docs/OperatorListTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/docs/OperatorListTest.java
index 75b7454082a..42e88c8e9bf 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/docs/OperatorListTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/docs/OperatorListTest.java
@@ -336,8 +336,10 @@ public class OperatorListTest extends 
BaseIgniteAbstractTest {
         ops.internal(SqlStdOperatorTable.NULLS_LAST);
         ops.internal(SqlStdOperatorTable.DESC);
 
-        // Ignite
+        // Context variable functions.
+        ops.add(SqlStdOperatorTable.CURRENT_USER);
 
+        // Ignite
         ops.add(IgniteSqlOperatorTable.TYPEOF);
         ops.add(IgniteSqlOperatorTable.RAND_UUID);
         ops.add(IgniteSqlOperatorTable.SYSTEM_RANGE);
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/RuntimeSortedIndexTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/RuntimeSortedIndexTest.java
index 1744e9eaac9..9e7512cfa23 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/RuntimeSortedIndexTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/RuntimeSortedIndexTest.java
@@ -130,7 +130,8 @@ public class RuntimeSortedIndexTest extends 
IgniteAbstractTest {
                         null,
                         SqlQueryProcessor.DEFAULT_TIME_ZONE_ID,
                         -1,
-                        Clock.systemUTC()
+                        Clock.systemUTC(),
+                        null
                 ),
                 RelCollations.of(ImmutableIntList.copyOf(idxCols)),
                 (o1, o2) -> {
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractExecutionTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractExecutionTest.java
index 2a3dd00ecfb..dc439faa727 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractExecutionTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/AbstractExecutionTest.java
@@ -157,7 +157,8 @@ public abstract class AbstractExecutionTest<T> extends 
IgniteAbstractTest {
                 TxAttributes.fromTx(new NoOpTransaction("fake-test-node", 
false)),
                 SqlQueryProcessor.DEFAULT_TIME_ZONE_ID,
                 bufferSize,
-                Clock.systemUTC()
+                Clock.systemUTC(),
+                null
         );
 
         contexts.add(executionContext);
diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
index a07353a4df0..4cd5a639e26 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
@@ -636,7 +636,8 @@ public class TestBuilders {
                     TxAttributes.fromTx(new NoOpTransaction(node.name(), 
false)),
                     zoneId,
                     -1,
-                    clock
+                    clock,
+                    null
             );
         }
     }
diff --git a/modules/sql-engine/src/test/resources/docs/operator_list.txt 
b/modules/sql-engine/src/test/resources/docs/operator_list.txt
index 51c3c417a58..51aac5d03d1 100644
--- a/modules/sql-engine/src/test/resources/docs/operator_list.txt
+++ b/modules/sql-engine/src/test/resources/docs/operator_list.txt
@@ -446,6 +446,9 @@ EXISTS <any>
 <any> DESC
 #cd2aa6863ed54bd03d844b08664115655e7ae2e4
 
+CURRENT_USER
+#3cfbde57fbba1435023dc75472eedaec6555d525
+
 TYPEOF(<any>)
 #85771dfef877b07351286b9f792f2ae2cd4a234d
 
diff --git 
a/modules/sql-engine/src/testFixtures/java/org/apache/ignite/internal/sql/engine/util/QueryCheckerImpl.java
 
b/modules/sql-engine/src/testFixtures/java/org/apache/ignite/internal/sql/engine/util/QueryCheckerImpl.java
index 6acb3fc0ee7..a37e2b88c99 100644
--- 
a/modules/sql-engine/src/testFixtures/java/org/apache/ignite/internal/sql/engine/util/QueryCheckerImpl.java
+++ 
b/modules/sql-engine/src/testFixtures/java/org/apache/ignite/internal/sql/engine/util/QueryCheckerImpl.java
@@ -330,7 +330,8 @@ abstract class QueryCheckerImpl implements QueryChecker {
         SqlProperties properties = new SqlProperties()
                 .allowedQueryTypes(SqlQueryType.SINGLE_STMT_TYPES)
                 .timeZoneId(timeZoneId)
-                .defaultSchema(defaultSchema);
+                .defaultSchema(defaultSchema)
+                .userName(Commons.SYSTEM_USER_NAME);
 
         String qry = queryTemplate.createQuery();
         boolean containExplain = "EXPLAIN ".equalsIgnoreCase(qry.substring(0, 
8));

Reply via email to