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));