Compare commits

..
458 changed files with 3087 additions and 7616 deletions
-5
View File
@@ -25,11 +25,6 @@ You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
## Changelog
### 25.2.4 2025-11-03
- A confirmation prompt has been added for potentially dangerous queries in the SQL Editor to help prevent unintended execution. You can customize this behavior in Preferences > SQL Editor;
- The value panel in the Data Editor has been redesigned to provide users with more space for viewing and editing data. Tab buttons for different data type visualizations are now aligned on a single level, creating a cleaner layout;
- Permission configuration for connections was simplified, providing a clearer appearance and improved performance.
### 25.2.3 2025-10-20
- Added visualization for column reordering in the Data Editor;
- Fixed the critical issue with duplicating the execution of queries executed by a shortcut in the SQL Editor;
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Model
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
Bundle-Version: 1.0.89.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 1.0.88.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.model</artifactId>
<version>1.0.89-SNAPSHOT</version>
<version>1.0.88-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -74,8 +74,4 @@ public interface SMAuthProviderExternal<AUTH_SESSION extends SMSession> extends
*/
default void postAuthentication() {}
@Override
default boolean supportsOpeningSessionAsChild() {
return true;
}
}
@@ -54,12 +54,12 @@ import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
import org.jkiss.utils.Pair;
import org.jkiss.utils.StandardConstants;
import java.io.IOException;
import java.nio.file.*;
import java.text.MessageFormat;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -1262,8 +1262,10 @@ public class LocalResourceController extends BaseLocalResourceController {
project.setProjectPermissions(allProjectPermissions);
if (Files.exists(path)) {
try {
project.setCreateTime(
OffsetDateTime.ofInstant(Files.getLastModifiedTime(path).toInstant(), ZoneId.of("UTC")).toInstant().toEpochMilli());
project.setCreateTime(OffsetDateTime.ofInstant(
Files.getLastModifiedTime(path).toInstant(),
StandardConstants.ZONE_ID_UTC
).toInstant().toEpochMilli());
} catch (IOException e) {
log.error(e);
}
@@ -66,7 +66,6 @@ import java.lang.reflect.InvocationTargetException;
import java.nio.file.Path;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -587,11 +586,8 @@ public class WebSession extends BaseWebSession
public WebAsyncTaskInfo createAndRunAsyncTask(@NotNull String taskName, @NotNull WebAsyncTaskProcessor<?> runnable) {
WebAsyncTaskInfo asyncTask = createAsyncTask(taskName);
return runAsyncTask(asyncTask, runnable);
}
public WebAsyncTaskInfo runAsyncTask(@NotNull WebAsyncTaskInfo asyncTask, @NotNull WebAsyncTaskProcessor<?> runnable) {
AbstractJob job = new AbstractJob(asyncTask.getName()) {
AbstractJob job = new AbstractJob(taskName) {
@Override
protected IStatus run(DBRProgressMonitor monitor) {
int curTaskCount = taskCount.incrementAndGet();
@@ -1023,25 +1019,6 @@ public class WebSession extends BaseWebSession
return globalProject;
}
public void handleTaskConfirmation(
@NotNull String taskId,
boolean confirmed,
boolean skipConfirmations
) {
String attributeName = WebSQLConstants.TASK_CONFIRMATION_ATTR_PREFIX + taskId;
if (confirmed && skipConfirmations) {
setAttribute(WebSQLConstants.SKIP_TASK_CONFIRMATIONS_ATTR, Boolean.TRUE);
}
CompletableFuture<Boolean> confirmationFuture = getAttribute(attributeName);
if (confirmationFuture != null) {
confirmationFuture.complete(confirmed);
removeAttribute(attributeName);
} else {
log.error("Received unexpected confirmation event for taskId: " + taskId);
}
}
private class SessionProgressMonitor extends BaseProgressMonitor {
@Override
public void beginTask(String name, int totalWork) {
@@ -20,7 +20,6 @@ import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.impl.preferences.AbstractUserPreferenceStore;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
import org.jkiss.utils.CommonUtils;
import java.io.IOException;
import java.util.Map;
@@ -86,13 +85,4 @@ public class WebSessionPreferenceStore extends AbstractUserPreferenceStore {
public void save() throws IOException {
throw new RuntimeException("Not implemented");
}
public boolean getUserPreferenceBoolean(@NotNull String name, boolean fallbackValue) {
String value = CommonUtils.toString(userPreferences.get(name));
if (value.isEmpty()) {
return fallbackValue;
} else {
return toBoolean(value);
}
}
}
@@ -17,18 +17,12 @@
package io.cloudbeaver.registry;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.DBPConditionalProperty;
import org.jkiss.dbeaver.model.impl.LocalizedPropertyDescriptor;
import org.jkiss.dbeaver.utils.RuntimeUtils;
import org.osgi.framework.Bundle;
public class WebAuthProviderProperty extends LocalizedPropertyDescriptor implements DBPConditionalProperty {
private static final String WEB_AUTH_PROPERTY_PREFIX = "prop.auth.model.";
private final String[] requiredFeatures;
@Nullable
private final String type;
@@ -36,7 +30,6 @@ public class WebAuthProviderProperty extends LocalizedPropertyDescriptor impleme
private final String authProviderId;
private final String hideExpr;
private final String readOnlyExpr;
private final Bundle bundle;
public WebAuthProviderProperty(String category, IConfigurationElement config, String authProviderId) {
super(category, config);
@@ -46,7 +39,6 @@ public class WebAuthProviderProperty extends LocalizedPropertyDescriptor impleme
this.type = config.getAttribute("type");
this.hideExpr = config.getAttribute("hideExpr");
this.readOnlyExpr = config.getAttribute("readOnlyExpr");
this.bundle = extractBundle(config);
}
@NotNull
@@ -71,34 +63,12 @@ public class WebAuthProviderProperty extends LocalizedPropertyDescriptor impleme
return readOnlyExpr;
}
@NotNull
@Override
public String getLocalizedName(@NotNull String locale) {
return RuntimeUtils.getBundleLocalization(bundle, locale).getString(getPropertyId());
}
@Nullable
@Override
public String getLocalizedDescription(@NotNull String locale) {
return RuntimeUtils.getBundleLocalization(bundle, locale).getString(getPropertyId() + "." + ATTR_DESCRIPTION);
}
private String getPropertyId() {
public String getPropertyId() {
if (authProviderId != null) {
return WEB_AUTH_PROPERTY_PREFIX + authProviderId + "." + this.getId();
return "prop.auth.model." + authProviderId + "." + this.getId();
} else {
return WEB_AUTH_PROPERTY_PREFIX + this.getId();
return "prop.auth.model." + this.getId();
}
}
@NotNull
private Bundle extractBundle(@NotNull IConfigurationElement config) {
final Bundle bundle;
String bundleName = config.getContributor().getName();
bundle = Platform.getBundle(bundleName);
if (bundle == null) {
throw new IllegalStateException("Bundle '" + bundleName + "' not found");
}
return bundle;
}
}
@@ -45,7 +45,4 @@ public class WebSQLConstants {
public static final String ATTR_SRID = "srid";
public static final String ATTR_PROPERTIES = "properties";
public static final String TASK_CONFIRMATION_ATTR_PREFIX = "session_task_confirmation_";
public static final String SKIP_TASK_CONFIRMATIONS_ATTR = "skip_session_task_confirmations";
public static final int TASK_CONFIRMATION_TIMEOUT_SECONDS = 60;
}
@@ -17,11 +17,11 @@
package io.cloudbeaver.utils;
import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.utils.StandardConstants;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class CBModelConstants {
public static final DateTimeFormatter ISO_DATE_FORMAT = DateTimeFormatter.ofPattern(DBConstants.DEFAULT_ISO_TIMESTAMP_FORMAT)
.withZone(ZoneId.of("UTC"));
.withZone(StandardConstants.ZONE_ID_UTC);
}
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Community Product
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
Bundle-Version: 25.2.5.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 25.2.4.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.product.ce</artifactId>
<version>25.2.5-SNAPSHOT</version>
<version>25.2.4-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Cloudbeaver Base JDBC drivers
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
Bundle-Version: 1.0.134.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 1.0.133.qualifier
Bundle-Release-Date: 20251103
Bundle-Vendor: DBeaver Corp
Bundle-ActivationPolicy: lazy
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
@@ -9,6 +9,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
<version>1.0.134-SNAPSHOT</version>
<version>1.0.133-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver CE Server
Bundle-SymbolicName: io.cloudbeaver.server.ce;singleton:=true
Bundle-Version: 25.2.5.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 25.2.4.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server.ce</artifactId>
<version>25.2.5-SNAPSHOT</version>
<version>25.2.4-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Server
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
Bundle-Version: 25.2.5.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 25.2.4.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server</artifactId>
<version>25.2.5-SNAPSHOT</version>
<version>25.2.4-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -69,10 +69,6 @@ enum CBServerEventId {
cb_workspace_config_changed @since(version: "25.1.1"),
"Server notification"
cb_server_notification@since(version: "25.2.1")
"Session task confirmations"
cb_session_task_confirmation_request@since(version: "25.2.2")
cb_session_task_query_confirmation_request@since(version: "25.2.2")
}
"Events sent by client"
@@ -80,8 +76,7 @@ enum CBClientEventId {
cb_client_topic_subscribe,
cb_client_topic_unsubscribe,
cb_client_projects_active,
cb_client_session_ping,
cb_client_session_task_confirmation@since(version: "25.2.2")
cb_client_session_ping
}
"Client subscribes on topic to receive only related events"
@@ -299,31 +294,6 @@ type WSServerNotificationEvent implements CBServerEvent @since(version: "25.2.1"
notificationType: WSServerNotificationEventType!
}
type WSSessionTaskConfirmationRequestEvent implements CBServerEvent @since(version: "25.2.2") {
id: CBServerEventId!
topicId: CBEventTopic!
taskId: ID!
title: String!
message: String!
}
type WSSessionTaskQueryConfirmationRequestEvent implements CBServerEvent @since(version: "25.2.2") {
id: CBServerEventId!
topicId: CBEventTopic!
taskId: ID!
title: String!
message: String!
query: String!
}
type WSSessionTaskConfirmationEvent implements CBClientEvent @since(version: "25.2.2") {
id: CBClientEventId!
topicId: CBEventTopic
taskId: ID!
confirmed: Boolean!
skipConfirmations: Boolean!
}
extend type Query {
emptyEvent: Boolean
}
@@ -362,7 +362,6 @@ extend type Mutation {
filter: SQLDataFilter,
dataFormat: ResultDataFormat, # requested data format. May be ignored by server
readLogs: Boolean # added 23.2.1
isInteractive: Boolean # Support WS events requested by server, e.g. confirmations. Added in 23.2.3
): AsyncTaskInfo!
"Creates async task for reading data from the container node"
@@ -1,31 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a field or method as sensitive, so its value won't be logged.
*/
@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface WebParameterSecure {
}
@@ -246,7 +246,14 @@ public class GraphQLEndpoint extends HttpServlet {
if (operationName != null) {
contextBuilder.operationName(operationName);
}
String sessionId = GraphQLLoggerUtil.getSmSessionId(request);
String userId = GraphQLLoggerUtil.getUserId(request);
String loggerMessage = GraphQLLoggerUtil.buildLoggerMessage(sessionId, userId, variables);
if (operationName != null) {
log.debug("API > " + operationName + loggerMessage);
} else if (DEBUG) {
log.debug("API > " + query + loggerMessage);
}
LocalDateTime startTime = LocalDateTime.now();
ExecutionInput executionInput = contextBuilder.build();
ExecutionResult executionResult = null;
@@ -16,27 +16,21 @@
*/
package io.cloudbeaver.server.graphql;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.cloudbeaver.WebParameterSecure;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.server.WebAppUtils;
import io.cloudbeaver.server.WebApplication;
import jakarta.servlet.http.HttpServletRequest;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.Log;
import org.jkiss.utils.CommonUtils;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.StringJoiner;
import java.util.Map;
import java.util.Set;
public class GraphQLLoggerUtil {
private static final Log log = Log.getLog(GraphQLLoggerUtil.class);
public static final String LOG_API_GRAPHQL_DEBUG_PARAMETER = "log.api.graphql.debug";
public static final Gson GSON = new GsonBuilder().create();
public static final String MASK_STRING = "****";
private static final Set<String> PROHIBITED_VARIABLES =
Set.of("password", "config", "parameters", "settings", "licenseText", "credentials", "username");
public static String getUserId(HttpServletRequest request) {
WebSession session = getWebSession(request);
@@ -69,67 +63,43 @@ public class GraphQLLoggerUtil {
.findWebSession(request);
}
public static String buildLoggerMessage(String sessionId, String userId, Method method, Object[] args) {
StringBuilder sb = new StringBuilder(64)
.append(" [user: ").append(userId)
.append(", sessionId: ").append(sessionId)
.append("]");
public static String buildLoggerMessage(String sessionId, String userId, Map<String, Object> variables) {
StringBuilder loggerMessage = new StringBuilder(" [user: ").append(userId)
.append(", sessionId: ").append(sessionId).append("]");
if (WebAppUtils.getWebPlatform().getPreferenceStore().getBoolean(LOG_API_GRAPHQL_DEBUG_PARAMETER)) {
sb.append('(');
String text = maskArgsToString(method, args);
if (CommonUtils.isNotEmpty(text)) {
sb.append(text);
if (WebAppUtils.getWebPlatform().getPreferenceStore().getBoolean(LOG_API_GRAPHQL_DEBUG_PARAMETER)
&& variables != null
) {
loggerMessage.append(" [variables] ");
String parsedVariables = parseVarialbes(variables);
if (CommonUtils.isNotEmpty(parsedVariables)) {
loggerMessage.append(parseVarialbes(variables));
}
sb.append(')');
}
return sb.toString();
return loggerMessage.toString();
}
public static String maskArgsToString(Method method, Object[] args) {
Parameter[] params = method.getParameters();
if (params.length == 0 || args == null || args.length == 0) {
return "";
}
private static String parseVarialbes(Map<String, Object> map) {
StringBuilder result = new StringBuilder();
int limit = Math.min(args.length, params.length);
StringJoiner joiner = new StringJoiner(", ");
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
for (int i = 0; i < limit; i++) {
Object value = args[i];
if (value instanceof WebSession) {
//we already log sessionId
continue;
}
if (params[i].isAnnotationPresent(WebParameterSecure.class)) {
joiner.add(MASK_STRING);
continue;
}
if (value instanceof String sv && CommonUtils.isEmpty(sv)) {
boolean isProhibited = PROHIBITED_VARIABLES.stream()
.anyMatch(prohibitedKey -> key.toLowerCase().contains(prohibitedKey.toLowerCase()));
if (isProhibited) {
result.append(key).append(": ").append("******** ");
continue;
}
if (value != null && !isSimple(value.getClass())) {
String stringValue;
try {
stringValue = GSON.toJson(value);
} catch (Exception e) {
stringValue = value.toString();
}
joiner.add(stringValue);
if (value instanceof Map) {
result.append(parseVarialbes((Map<String, Object>) value));
} else {
joiner.add(String.valueOf(value));
result.append(key).append(": ").append(value).append(" ");
}
}
return joiner.toString();
return result.toString().trim();
}
private static boolean isSimple(Class<?> cls) {
return cls.isPrimitive()
|| Number.class.isAssignableFrom(cls)
|| CharSequence.class.isAssignableFrom(cls)
|| Boolean.class.equals(cls)
|| Enum.class.isAssignableFrom(cls);
}
}
@@ -25,7 +25,10 @@ import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
import org.jkiss.dbeaver.model.websocket.event.client.*;
import org.jkiss.dbeaver.model.websocket.event.client.WSSessionPingClientEvent;
import org.jkiss.dbeaver.model.websocket.event.client.WSSubscribeOnTopicClientEvent;
import org.jkiss.dbeaver.model.websocket.event.client.WSUnsubscribeFromTopicClientEvent;
import org.jkiss.dbeaver.model.websocket.event.client.WSUpdateActiveProjectsClientEvent;
import org.jkiss.dbeaver.model.websocket.event.session.WSAccessTokenExpiredEvent;
import org.jkiss.dbeaver.model.websocket.event.session.WSSocketConnectedEvent;
import org.jkiss.utils.CommonUtils;
@@ -107,17 +110,6 @@ public class CBEventsWebSocket extends CBAbstractWebSocket implements CBWebSessi
}
break;
}
case WSSessionTaskConfirmationEvent.ID: {
if (webSession instanceof WebSession session) {
var taskConfirmationEvent = (WSSessionTaskConfirmationEvent) clientEvent;
session.handleTaskConfirmation(
taskConfirmationEvent.getTaskId(),
taskConfirmationEvent.isConfirmed(),
taskConfirmationEvent.isSkipConfirmations()
);
}
break;
}
default:
var e = new DBWebException("Unknown websocket client event: " + clientEvent.getId());
log.error(e.getMessage(), e);
@@ -16,7 +16,6 @@
*/
package io.cloudbeaver.service;
import graphql.GraphQLContext;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
@@ -28,11 +27,9 @@ import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.session.WebSessionProvider;
import io.cloudbeaver.server.WebAppUtils;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.server.graphql.GraphQLLoggerUtil;
import io.cloudbeaver.service.security.SMUtils;
import io.cloudbeaver.utils.ServletAppUtils;
import io.cloudbeaver.utils.WebDataSourceUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
@@ -164,7 +161,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
}
@NotNull
public static WebConnectionInfo getWebConnection(@NotNull WebSession session, @Nullable String projectId, @NotNull String connectionId) throws DBWebException {
public static WebConnectionInfo getWebConnection(@NotNull WebSession session, @NotNull String projectId, @NotNull String connectionId) throws DBWebException {
return WebDataSourceUtils.getWebConnectionInfo(session, projectId, connectionId);
}
@@ -343,27 +340,16 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
}
}
}
// Perform any checks before action call
protected void beforeWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
GraphQLContext graphQlContext = this.env.getGraphQlContext();
HttpServletRequest request = graphQlContext.get("request");
if (request == null) {
return;
}
String sessionId = GraphQLLoggerUtil.getSmSessionId(request);
String userId = GraphQLLoggerUtil.getUserId(request);
String loggerMessage = GraphQLLoggerUtil.buildLoggerMessage(sessionId, userId, method, args);
}
log.debug("API > " + method.getName() + loggerMessage);
setLogContext(method, args);
}
protected void afterWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
Log.setContext(null);
}
// Perform any checks before action call
protected void beforeWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
setLogContext(method, args);
}
protected void afterWebActionCall(WebAction webAction, Method method, Object[] args) throws DBException {
Log.setContext(null);
}
protected void setLogContext(Method method, @Nullable Object[] args) {
@@ -16,7 +16,10 @@
*/
package io.cloudbeaver.service.core;
import io.cloudbeaver.*;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebAction;
import io.cloudbeaver.WebObjectId;
import io.cloudbeaver.WebProjectAction;
import io.cloudbeaver.model.*;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.DBWService;
@@ -116,7 +119,7 @@ public interface DBWServiceCore extends DBWService {
@NotNull WebSession webSession,
@Nullable String projectId,
@NotNull String connectionId,
@WebParameterSecure @Nullable Map<String, Object> authProperties,
@Nullable Map<String, Object> authProperties,
@Nullable List<WebNetworkHandlerConfigInput> networkCredentials,
boolean saveCredentials,
boolean sharedCredentials,
@@ -127,7 +130,7 @@ public interface DBWServiceCore extends DBWService {
WebConnectionInfo createConnection(
@NotNull WebSession webSession,
@Nullable @WebObjectId String projectId,
@WebParameterSecure @NotNull Map<String, Object> connectionConfig
@NotNull Map<String, Object> connectionConfig
) throws DBWebException;
@WebProjectAction(requireProjectPermissions = {RMConstants.PERMISSION_PROJECT_DATASOURCES_EDIT})
@@ -339,7 +339,6 @@ public class WebServiceCore implements DBWServiceCore {
if (e instanceof DBCConnectException) {
Throwable rootCause = CommonUtils.getRootCause(e);
if (rootCause instanceof ClassNotFoundException) {
log.error(e);
throwDriverNotFoundException(dataSourceContainer);
}
}
@@ -555,7 +554,6 @@ public class WebServiceCore implements DBWServiceCore {
if (ct.getConnectError() instanceof DBCConnectException error) {
Throwable rootCause = CommonUtils.getRootCause(error);
if (rootCause instanceof ClassNotFoundException) {
log.error(error);
throwDriverNotFoundException(testDataSource);
}
}
@@ -636,7 +634,7 @@ public class WebServiceCore implements DBWServiceCore {
WebConnectionInfo connectionInfo = project.getWebConnectionInfo(connectionId);
DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer();
WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer);
boolean disconnected = WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer);
return connectionInfo;
}
@@ -756,7 +754,8 @@ public class WebServiceCore implements DBWServiceCore {
}
}
private static void throwDriverNotFoundException(@NotNull DBPDataSourceContainer container) throws DBWebException {
@NotNull
private static String throwDriverNotFoundException(@NotNull DBPDataSourceContainer container) throws DBWebException {
throw new DBWebException("Driver files for %s are not found. Please ask the administrator to download it."
.formatted(container.getDriver().getName()));
}
@@ -101,8 +101,7 @@ public interface DBWServiceSQL extends DBWService {
@Nullable String resultId,
@Nullable WebSQLDataFilter filter,
@Nullable WebDataFormat dataFormat,
boolean readLogs,
boolean useEvents
boolean readLogs
) throws DBException;
@WebAction
@@ -48,10 +48,10 @@ import org.jkiss.dbeaver.model.struct.rdb.DBSSchema;
import org.jkiss.dbeaver.model.websocket.event.WSTransactionalCountEvent;
import org.jkiss.dbeaver.utils.RuntimeUtils;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.StandardConstants;
import java.lang.reflect.InvocationTargetException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
@@ -74,7 +74,7 @@ public class WebSQLContextInfo implements WebSessionProvider {
private final AtomicInteger resultId = new AtomicInteger();
public static final DateTimeFormatter ISO_DATE_FORMAT = DateTimeFormatter.ofPattern(DBConstants.DEFAULT_ISO_TIMESTAMP_FORMAT)
.withZone(ZoneId.of("UTC"));
.withZone(StandardConstants.ZONE_ID_UTC);
public WebSQLContextInfo(
WebSQLProcessor processor, String id, String catalogName, String schemaName, String projectId
@@ -17,14 +17,11 @@
package io.cloudbeaver.service.sql;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.WebAsyncTaskInfo;
import io.cloudbeaver.model.WebConnectionInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.session.WebSessionPreferenceStore;
import io.cloudbeaver.model.session.WebSessionProvider;
import io.cloudbeaver.server.WebAppUtils;
import io.cloudbeaver.server.jobs.SqlOutputLogReaderJob;
import io.cloudbeaver.service.sql.messages.WebSQLMessages;
import org.eclipse.jface.text.Document;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
@@ -51,26 +48,17 @@ import org.jkiss.dbeaver.model.sql.parser.SQLParserContext;
import org.jkiss.dbeaver.model.sql.parser.SQLRuleManager;
import org.jkiss.dbeaver.model.sql.parser.SQLScriptParser;
import org.jkiss.dbeaver.model.struct.*;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
import org.jkiss.dbeaver.model.websocket.event.WSTransactionalCountEvent;
import org.jkiss.dbeaver.model.websocket.event.session.WSSessionTaskConfirmationRequestEvent;
import org.jkiss.dbeaver.model.websocket.event.session.WSSessionTaskQueryConfirmationRequestEvent;
import org.jkiss.dbeaver.registry.confirmation.ConfirmationConstants;
import org.jkiss.dbeaver.registry.confirmation.ConfirmationDescriptor;
import org.jkiss.dbeaver.registry.confirmation.ConfirmationRegistry;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@@ -178,9 +166,7 @@ public class WebSQLProcessor implements WebSessionProvider {
@Nullable WebSQLDataFilter filter,
@Nullable WebDataFormat dataFormat,
@NotNull WebSession webSession,
@NotNull WebAsyncTaskInfo asyncTask,
boolean readLogs,
boolean useEvents
boolean readLogs
) throws DBWebException, DBCException {
if (filter == null) {
// Use default filter
@@ -216,11 +202,9 @@ public class WebSQLProcessor implements WebSessionProvider {
SQLScriptElement element = SQLScriptParser.extractActiveQuery(parserContext, 0, sql.length());
boolean isGenerated = false;
if (element instanceof SQLControlCommand command) {
SQLControlResult controlResult = dataContainer.getScriptContext().executeControlCommand(monitor, command);
if (controlResult.getTransformed() != null) {
isGenerated = true;
element = controlResult.getTransformed();
} else {
WebSQLQueryResults stats = new WebSQLQueryResults(webSession, dataFormat);
@@ -228,13 +212,6 @@ public class WebSQLProcessor implements WebSessionProvider {
}
}
if (element instanceof SQLQuery mainQuery) {
if (useEvents) {
boolean isConfirmed = confirmQueryIfNeeded(mainQuery.getScriptElements(), asyncTask, isGenerated);
if (!isConfirmed) {
throw new DBWebException("Query execution was cancelled by user");
}
}
DBExecUtils.tryExecuteRecover(monitor, connection.getDataSource(), param -> {
try (DBCSession session = context.openSession(monitor, resolveQueryPurpose(dataFilter), "Execute SQL")) {
List<SQLScriptElement> sqlQueries = mainQuery.getScriptElements();
@@ -292,6 +269,8 @@ public class WebSQLProcessor implements WebSessionProvider {
sqlOutputLogReaderJob.join();
}
fillQueryResults(contextInfo, dataContainer, dbStat, hasResultSet, executeInfo, webDataFilter, dataFilter, dataFormat);
} catch (DBException e) {
throw new InvocationTargetException(e);
}
}
}
@@ -350,12 +329,14 @@ public class WebSQLProcessor implements WebSessionProvider {
results.setResultSet(resultSet);
executeInfo.setResults(new WebSQLQueryResults[]{results});
setResultFilterText(session.getDataSource(), executeInfo, dataFilter);
setResultFilterText(dataContainer, session.getDataSource(), executeInfo, dataFilter);
executeInfo.setFullQuery(statistics.getQueryText());
if (resultSet != null && resultSet.getRowsWithMetaData() != null && resultSet.getResultsInfo() != null) {
resultSet.getResultsInfo().setQueryText(statistics.getQueryText());
executeInfo.setStatusMessage(resultSet.getRowsWithMetaData().size() + " row(s) fetched");
}
} catch (DBException e) {
throw new InvocationTargetException(e);
}
}
});
@@ -395,7 +376,7 @@ public class WebSQLProcessor implements WebSessionProvider {
for (var rowIdentifier : rowIdentifierList) {
Map<DBSDataManipulator.ExecuteBatch, Object[]> resultBatches = new LinkedHashMap<>();
DBSDataManipulator dataManipulator = generateUpdateResultsDataBatch(
monitor, resultsInfo, rowIdentifier, updatedRows, deletedRows, addedRows, resultBatches, keyReceiver);
monitor, resultsInfo, rowIdentifier, updatedRows, deletedRows, addedRows, dataFormat, resultBatches, keyReceiver);
DBCExecutionContext executionContext = getExecutionContext(dataManipulator);
try (DBCSession session = executionContext.openSession(monitor, DBCExecutionPurpose.USER, "Update data in container")) {
@@ -496,7 +477,7 @@ public class WebSQLProcessor implements WebSessionProvider {
@NotNull Set<WebSQLQueryResultSetRow> newResultSetRows,
@Nullable WebDataFormat dataFormat,
@NotNull DBRProgressMonitor monitor)
throws DBException {
throws DBCException {
try (DBCSession session = getExecutionContext().openSession(
monitor,
DBCExecutionPurpose.UTIL,
@@ -572,8 +553,9 @@ public class WebSQLProcessor implements WebSessionProvider {
@NotNull String resultsId,
@Nullable List<WebSQLResultsRow> updatedRows,
@Nullable List<WebSQLResultsRow> deletedRows,
@Nullable List<WebSQLResultsRow> addedRows
) throws DBException {
@Nullable List<WebSQLResultsRow> addedRows,
@Nullable WebDataFormat dataFormat) throws DBException
{
Map<DBSDataManipulator.ExecuteBatch, Object[]> resultBatches = new LinkedHashMap<>();
@@ -589,7 +571,7 @@ public class WebSQLProcessor implements WebSessionProvider {
StringBuilder sqlBuilder = new StringBuilder();
for (var rowIdentifier : rowIdentifierList) {
DBSDataManipulator dataManipulator = generateUpdateResultsDataBatch(
monitor, resultsInfo, rowIdentifier, updatedRows, deletedRows, addedRows, resultBatches, null);
monitor, resultsInfo, rowIdentifier, updatedRows, deletedRows, addedRows, dataFormat, resultBatches, null);
List<DBEPersistAction> actions = new ArrayList<>();
@@ -615,6 +597,7 @@ public class WebSQLProcessor implements WebSessionProvider {
@Nullable List<WebSQLResultsRow> updatedRows,
@Nullable List<WebSQLResultsRow> deletedRows,
@Nullable List<WebSQLResultsRow> addedRows,
@Nullable WebDataFormat dataFormat,
@NotNull Map<DBSDataManipulator.ExecuteBatch, Object[]> resultBatches,
@Nullable DBDDataReceiver keyReceiver)
throws DBException
@@ -874,6 +857,7 @@ public class WebSQLProcessor implements WebSessionProvider {
@NotNull
public WebSQLExecutionPlan explainExecutionPlan(
@NotNull DBRProgressMonitor monitor,
@NotNull WebSQLContextInfo contextInfo,
@NotNull String sql,
@NotNull Map<String, Object> configuration) throws DBWebException {
@@ -898,6 +882,8 @@ public class WebSQLProcessor implements WebSessionProvider {
DBCQueryPlannerConfiguration planConfig = new DBCQueryPlannerConfiguration();
planConfig.getParameters().putAll(configuration);
dbcPlan[0] = planner.planQueryExecution(session, sql, planConfig);
} catch (DBException e) {
throw new InvocationTargetException(e);
}
});
} catch (DBException e) {
@@ -937,14 +923,16 @@ public class WebSQLProcessor implements WebSessionProvider {
private void readCellDataValue(
@NotNull DBRProgressMonitor monitor,
@NotNull WebSQLResultsInfo resultsInfo,
@NotNull WebSQLResultsRow row,
@Nullable WebSQLResultsRow row,
@NotNull WebSQLCellValueReceiver dataReceiver
) throws DBException {
DBSDataContainer dataContainer = resultsInfo.getDataContainer();
DBCExecutionContext executionContext = getExecutionContext(dataContainer);
try (DBCSession session = executionContext.openSession(monitor, DBCExecutionPurpose.USER, "Generate data update batches")) {
DBDDataFilter dataFilter = new DBDDataFilter();
addKeyAttributes(resultsInfo, row, dataContainer, session, dataFilter);
if (!resultsInfo.isSingleRow()) {
addKeyAttributes(resultsInfo, row, dataContainer, session, dataFilter);
}
WebExecutionSource executionSource = new WebExecutionSource(dataContainer, executionContext, this);
dataContainer.readData(
executionSource, session, dataReceiver, dataFilter,
@@ -954,16 +942,12 @@ public class WebSQLProcessor implements WebSessionProvider {
private void addKeyAttributes(
@NotNull WebSQLResultsInfo resultsInfo,
@NotNull WebSQLResultsRow row,
@Nullable WebSQLResultsRow row,
@NotNull DBSDataContainer dataContainer,
@NotNull DBCSession session,
@NotNull DBDDataFilter dataFilter
) throws DBException {
DBDRowIdentifier rowIdentifier = resultsInfo.getDefaultRowIdentifier();
if (rowIdentifier == null || rowIdentifier.isIncomplete()) {
return;
}
DBDAttributeBinding[] keyAttributes = rowIdentifier.getAttributes().toArray(new DBDAttributeBinding[0]);
DBDAttributeBinding[] keyAttributes = resultsInfo.getDefaultRowIdentifier().getAttributes().toArray(new DBDAttributeBinding[0]);
Object[] rowValues = new Object[keyAttributes.length];
List<DBDAttributeConstraint> constraints = new ArrayList<>();
for (int i = 0; i < keyAttributes.length; i++) {
@@ -1070,6 +1054,7 @@ public class WebSQLProcessor implements WebSessionProvider {
try (WebSQLQueryDataReceiver dataReceiver = new WebSQLQueryDataReceiver(contextInfo, dataContainer, dataFormat)) {
readResultSet(dbStat.getSession(), resultSet, webDataFilter, dataReceiver);
results.setResultSet(dataReceiver.getResultSet());
dataReceiver.getResultSet().getResultsInfo().setQueryText(resultSet.getSourceStatement().getQueryString());
}
}
resultList.add(results);
@@ -1089,15 +1074,11 @@ public class WebSQLProcessor implements WebSessionProvider {
}
executeInfo.setResults(resultList.toArray(new WebSQLQueryResults[0]));
setResultFilterText(dbStat.getSession().getDataSource(), executeInfo, dataFilter);
setResultFilterText(dataContainer, dbStat.getSession().getDataSource(), executeInfo, dataFilter);
executeInfo.setFullQuery(dbStat.getQueryString());
}
private void setResultFilterText(
@NotNull DBPDataSource dataSource,
@NotNull WebSQLExecuteInfo executeInfo,
@NotNull DBDDataFilter filter
) throws DBException {
private void setResultFilterText(@NotNull DBSDataContainer dataContainer, @NotNull DBPDataSource dataSource, @NotNull WebSQLExecuteInfo executeInfo, @NotNull DBDDataFilter filter) throws DBException {
if (!filter.getConstraints().isEmpty() || !CommonUtils.isEmpty(filter.getWhere())) {
StringBuilder where = new StringBuilder();
SQLUtils.appendConditionString(
@@ -1110,13 +1091,8 @@ public class WebSQLProcessor implements WebSessionProvider {
}
}
private void readResultSet(
@NotNull DBCSession session,
@NotNull DBCResultSet dbResult,
@NotNull WebSQLDataFilter filter,
@NotNull WebSQLQueryDataReceiver dataReceiver
) throws DBException {
DBDDataReceiver.startFetchWorkflow(dataReceiver, session, dbResult, filter.getOffset(), filter.getLimit());
private void readResultSet(@NotNull DBCSession session, @NotNull DBCResultSet dbResult, @NotNull WebSQLDataFilter filter, @NotNull WebSQLQueryDataReceiver dataReceiver) throws DBCException {
dataReceiver.fetchStart(session, dbResult, filter.getOffset(), filter.getLimit());
int rowCount = 0;
while (dbResult.nextRow()) {
if (rowCount > filter.getLimit()) {
@@ -1126,6 +1102,7 @@ public class WebSQLProcessor implements WebSessionProvider {
dataReceiver.fetchRow(session, dbResult);
rowCount++;
}
dataReceiver.fetchEnd(session, dbResult);
}
/**
@@ -1163,6 +1140,7 @@ public class WebSQLProcessor implements WebSessionProvider {
if (keyValue == null) {
continue;
}
boolean updated = false;
if (!CommonUtils.isEmpty(keyAttribute.getName())) {
DBDAttributeBinding binding = DBUtils.findObject(resultsAttributes, keyAttribute.getName());
if (binding != null) {
@@ -1233,7 +1211,8 @@ public class WebSQLProcessor implements WebSessionProvider {
private Object setCellRowValue(Object cellRow, WebSession webSession, DBCSession dbcSession, DBDAttributeBinding allAttributes, boolean withoutExecution)
throws DBException {
if (cellRow instanceof Map<?, ?> variables) {
if (cellRow instanceof Map<?, ?>) {
Map<String, Object> variables = (Map<String, Object>) cellRow;
if (variables.get(FILE_ID) != null) {
Path path = WebAppUtils.getWebPlatform()
.getTempFolder(webSession.getProgressMonitor(), TEMP_FILE_FOLDER)
@@ -1250,112 +1229,4 @@ public class WebSQLProcessor implements WebSessionProvider {
}
return convertInputCellValue(dbcSession, allAttributes, cellRow, withoutExecution);
}
private boolean confirmQueryIfNeeded(
@NotNull List<SQLScriptElement> scriptElements,
@NotNull WebAsyncTaskInfo asyncTask,
boolean isGenerated
) throws DBWebException {
Boolean skipConfirmations = webSession.getAttribute(WebSQLConstants.SKIP_TASK_CONFIRMATIONS_ATTR);
if (skipConfirmations != null && skipConfirmations) {
return true;
}
boolean hasGeneratedUpdates = false;
boolean hasDangerousUpdates = false;
boolean hasDropStatement = false;
String title = null;
String message = null;
String queryPreview = null;
if (isGenerated) {
Set<SQLQueryCategory> categories = SQLQueryCategory.categorizeScript(scriptElements);
hasGeneratedUpdates = categories.contains(SQLQueryCategory.DDL) ||
categories.contains(SQLQueryCategory.DML) ||
categories.contains(SQLQueryCategory.UNKNOWN);
title = WebSQLMessages.model_web_ai_query_confirmation_title;
message = WebSQLMessages.model_web_ai_query_confirmation_message;
queryPreview = scriptElements.stream()
.map(SQLScriptElement::getText)
.collect(Collectors.joining("\n\n"));
} else {
WebSessionPreferenceStore store = webSession.getUserPreferenceStore();
boolean confirmDangerousQueries = store.getUserPreferenceBoolean(ConfirmationConstants.CONFIRM_DANGER_SQL_KEY, true);
boolean confirmDropQueries = store.getUserPreferenceBoolean(ConfirmationConstants.CONFIRM_DROP_SQL_KEY, true);
for (SQLScriptElement scriptElement : scriptElements) {
if (scriptElement instanceof SQLQuery sqlQuery) {
if (confirmDangerousQueries && sqlQuery.isDeleteUpdateDangerous()) {
hasDangerousUpdates = true;
ConfirmationDescriptor descriptor = ConfirmationRegistry.getInstance()
.getConfirmation(ConfirmationConstants.CONFIRM_DANGER_SQL_ID);
title = descriptor.getLocalizedTitle(webSession.getLocale());
var entityMetadata = sqlQuery.getEntityMetadata(false);
message = MessageFormat.format(
descriptor.getLocalizedMessage(webSession.getLocale()),
sqlQuery.getType().name(),
entityMetadata != null ? entityMetadata.getEntityName() : "multiple tables"
);
break;
}
if (confirmDropQueries && sqlQuery.isDropTableDangerous()) {
hasDropStatement = true;
ConfirmationDescriptor descriptor = ConfirmationRegistry.getInstance()
.getConfirmation(ConfirmationConstants.CONFIRM_DROP_SQL_ID);
title = descriptor.getLocalizedTitle(webSession.getLocale());
message = descriptor.getLocalizedMessage(webSession.getLocale());
break;
}
}
}
}
if (!hasGeneratedUpdates && !hasDangerousUpdates && !hasDropStatement) {
return true;
} else {
return requestConfirmation(asyncTask, queryPreview, title, message);
}
}
private boolean requestConfirmation(
@NotNull WebAsyncTaskInfo asyncTask,
@Nullable String query,
@NotNull String title,
@NotNull String message
) throws DBWebException {
String attributeName = WebSQLConstants.TASK_CONFIRMATION_ATTR_PREFIX + asyncTask.getId();
CompletableFuture<Boolean> confirmationFuture = new CompletableFuture<>();
webSession.setAttribute(attributeName, confirmationFuture);
webSession.addSessionEvent(createConfirmationEvent(asyncTask, query, title, message));
try {
Boolean isConfirmed = confirmationFuture.get(WebSQLConstants.TASK_CONFIRMATION_TIMEOUT_SECONDS, TimeUnit.SECONDS);
return isConfirmed != null && isConfirmed;
} catch (TimeoutException e) {
throw new DBWebException("Query confirmation timeout");
} catch (Exception e) {
throw new DBWebException("Error when processing confirmation response", e);
} finally {
webSession.removeAttribute(attributeName);
}
}
@NotNull
private WSEvent createConfirmationEvent(
@NotNull WebAsyncTaskInfo asyncTask,
@Nullable String query,
@NotNull String title,
@NotNull String message
) {
WSEvent confirmationEvent;
if (query != null) {
confirmationEvent = new WSSessionTaskQueryConfirmationRequestEvent(
asyncTask.getId(), title, message, query
);
} else {
confirmationEvent = new WSSessionTaskConfirmationRequestEvent(
asyncTask.getId(), title, message
);
}
return confirmationEvent;
}
}
@@ -104,7 +104,7 @@ public class WebSQLQueryDataContainer implements DBSDataContainer, DBPContextPro
long maxRows,
long flags,
int fetchSize
) throws DBException {
) throws DBCException {
return queryDataContainer.readData(source, session, dataReceiver, dataFilter, firstRow, maxRows, flags, fetchSize);
}
@@ -162,7 +162,6 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
webResultSet.setReadOnlyInfo(contextInfo.getProcessor().getExecutionContext());
WebSQLResultsInfo resultsInfo = contextInfo.saveResult(dataContainer, trace, bindings, rows.size() == 1);
resultsInfo.setQueryText(resultSet.getSourceStatement().getQueryString());
webResultSet.setResultsInfo(resultsInfo);
boolean isSingleEntity = DBExecUtils.detectSingleSourceTable(bindings) != null;
@@ -233,6 +232,6 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
@Override
public void close() {
// no-op
rows.clear();
}
}
@@ -277,10 +277,8 @@ public class WebSQLUtils {
@Nullable String resultId,
@Nullable WebSQLDataFilter filter,
@Nullable WebDataFormat dataFormat,
boolean readLogs,
boolean useEvents
boolean readLogs
) {
final WebAsyncTaskInfo task = webSession.createAsyncTask("SQL execute");
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
@Override
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
@@ -288,8 +286,7 @@ public class WebSQLUtils {
monitor.beginTask("Execute query", 1);
monitor.subTask("Process query " + sql);
WebSQLExecuteInfo executeResults = contextInfo.getProcessor().processQuery(
monitor, contextInfo, sql, resultId, filter, dataFormat, webSession, task, readLogs, useEvents
);
monitor, contextInfo, sql, resultId, filter, dataFormat, webSession, readLogs);
this.result = executeResults.getStatusMessage();
this.extendedResults = executeResults;
} catch (Throwable e) {
@@ -299,6 +296,6 @@ public class WebSQLUtils {
}
}
};
return webSession.runAsyncTask(task, runnable);
return webSession.createAndRunAsyncTask("SQL execute", runnable);
}
}
@@ -191,8 +191,7 @@ public class WebServiceBindingSQL extends WebServiceBindingBase<DBWServiceSQL>
getArgument(env, "resultId"),
getDataFilter(env),
getDataFormat(env),
CommonUtils.toBoolean(getArgument(env, "readLogs")),
CommonUtils.toBoolean(getArgument(env, "isInteractive"))
CommonUtils.toBoolean(getArgument(env, "readLogs"))
)
)
.dataFetcher("asyncReadDataFromContainer", env ->
@@ -110,7 +110,7 @@ public class WebServiceSQL implements DBWServiceSQL {
@Override
@NotNull
public WebSQLDialectInfo getDialectInfo(@NotNull WebConnectionInfo connectionInfo) {
public WebSQLDialectInfo getDialectInfo(@NotNull WebConnectionInfo connectionInfo) throws DBWebException {
DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer();
SQLDialect dialect = getSqlDialectFromConnection(dataSourceContainer);
return new WebSQLDialectInfo(dataSourceContainer.getDataSource(), dialect);
@@ -400,16 +400,21 @@ public class WebServiceSQL implements DBWServiceSQL {
DBExecUtils.tryExecuteRecover(
monitor,
contextInfo.getProcessor().getConnection().getDataSource(),
monitor1 ->
result[0] = contextInfo.getProcessor().updateResultsDataBatch(
monitor1, contextInfo, resultsId, updatedRows, deletedRows, addedRows, dataFormat)
monitor1 -> {
try {
result[0] = contextInfo.getProcessor().updateResultsDataBatch(
monitor1, contextInfo, resultsId, updatedRows, deletedRows, addedRows, dataFormat);
} catch (Exception e) {
throw new InvocationTargetException(e);
}
}
);
return result[0];
}
@FunctionalInterface
private interface ThrowableFunction<T, R> {
R apply(T obj) throws DBException;
R apply(T obj) throws Exception;
}
@Override
@@ -432,6 +437,9 @@ public class WebServiceSQL implements DBWServiceSQL {
@NotNull Integer lobColumnIndex,
@NotNull WebSQLResultsRow row
) throws DBWebException {
if (row == null) {
throw new DBWebException("Results row is not found");
}
WebSQLProcessor processor = contextInfo.getProcessor();
ThrowableFunction<DBRProgressMonitor, String> function = monitor -> processor.readStringValue(
monitor, contextInfo, resultsId, lobColumnIndex, row);
@@ -449,7 +457,13 @@ public class WebServiceSQL implements DBWServiceSQL {
DBExecUtils.tryExecuteRecover(
processor.getWebSession().getProgressMonitor(),
processor.getConnection().getDataSource(),
monitor -> result.append(function.apply(monitor))
monitor -> {
try {
result.append(function.apply(monitor));
} catch (Exception e) {
throw new InvocationTargetException(e);
}
}
);
return result.toString();
} catch (DBException e) {
@@ -462,8 +476,7 @@ public class WebServiceSQL implements DBWServiceSQL {
try {
return contextInfo.getProcessor().generateResultsDataUpdateScript(
contextInfo.getProcessor().getWebSession().getProgressMonitor(),
contextInfo, resultsId, updatedRows, deletedRows, addedRows
);
contextInfo, resultsId, updatedRows, deletedRows, addedRows, dataFormat);
} catch (DBException e) {
throw new DBWebException("Error genering update script", e);
}
@@ -479,8 +492,7 @@ public class WebServiceSQL implements DBWServiceSQL {
@Nullable String resultId,
@Nullable WebSQLDataFilter filter,
@Nullable WebDataFormat dataFormat,
boolean readLogs,
boolean useEvents
boolean readLogs
) throws DBException {
if (DBWorkbench.isDistributed() && !webSession.hasPermission(DBWConstants.PERMISSION_SQL_EXECUTE_QUERY)) {
throw new DBWebException("Permission denied");
@@ -492,8 +504,7 @@ public class WebServiceSQL implements DBWServiceSQL {
resultId,
filter,
dataFormat,
readLogs,
useEvents
readLogs
);
}
@@ -503,11 +514,10 @@ public class WebServiceSQL implements DBWServiceSQL {
@NotNull String nodePath,
@Nullable String resultId,
@Nullable WebSQLDataFilter filter,
@Nullable WebDataFormat dataFormat
) {
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
@Nullable WebDataFormat dataFormat) throws DBWebException {
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<String>() {
@Override
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
monitor.beginTask("Read data", 1);
monitor.subTask("Extra data from " + nodePath);
@@ -521,8 +531,7 @@ public class WebServiceSQL implements DBWServiceSQL {
dataContainer,
resultId,
filter != null ? filter : new WebSQLDataFilter(),
dataFormat
);
dataFormat);
this.result = executeResults.getStatusMessage();
this.extendedResults = executeResults;
} catch (Throwable e) {
@@ -553,25 +562,24 @@ public class WebServiceSQL implements DBWServiceSQL {
@Override
public WebSQLExecuteInfo asyncGetQueryResults(@NotNull WebSession webSession, @NotNull String taskId) throws DBWebException {
WebAsyncTaskInfo taskStatus = webSession.asyncTaskStatus(taskId, false);
return (WebSQLExecuteInfo) taskStatus.getExtendedResult();
if (taskStatus != null) {
return (WebSQLExecuteInfo) taskStatus.getExtendedResult();
}
return null;
}
////////////////////////////////////////////////////
// Explain plan
@Override
public WebAsyncTaskInfo asyncSqlExplainExecutionPlan(
@NotNull WebSQLContextInfo contextInfo,
@NotNull String sql,
@NotNull Map<String, Object> configuration
) {
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
public WebAsyncTaskInfo asyncSqlExplainExecutionPlan(@NotNull WebSQLContextInfo contextInfo, @NotNull String sql, @NotNull Map<String, Object> configuration) throws DBException {
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<String>() {
@Override
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
monitor.beginTask("Explain execution plan", 1);
monitor.subTask("Explain query [" + sql + "] execution plan");
WebSQLExecutionPlan executeResults = contextInfo.getProcessor().explainExecutionPlan(monitor, sql, configuration);
WebSQLExecutionPlan executeResults = contextInfo.getProcessor().explainExecutionPlan(monitor, contextInfo, sql, configuration);
this.result = "Execution plan explain has been scheduled";
this.extendedResults = executeResults;
} catch (Throwable e) {
@@ -587,11 +595,14 @@ public class WebServiceSQL implements DBWServiceSQL {
@Override
public WebSQLExecutionPlan asyncSqlExplainExecutionPlanResult(@NotNull WebSession webSession, @NotNull String taskId) throws DBWebException {
WebAsyncTaskInfo taskStatus = webSession.asyncTaskStatus(taskId, false);
return (WebSQLExecutionPlan) taskStatus.getExtendedResult();
if (taskStatus != null) {
return (WebSQLExecutionPlan) taskStatus.getExtendedResult();
}
return null;
}
@Override
public WebSQLScriptInfo parseSqlScript(@NotNull WebConnectionInfo connectionInfo, @NotNull String sqlScript) {
public WebSQLScriptInfo parseSqlScript(@NotNull WebConnectionInfo connectionInfo, @NotNull String sqlScript) throws DBWebException {
SQLDialect dialect = getSqlDialectFromConnection(connectionInfo.getDataSourceContainer());
List<SQLScriptElement> queries = SQLScriptParser.parseScript(
connectionInfo.getDataSource(),
@@ -605,7 +616,7 @@ public class WebServiceSQL implements DBWServiceSQL {
}
@Override
public WebSQLQueryInfo parseSqlQuery(@NotNull WebConnectionInfo connectionInfo, @NotNull String sqlScript, int cursorPosition) {
public WebSQLQueryInfo parseSqlQuery(@NotNull WebConnectionInfo connectionInfo, @NotNull String sqlScript, int cursorPosition) throws DBWebException {
SQLDialect dialect = getSqlDialectFromConnection(connectionInfo.getDataSourceContainer());
SQLScriptElement query = SQLScriptParser.parseQuery(
connectionInfo.getDataSource(),
@@ -648,20 +659,14 @@ public class WebServiceSQL implements DBWServiceSQL {
}
@Override
public WebAsyncTaskInfo getRowDataCount(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo, @NotNull String resultsId) {
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
public WebAsyncTaskInfo getRowDataCount(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo, @NotNull String resultsId) throws DBWebException {
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<String>() {
@Override
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
monitor.beginTask("Get row data count", 1);
WebSQLResultsInfo results = contextInfo.getResults(resultsId);
long rowCount = DBUtils.readRowCount(
monitor,
contextInfo.getProcessor().getExecutionContext(),
results.getDataContainer(),
null,
this
);
long rowCount = DBUtils.readRowCount(monitor, contextInfo.getProcessor().getExecutionContext(), results.getDataContainer(), null, this);
this.result = "Row data count completed";
this.extendedResults = rowCount;
} catch (Throwable e) {
@@ -678,16 +683,19 @@ public class WebServiceSQL implements DBWServiceSQL {
@Nullable
public Long getRowDataCountResult(@NotNull WebSession webSession, @NotNull String taskId) throws DBWebException {
WebAsyncTaskInfo taskStatus = webSession.asyncTaskStatus(taskId, false);
return (Long) taskStatus.getExtendedResult();
if (taskStatus != null) {
return (Long) taskStatus.getExtendedResult();
}
return null;
}
@Override
public WebAsyncTaskInfo asyncSqlSetAutoCommit(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo, boolean autoCommit) {
public WebAsyncTaskInfo asyncSqlSetAutoCommit(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo, boolean autoCommit) throws DBWebException {
return contextInfo.setAutoCommit(autoCommit);
}
@Override
public WebAsyncTaskInfo asyncSqlRollbackTransaction(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo) {
public WebAsyncTaskInfo asyncSqlRollbackTransaction(@NotNull WebSession webSession, @NotNull WebSQLContextInfo contextInfo) throws DBWebException {
return contextInfo.rollbackTransaction();
}
@@ -1,35 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.service.sql.messages;
import org.eclipse.osgi.util.NLS;
public class WebSQLMessages extends NLS {
static final String BUNDLE_NAME = "io.cloudbeaver.service.sql.messages.WebSQLResources"; //$NON-NLS-1$
// TODO: add localizations
public static String model_web_ai_query_confirmation_title;
public static String model_web_ai_query_confirmation_message;
static {
NLS.initializeMessages(BUNDLE_NAME, WebSQLMessages.class);
}
private WebSQLMessages() {
}
}
@@ -1,2 +0,0 @@
model_web_ai_query_confirmation_title = Confirm query execution
model_web_ai_query_confirmation_message = The query you're about to execute can modify existing data or schema.\nDo you want to continue?
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Administration
Bundle-SymbolicName: io.cloudbeaver.service.admin;singleton:=true
Bundle-Version: 1.0.133.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 1.0.132.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.admin</artifactId>
<version>1.0.133-SNAPSHOT</version>
<version>1.0.132-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -16,7 +16,10 @@
*/
package io.cloudbeaver.service.admin;
import io.cloudbeaver.*;
import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.DBWFeatureSet;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebAction;
import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.registry.WebAuthProviderConfiguration;
@@ -113,7 +116,7 @@ public interface DBWServiceAdmin extends DBWService {
@NotNull WebSession webSession,
@NotNull String userID,
@NotNull String providerId,
@WebParameterSecure @NotNull Map<String, Object> credentials
@NotNull Map<String, Object> credentials
) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
@@ -153,7 +156,7 @@ public interface DBWServiceAdmin extends DBWService {
boolean disabled,
@Nullable String iconURL,
@Nullable String description,
@WebParameterSecure @Nullable Map<String, Object> parameters) throws DBWebException;
@Nullable Map<String, Object> parameters) throws DBWebException;
@WebAction(requirePermissions = DBWConstants.PERMISSION_ADMIN)
boolean deleteAuthProviderConfiguration(@NotNull WebSession webSession, @NotNull String id) throws DBWebException;
@@ -16,7 +16,10 @@
*/
package io.cloudbeaver.service.admin.impl;
import io.cloudbeaver.*;
import io.cloudbeaver.DBWFeatureSet;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebProjectImpl;
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.auth.provider.local.LocalAuthProvider;
import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.config.CBAppConfig;
@@ -360,11 +363,7 @@ public class WebServiceAdmin implements DBWServiceAdmin {
}
@Override
public boolean setUserCredentials(@NotNull WebSession webSession,
@NotNull String userID,
@NotNull String providerId,
@WebParameterSecure @NotNull Map<String, Object> credentials
) throws DBWebException {
public boolean setUserCredentials(@NotNull WebSession webSession, @NotNull String userID, @NotNull String providerId, @NotNull Map<String, Object> credentials) throws DBWebException {
WebAuthProviderDescriptor authProvider = WebAuthProviderRegistry.getInstance().getAuthProvider(providerId);
if (authProvider == null) {
throw new DBWebException("Invalid auth provider '" + providerId + "'");
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Authentication
Bundle-SymbolicName: io.cloudbeaver.service.auth;singleton:=true
Bundle-Version: 1.0.133.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 1.0.132.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.auth</artifactId>
<version>1.0.133-SNAPSHOT</version>
<version>1.0.132-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -18,7 +18,6 @@ package io.cloudbeaver.service.auth;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebAction;
import io.cloudbeaver.WebParameterSecure;
import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.DBWService;
@@ -78,11 +77,7 @@ public interface DBWServiceAuth extends DBWService {
WebAuthProviderInfo[] getAuthProviders(@NotNull HttpServletRequest request) throws DBWebException;
@WebAction()
boolean changeLocalPassword(
@NotNull WebSession webSession,
@WebParameterSecure @NotNull String oldPassword,
@WebParameterSecure @NotNull String newPassword
) throws DBWebException;
boolean changeLocalPassword(@NotNull WebSession webSession, @NotNull String oldPassword, @NotNull String newPassword) throws DBWebException;
@WebAction(authRequired = false)
WebPropertyInfo[] listUserProfileProperties(@NotNull WebSession webSession);
@@ -71,13 +71,8 @@ public class WebAsyncAuthJob extends AbstractJob implements CustomCancelableJob
@Override
public void cancelJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo) {
cancelJob(webSession, taskInfo, "Canceled by the user");
}
public void cancelJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo, @NotNull String errorMessage) {
taskInfo.setRunning(false);
taskInfo.setJobError(new DBException(errorMessage));
taskInfo.setJobError(new DBException("Canceled by the user"));
WebEventUtils.sendAsyncTaskEvent(webSession, taskInfo);
}
}
@@ -1,51 +0,0 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.service.auth;
import io.cloudbeaver.model.WebAsyncTaskInfo;
import io.cloudbeaver.model.session.WebSession;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.jkiss.code.NotNull;
public class WebAsyncAuthTimeoutJob extends Job {
@NotNull
private final WebSession webSession;
@NotNull
private final WebAsyncTaskInfo asyncInfo;
@NotNull
private final WebAsyncAuthJob authJob;
public WebAsyncAuthTimeoutJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo asyncInfo, @NotNull WebAsyncAuthJob authJob) {
super("Auth timeout job for " + asyncInfo.getName());
this.webSession = webSession;
this.asyncInfo = asyncInfo;
this.authJob = authJob;
}
@NotNull
@Override
protected IStatus run(@NotNull IProgressMonitor monitor) {
if (asyncInfo.isRunning()) {
authJob.cancelJob(webSession, asyncInfo, "Authentication timeout exceeded");
}
return Status.OK_STATUS;
}
}
@@ -62,7 +62,7 @@ import java.util.Map;
public class WebServiceAuthImpl implements DBWServiceAuth {
private static final Log log = Log.getLog(WebServiceAuthImpl.class);
private static final long DEFAULT_TIMEOUT_MILLISECONDS = 5 * 60 * 1000;
public static final String CONFIG_TEMP_ADMIN_USER_ID = "temp_config_admin";
@Override
public WebAuthStatus authLogin(
@@ -120,15 +120,11 @@ public class WebServiceAuthImpl implements DBWServiceAuth {
if (CommonUtils.isEmpty(smAuthInfo.getRedirectUrl())) {
throw new DBWebException("Missing redirect URL");
}
WebAsyncAuthJob job = new WebAsyncAuthJob(
providerId + " authentication job",
smAuthInfo.getAuthAttemptId(),
linkWithActiveUser
);
WebAsyncTaskInfo authTask = webSession.createAsyncTask(providerId + " authentication");
authTask.setRunning(true);
authTask.setJob(job);
new WebAsyncAuthTimeoutJob(webSession, authTask, job).schedule(DEFAULT_TIMEOUT_MILLISECONDS);
authTask.setJob(
new WebAsyncAuthJob(providerId + " authentication job", smAuthInfo.getAuthAttemptId(), linkWithActiveUser)
);
return new WebAsyncAuthStatus(smAuthInfo.getRedirectUrl(), authTask);
} catch (SMTooManySessionsException e) {
throw new DBWebException("User authentication failed", e.getErrorType(), e);
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Data Transfer
Bundle-SymbolicName: io.cloudbeaver.service.data.transfer;singleton:=true
Bundle-Version: 1.0.134.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 1.0.133.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.data.transfer</artifactId>
<version>1.0.134-SNAPSHOT</version>
<version>1.0.133-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -61,7 +61,7 @@ public interface DBWServiceDataTransfer extends DBWService {
@NotNull WebSQLResultsInfo webSQLResultsInfo,
@NotNull WebSession webSession) throws DBWebException;
@WebAction(requireGlobalPermissions = RMConstants.PERMISSION_DATA_EDITOR_EXPORT)
@WebAction(requireGlobalPermissions = RMConstants.GLOBAL_PERMISSION_DATA_EDITOR_EXPORT)
WebAsyncTaskInfo dataTransferExportDataFromResults(
WebSQLContextInfo sqlContextInfo,
String resultsId,
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - File System
Bundle-SymbolicName: io.cloudbeaver.service.fs;singleton:=true
Bundle-Version: 1.0.51.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 1.0.50.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server.ce,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.fs</artifactId>
<version>1.0.51-SNAPSHOT</version>
<version>1.0.50-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Name: Cloudbeaver Web Service - LDAP
Bundle-Vendor: DBeaver Corp
Bundle-SymbolicName: io.cloudbeaver.service.ldap.auth;singleton:=true
Bundle-Version: 1.0.16.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 1.0.15.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model;visibility:=reexport,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.ldap.auth</artifactId>
<version>1.0.16-SNAPSHOT</version>
<version>1.0.15-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -39,7 +39,6 @@ import org.jkiss.dbeaver.model.security.SMController;
import org.jkiss.utils.CommonUtils;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
@@ -49,8 +48,6 @@ import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
@@ -309,22 +306,18 @@ public class LdapAuthProvider implements SMAuthProviderExternal<SMSession>, SMBr
@NotNull
private String findUserNameFromDN(@NotNull String fullUserDN, @NotNull LdapSettings ldapSettings)
throws DBException {
try {
LdapName ldapDN = new LdapName(fullUserDN);
for (Rdn rdn : ldapDN.getRdns()) {
if (rdn.getType().equalsIgnoreCase(ldapSettings.getUserIdentifierAttr())) {
Object v = rdn.getValue();
if (v instanceof byte[]) {
return new String((byte[]) v, StandardCharsets.UTF_8);
}
return String.valueOf(v);
}
throws DBException {
String userId = null;
for (String dn : fullUserDN.split(",")) {
if (dn.startsWith(ldapSettings.getUserIdentifierAttr() + "=")) {
userId = dn.split("=")[1];
break;
}
throw new DBException("Failed to determine userId from user DN: " + fullUserDN);
} catch (Exception e) {
throw new DBException("Invalid user DN: " + fullUserDN, e);
}
if (userId == null) {
throw new DBException("Failed to determine userId from user DN: " + fullUserDN);
}
return userId;
}
@NotNull
@@ -555,16 +548,14 @@ public class LdapAuthProvider implements SMAuthProviderExternal<SMSession>, SMBr
context = initConnection(environment);
String searchFilter = "(member={0})";
String searchFilter = "(member=" + fullDN + ")";
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchResults = context.search(ldapSettings.getBaseDN(), searchFilter, new Object[] {fullDN}, searchControls);
searchResults = context.search(ldapSettings.getBaseDN(), searchFilter, searchControls);
while (searchResults.hasMore()) {
try {
SearchResult next = searchResults.next();
//add full dn
result.add(next.getNameInNamespace());
//add relative dn to base dn
result.add(next.getName());
} catch (Exception e) {
log.error("Failed fetch user group. Skipping...", e);
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Metadata
Bundle-SymbolicName: io.cloudbeaver.service.metadata;singleton:=true
Bundle-Version: 1.0.137.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 1.0.136.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.metadata</artifactId>
<version>1.0.137-SNAPSHOT</version>
<version>1.0.136-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Resource manager NIO implementation
Bundle-SymbolicName: io.cloudbeaver.service.rm.nio;singleton:=true
Bundle-Version: 1.0.51.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 1.0.50.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.model,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.rm.nio</artifactId>
<version>1.0.51-SNAPSHOT</version>
<version>1.0.50-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: Cloudbeaver Web Service - Resource manager
Bundle-SymbolicName: io.cloudbeaver.service.rm;singleton:=true
Bundle-Version: 1.0.86.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 1.0.85.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: io.cloudbeaver.server,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.rm</artifactId>
<version>1.0.86-SNAPSHOT</version>
<version>1.0.85-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Name: Cloudbeaver Web Service - Security
Bundle-Vendor: DBeaver Corp
Bundle-SymbolicName: io.cloudbeaver.service.security;singleton:=true
Bundle-Version: 1.0.89.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 1.0.88.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Require-Bundle: org.jkiss.dbeaver.model;visibility:=reexport,
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.service.security</artifactId>
<version>1.0.89-SNAPSHOT</version>
<version>1.0.88-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -101,11 +101,6 @@ public class LocalAuthProvider implements SMAuthProvider<LocalAuthSession>, SMBr
}
@Override
public boolean supportsOpeningSessionAsChild() {
return false;
}
public static boolean changeUserPassword(@NotNull WebSession webSession, @NotNull String oldPassword, @NotNull String newPassword) throws DBException {
String userName = webSession.getUser().getUserId();
@@ -2472,13 +2472,6 @@ public class CBEmbeddedSecurityController<T extends ServletAuthApplication>
if (autoAssign != null && CommonUtils.isNotEmpty(autoAssign.getAuthRoleAssignReason())) {
log.info(activeUserId + " authenticated with role " + autoAssign.getAuthRole() + ", reason: " + autoAssign.getAuthRoleAssignReason());
}
} else {
SMAuthProvider<?> authProviderInstance = authProvider.getInstance();
if (!authProviderInstance.supportsOpeningSessionAsChild()) {
var error = "Auth provider '" + authProviderId + "' doesn't support opening as a child session";
updateAuthStatus(authId, SMAuthStatus.ERROR, dbStoredUserData, error, null);
return SMAuthInfo.error(authId, error, isMainAuthSession, null, authInfo.getAppSessionId());
}
}
dbStoredUserData.put(
authConfiguration,
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
Bundle-Vendor: DBeaver Corp
Bundle-Name: CloudBeaver SLF4j Binding
Bundle-SymbolicName: io.cloudbeaver.slf4j;singleton:=true
Bundle-Version: 1.0.49.qualifier
Bundle-Release-Date: 20251117
Bundle-Version: 1.0.48.qualifier
Bundle-Release-Date: 20251103
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Fragment-Host: slf4j.api
+1 -1
View File
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.slf4j</artifactId>
<version>1.0.49-SNAPSHOT</version>
<version>1.0.48-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
-1
View File
@@ -23,7 +23,6 @@
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.25.5</version>
</dependency>
</dependencies>
+1 -1
View File
@@ -18,7 +18,7 @@
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>13.2.1.jre11</version>
<version>12.8.0.jre11</version>
</dependency>
<!--
<dependency>
@@ -2,7 +2,7 @@
<feature
id="io.cloudbeaver.ce.drivers.feature"
label="Cloudbeaver CE 3rd Party Drivers"
version="1.0.157.qualifier"
version="1.0.156.qualifier"
provider-name="DBeaver Corp">
<plugin id="io.cloudbeaver.resources.drivers.base" download-size="0" install-size="0" version="0.0.0"/>
@@ -9,6 +9,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.ce.drivers.feature</artifactId>
<version>1.0.157-SNAPSHOT</version>
<version>1.0.156-SNAPSHOT</version>
<packaging>eclipse-feature</packaging>
</project>
@@ -2,7 +2,7 @@
<feature
id="io.cloudbeaver.product.ce.feature"
label="%featureName"
version="25.2.5.qualifier"
version="25.2.4.qualifier"
provider-name="%providerName"
plugin="io.cloudbeaver.product.ce">
@@ -10,7 +10,7 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.product.ce.feature</artifactId>
<version>25.2.5-SNAPSHOT</version>
<version>25.2.4-SNAPSHOT</version>
<packaging>eclipse-feature</packaging>
<build>
@@ -2,7 +2,7 @@
<feature
id="io.cloudbeaver.server.feature"
label="%featureName"
version="25.2.5.qualifier"
version="25.2.4.qualifier"
provider-name="%providerName"
plugin="io.cloudbeaver.server">
@@ -10,6 +10,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.server.feature</artifactId>
<version>25.2.5-SNAPSHOT</version>
<version>25.2.4-SNAPSHOT</version>
<packaging>eclipse-feature</packaging>
</project>
@@ -2,7 +2,7 @@
<feature
id="io.cloudbeaver.ws.feature"
label="%featureName"
version="1.0.87.qualifier"
version="1.0.86.qualifier"
provider-name="%providerName"
plugin="io.cloudbeaver.model">
@@ -10,6 +10,6 @@
<relativePath>../</relativePath>
</parent>
<artifactId>io.cloudbeaver.ws.feature</artifactId>
<version>1.0.87-SNAPSHOT</version>
<version>1.0.86-SNAPSHOT</version>
<packaging>eclipse-feature</packaging>
</project>
@@ -2,7 +2,7 @@
<?pde version="3.5"?>
<product name="CloudBeaver ce Server" uid="io.cloudbeaver.product" id="io.cloudbeaver.product.ce.product"
application="io.cloudbeaver.product.ce.application" version="25.2.5.qualifier" useFeatures="true"
application="io.cloudbeaver.product.ce.application" version="25.2.4.qualifier" useFeatures="true"
includeLaunchers="false">
<configIni use="default">
+1 -1
View File
@@ -9,7 +9,7 @@
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
<version>25.2.5-SNAPSHOT</version>
<version>25.2.4-SNAPSHOT</version>
<artifactId>web-server</artifactId>
<packaging>eclipse-repository</packaging>
<name>Cloudbeaver Server Product</name>
@@ -1,11 +1,3 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { memo, use, useMemo } from 'react';
import { Cell, type CellRendererProps } from 'react-data-grid';
import { DataGridCellContext, type IDataGridCellRenderer } from './DataGridCellContext.js';
@@ -85,17 +77,19 @@ export const BaseCell = memo(function BaseCell<TRow, TSummaryRow>(props: CellRen
const renderDefaultCell = useMemo<IDataGridCellRenderer>(
() =>
({ onClick, onDoubleClick, onContextMenu, ...rest }) => (
<Cell
{...props}
title={tooltip}
onCellClick={(_, event) => onClick?.(event)}
onCellDoubleClick={(_, event) => onDoubleClick?.(event)}
onCellContextMenu={(_, event) => onContextMenu?.(event)}
{...rest}
isCellSelected={props.isCellSelected || rest.isFocused || false}
/>
),
({ onClick, onDoubleClick, onContextMenu, ...rest }) => {
return (
<Cell
{...props}
title={tooltip}
onCellClick={(_, event) => onClick?.(event)}
onCellDoubleClick={(_, event) => onDoubleClick?.(event)}
onCellContextMenu={(_, event) => onContextMenu?.(event)}
{...rest}
isCellSelected={props.isCellSelected || rest.isFocused || false}
/>
);
},
[...Object.values(props), tooltip],
);
@@ -1,14 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { Row, type RenderRowProps } from 'react-data-grid';
import { DataGridRowContext, type IDataGridRowRenderer } from './DataGridRowContext.js';
import { memo, use, useEffect, useMemo } from 'react';
import { DataGridRowContext } from './DataGridRowContext.js';
import { memo, use, useEffect } from 'react';
import { useGridReactiveValue } from './useGridReactiveValue.js';
export const BaseRow = memo(function BaseRow<TRow, TSummaryRow>(props: RenderRowProps<TRow, TSummaryRow>) {
@@ -20,18 +12,5 @@ export const BaseRow = memo(function BaseRow<TRow, TSummaryRow>(props: RenderRow
rowContext?.onScrollToBottom?.();
}
}, [rowsCount]);
const renderDefaultRow = useMemo<IDataGridRowRenderer>(
() => override => (
<Row
{...props}
{...override}
/>
),
[...Object.values(props)],
);
const rowElement = useGridReactiveValue(rowContext?.rowElement, props.rowIdx, props, renderDefaultRow);
return rowElement ?? <Row {...props} />;
return <Row {...props} />;
}) as <TRow, TSummaryRow>(props: RenderRowProps<TRow, TSummaryRow>) => React.ReactNode;
@@ -42,7 +42,6 @@ export interface DataGridCellKeyboardEvent extends React.KeyboardEvent<HTMLDivEl
export interface DataGridProps extends IDataGridCellContext, IDataGridRowContext, IDataGridHeaderCellContext, React.PropsWithChildren {
getRowHeight?: (rowIdx: number) => number;
getRowId?: (rowIdx: number) => React.Key;
getRowClass?: (rowIdx: number) => string | null;
columnCount: IGridReactiveValue<number, []>;
getColumnKey?: (colIdx: number) => string;
onScroll?: (event: React.UIEvent<HTMLDivElement>) => void;
@@ -79,11 +78,9 @@ export const DataGrid = forwardRef<DataGridRef, DataGridProps>(function DataGrid
getCellEditable,
columnCount,
getColumnKey,
rowElement,
rowCount,
getRowId,
getRowHeight,
getRowClass,
onHeaderReorder,
onScroll,
onScrollToBottom,
@@ -184,7 +181,7 @@ export const DataGrid = forwardRef<DataGridRef, DataGridProps>(function DataGrid
return (
<HeaderDnDContext value={dndHeaderContext}>
<DataGridRowContext value={{ rowElement, rowCount, onScrollToBottom }}>
<DataGridRowContext value={{ rowCount, onScrollToBottom }}>
<DataGridCellContext value={{ cell, cellText, cellElement, cellTooltip, onCellChange }}>
<DataGridCellHeaderContext
value={{ headerElement, headerText, getHeaderDnD, columnSortable, onColumnSort, columnSortingState, onHeaderKeyDown }}
@@ -197,7 +194,6 @@ export const DataGrid = forwardRef<DataGridRef, DataGridProps>(function DataGrid
headerRowHeight={getHeaderHeight?.()}
rowHeight={getRowHeight ? row => getRowHeight(row.idx) : undefined}
rowKeyGetter={getRowId ? row => getRowId(row.idx) : undefined}
rowClass={getRowClass ? row => getRowClass(row.idx) : undefined}
columnWidths={columnWidths}
renderers={{
renderRow: rowRenderer,
@@ -1,11 +1,3 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createContext } from 'react';
import type { IGridReactiveValue } from './IGridReactiveValue.js';
@@ -1,22 +1,7 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createContext } from 'react';
import type { IGridReactiveValue } from './IGridReactiveValue.js';
export interface IDataGridRowProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {}
export interface IDataGridRowRenderer {
(propsOverride: Partial<IDataGridRowProps>): React.ReactNode;
}
export interface IDataGridRowContext {
rowElement?: IGridReactiveValue<React.ReactNode, [rowIdx: number, props: IDataGridRowProps, renderDefaultRow: IDataGridRowRenderer]>;
rowCount: IGridReactiveValue<number, []>;
onScrollToBottom?: () => void;
}
@@ -1,13 +1,4 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
export * from './DataGrid.js';
export * from './DataGridRowContext.js';
export * from './DataGridCellContext.js';
export * from './DataGridCellInnerContext.js';
export * from './IGridReactiveValue.js';
@@ -1 +0,0 @@
# @dbeaver/react-translate
@@ -1,36 +0,0 @@
{
"name": "@dbeaver/react-translate",
"type": "module",
"sideEffects": [],
"exports": {
".": "./lib/index.js",
"./*": "./lib/*.js"
},
"scripts": {
"clear": "rimraf lib"
},
"files": [
"package.json",
"LICENSE",
"README.md",
"CHANGELOG.md",
"lib",
"!lib/**/*.d.ts.map",
"!lib/**/*.test.js",
"!lib/**/*.test.d.ts",
"!lib/**/*.test.d.ts.map",
"!lib/**/*.test.js.map",
"!lib/tests",
"!.tsbuildinfo"
],
"packageManager": "yarn@4.9.2",
"devDependencies": {
"@dbeaver/tsconfig": "workspace:^",
"@types/react": "^19",
"rimraf": "^6",
"typescript": "^5"
},
"dependencies": {
"react": "^19"
}
}
@@ -1,21 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { memo } from 'react';
import { useTranslate } from './useTranslate.js';
interface Props {
children: string;
args?: Record<string, any>;
fallback?: string;
}
export const Translate = memo<Props>(function Translate({ children, args, fallback }) {
const t = useTranslate();
return t(children, fallback, args);
});
@@ -1,15 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { createContext } from 'react';
import type { LocalizationToken } from './LocalizationToken.js';
export type TranslateFn = <T extends LocalizationToken | undefined>(token: T, fallback?: T, args?: Record<string | number, any>) => T;
export const TranslateContext = createContext<{ translate: TranslateFn }>({
translate: (key, fallback) => fallback || key,
});
@@ -1,12 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
export * from './LocalizationToken.js';
export * from './useTranslate.js';
export * from './Translate.js';
export * from './TranslateContext.js';
@@ -1,15 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { use } from 'react';
import { TranslateContext, type TranslateFn } from './TranslateContext.js';
export function useTranslate(): TranslateFn {
const context = use(TranslateContext);
return context.translate;
}
@@ -1,16 +0,0 @@
{
"extends": "@dbeaver/tsconfig/tsconfig.webview.json",
"compilerOptions": {
"tsBuildInfoFile": "./lib/.tsbuildinfo",
"rootDir": "src",
"rootDirs": [
"src"
],
"outDir": "lib",
"incremental": true,
"composite": true
},
"include": [
"src"
]
}
@@ -1,8 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.0708 13.9291L13.9286 28.0712L1.85742 16L15.9996 1.85786L28.0708 13.9291ZM25.4999 14.0002L15.9996 4.68629L6.50057 14.0002H25.4999Z" fill="#3387C5"/>
<mask id="path-2-inside-1_6364_2329" fill="white">
<path d="M14.9497 5.05025C13.637 3.7375 11.8565 3 10 3C8.14348 3 6.36301 3.7375 5.05025 5.05025C3.7375 6.36301 3 8.14349 3 10C3 11.8565 3.7375 13.637 5.05025 14.9497L10 10L14.9497 5.05025Z"/>
</mask>
<path d="M14.9497 5.05025C13.637 3.7375 11.8565 3 10 3C8.14348 3 6.36301 3.7375 5.05025 5.05025C3.7375 6.36301 3 8.14349 3 10C3 11.8565 3.7375 13.637 5.05025 14.9497L10 10L14.9497 5.05025Z" fill="#D9D9D9" stroke="#3387C5" stroke-width="4" mask="url(#path-2-inside-1_6364_2329)"/>
<path d="M30 27C30 29 28 31 26 31C24 31 22 29 22 27C22 25 23.5 23.3333 26 20C28.5 23.3333 30 25 30 27Z" fill="#3387C5"/>
</svg>

Before

Width:  |  Height:  |  Size: 916 B

@@ -1,8 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.5601 10.0096L11.0045 20.565L1.99512 11.5557L12.5507 1.0002L21.5601 10.0096ZM19.6411 10.0627L12.55 3.11115L5.46023 10.0627H19.6411Z" fill="#3387C5"/>
<mask id="path-2-inside-1_6364_2418" fill="white">
<path d="M11.7667 3.38279C10.7869 2.40299 9.45798 1.85254 8.07231 1.85254C6.68665 1.85254 5.35773 2.40299 4.37792 3.38279C3.39811 4.3626 2.84766 5.6915 2.84766 7.07716C2.84766 8.46281 3.39811 9.79171 4.37792 10.7715L8.07231 7.07716L11.7667 3.38279Z"/>
</mask>
<path d="M11.7667 3.38279C10.7869 2.40299 9.45798 1.85254 8.07231 1.85254C6.68665 1.85254 5.35773 2.40299 4.37792 3.38279C3.39811 4.3626 2.84766 5.6915 2.84766 7.07716C2.84766 8.46281 3.39811 9.79171 4.37792 10.7715L8.07231 7.07716L11.7667 3.38279Z" fill="#D9D9D9" stroke="#3387C5" stroke-width="3" mask="url(#path-2-inside-1_6364_2418)"/>
<path d="M22.9994 19.7656C22.9994 21.2584 21.5066 22.7511 20.0138 22.7511C18.5211 22.7511 17.0283 21.2584 17.0283 19.7656C17.0283 18.2729 18.1479 17.0289 20.0138 14.541C21.8798 17.0289 22.9994 18.2729 22.9994 19.7656Z" fill="#3387C5"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

@@ -1,8 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.0728 6.79956L7.27946 13.5932L1.48047 7.79395L8.27381 1.00034L14.0728 6.79956ZM5.39227 6.83477L9.23501 6.83408H12.8381L8.2745 2.36003L3.71085 6.83408L5.39227 6.83477Z" fill="#3387C5"/>
<mask id="path-2-inside-1_6364_2433" fill="white">
<path d="M7.77072 2.53377C7.1401 1.90312 6.28479 1.54883 5.39295 1.54883C4.50111 1.54883 3.6458 1.90312 3.01518 2.53377C2.38455 3.16442 2.03027 4.01977 2.03027 4.91164C2.03027 5.80351 2.38455 6.65886 3.01518 7.28951L5.39295 4.91164L7.77072 2.53377Z"/>
</mask>
<path d="M7.77072 2.53377C7.1401 1.90312 6.28479 1.54883 5.39295 1.54883C4.50111 1.54883 3.6458 1.90312 3.01518 2.53377C2.38455 3.16442 2.03027 4.01977 2.03027 4.91164C2.03027 5.80351 2.38455 6.65886 3.01518 7.28951L5.39295 4.91164L7.77072 2.53377Z" fill="#D9D9D9" stroke="#3387C5" stroke-width="2" mask="url(#path-2-inside-1_6364_2433)"/>
<path d="M15.0003 13.0786C15.0003 14.0394 14.0395 15.0002 13.0788 15.0002C12.118 15.0002 11.1572 14.0394 11.1572 13.0786C11.1572 12.1178 11.8778 11.3172 13.0788 9.71582C14.2797 11.3172 15.0003 12.1178 15.0003 13.0786Z" fill="#3387C5"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

@@ -1,4 +1,4 @@
<svg>
<svg xmlns="http://www.w3.org/2000/svg">
<!-- SORTING ICONS -->
<symbol id="icon-sort-unknown" viewBox="0 0 32 32" fill="currentColor">
<polygon points="4 10 14 10 9 4 4 10" />
@@ -76,7 +76,7 @@
<!-- CROSS ICON (ONLY ONE SIZE)-->
<symbol id="icon-cross_m" viewBox="0 0 24 24">
<path fill="currentColor" fill-rule="evenodd"
<path xmlns="http://www.w3.org/2000/svg" fill="currentColor" fill-rule="evenodd"
clip-rule="evenodd"
d="M3.31579 3.31579C2.89474 3.73684 2.89474 4.42105 3.31579 4.84211L10.4737 12L3.31579 19.1579C2.89474 19.5789 2.89474 20.2632 3.31579 20.6842C3.73684 21.1053 4.42105 21.1053 4.84211 20.6842L12 13.5263L19.1579 20.6842C19.5789 21.1053 20.2632 21.1053 20.6842 20.6842C21.1053 20.2632 21.1053 19.5789 20.6842 19.1579L13.5263 12L20.6842 4.84211C21.1053 4.42105 21.1053 3.73684 20.6842 3.31579C20.2632 2.89474 19.5789 2.89474 19.1579 3.31579L12 10.4737L4.84211 3.31579C4.42105 2.89474 3.73684 2.89474 3.31579 3.31579Z" />
</symbol>
@@ -298,78 +298,5 @@
<rect x="5.35" y="8.32" width="1.01" height="7.07"
transform="translate(-6.67 7.61) rotate(-45)" />
</symbol>
<!-- ARROW ICONS END -->
<symbol id="icon-no-color" viewBox="0 0 32 32" fill="none">
<path
d="M16 3.6709C17.2541 5.34249 18.3954 6.80132 19.4092 8.10742C20.6414 9.69492 21.6732 11.0484 22.5078 12.3213C24.1549 14.8333 25 16.9781 25 19.8184C24.9999 25.3957 20.5013 29 16 29C11.4987 29 7.0001 25.3957 7 19.8184C7 16.9781 7.84511 14.8333 9.49219 12.3213C10.3268 11.0484 11.3586 9.69492 12.5908 8.10742C13.6046 6.80132 14.7459 5.34249 16 3.6709Z"
stroke="#3387C5" stroke-width="2" />
<path d="M3 3L29 29" stroke="#3387C5" stroke-width="2" />
</symbol>
<symbol id="icon-no-color_m" viewBox="0 0 24 24" fill="none">
<path
d="M12 3.27051C12.8606 4.4394 13.6453 5.46203 14.3428 6.37891C15.2052 7.5127 15.9265 8.47801 16.5098 9.38574C17.6602 11.1761 18.25 12.7038 18.25 14.7275C18.2499 18.7036 15.1113 21.25 12 21.25C8.88869 21.25 5.75014 18.7036 5.75 14.7275C5.75 12.7038 6.33976 11.1761 7.49023 9.38574C8.07355 8.47801 8.79477 7.5127 9.65723 6.37891C10.3547 5.46203 11.1394 4.4394 12 3.27051Z"
stroke="#3387C5" stroke-width="1.5" />
<path d="M2.87109 2.72266L21.6643 21.5159" stroke="#3387C5" stroke-width="1.5" />
</symbol>
<symbol id="icon-no-color_sm" viewBox="0 0 16 16" fill="none">
<g clip-path="url(#clip0_6364_2471)">
<path
d="M8 1.83594C8.627 2.67162 9.19823 3.40073 9.70508 4.05371C10.3211 4.8473 10.8367 5.52384 11.2539 6.16016C12.0775 7.41621 12.5 8.48898 12.5 9.90918C12.5 12.6978 10.2506 14.5 8 14.5C5.74937 14.5 3.50005 12.6978 3.5 9.90918C3.5 8.48898 3.92249 7.41621 4.74609 6.16016C5.16334 5.52384 5.67894 4.8473 6.29492 4.05371C6.80177 3.40073 7.373 2.67162 8 1.83594Z"
stroke="#3387C5" />
<path d="M1 1L15 15" stroke="#3387C5" />
</g>
<defs>
<clipPath id="clip0_6364_2471">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</symbol>
<symbol id="icon-fill-color" viewBox="0 0 32 32" fill="currentColor">
<path
d="M28.0708 13.9291L13.9286 28.0712L1.85742 16L15.9996 1.85786L28.0708 13.9291ZM25.4999 14.0002L15.9996 4.68629L6.50057 14.0002H25.4999Z"
fill="#3387C5" />
<mask id="path-2-inside-1_6364_2329" fill="white">
<path
d="M14.9497 5.05025C13.637 3.7375 11.8565 3 10 3C8.14348 3 6.36301 3.7375 5.05025 5.05025C3.7375 6.36301 3 8.14349 3 10C3 11.8565 3.7375 13.637 5.05025 14.9497L10 10L14.9497 5.05025Z" />
</mask>
<path
d="M14.9497 5.05025C13.637 3.7375 11.8565 3 10 3C8.14348 3 6.36301 3.7375 5.05025 5.05025C3.7375 6.36301 3 8.14349 3 10C3 11.8565 3.7375 13.637 5.05025 14.9497L10 10L14.9497 5.05025Z"
fill="#D9D9D9" stroke="#3387C5" stroke-width="4" mask="url(#path-2-inside-1_6364_2329)" />
<path
d="M30 27C30 29 28 31 26 31C24 31 22 29 22 27C22 25 23.5 23.3333 26 20C28.5 23.3333 30 25 30 27Z"
fill="#3387C5" />
</symbol>
<symbol id="icon-fill-color_m" viewBox="0 0 24 24" fill="currentColor">
<path
d="M21.5601 10.0096L11.0045 20.565L1.99512 11.5557L12.5507 1.0002L21.5601 10.0096ZM19.6411 10.0627L12.55 3.11115L5.46023 10.0627H19.6411Z"
fill="#3387C5" />
<mask id="path-2-inside-1_6364_2418" fill="white">
<path
d="M11.7667 3.38279C10.7869 2.40299 9.45798 1.85254 8.07231 1.85254C6.68665 1.85254 5.35773 2.40299 4.37792 3.38279C3.39811 4.3626 2.84766 5.6915 2.84766 7.07716C2.84766 8.46281 3.39811 9.79171 4.37792 10.7715L8.07231 7.07716L11.7667 3.38279Z" />
</mask>
<path
d="M11.7667 3.38279C10.7869 2.40299 9.45798 1.85254 8.07231 1.85254C6.68665 1.85254 5.35773 2.40299 4.37792 3.38279C3.39811 4.3626 2.84766 5.6915 2.84766 7.07716C2.84766 8.46281 3.39811 9.79171 4.37792 10.7715L8.07231 7.07716L11.7667 3.38279Z"
fill="#D9D9D9" stroke="#3387C5" stroke-width="3" mask="url(#path-2-inside-1_6364_2418)" />
<path
d="M22.9994 19.7656C22.9994 21.2584 21.5066 22.7511 20.0138 22.7511C18.5211 22.7511 17.0283 21.2584 17.0283 19.7656C17.0283 18.2729 18.1479 17.0289 20.0138 14.541C21.8798 17.0289 22.9994 18.2729 22.9994 19.7656Z"
fill="#3387C5" />
</symbol>
<symbol id="icon-fill-color_sm" viewBox="0 0 16 16" fill="currentColor">
<path
d="M14.0728 6.79956L7.27946 13.5932L1.48047 7.79395L8.27381 1.00034L14.0728 6.79956ZM5.39227 6.83477L9.23501 6.83408H12.8381L8.2745 2.36003L3.71085 6.83408L5.39227 6.83477Z"
fill="#3387C5" />
<mask id="path-2-inside-1_6364_2433" fill="white">
<path
d="M7.77072 2.53377C7.1401 1.90312 6.28479 1.54883 5.39295 1.54883C4.50111 1.54883 3.6458 1.90312 3.01518 2.53377C2.38455 3.16442 2.03027 4.01977 2.03027 4.91164C2.03027 5.80351 2.38455 6.65886 3.01518 7.28951L5.39295 4.91164L7.77072 2.53377Z" />
</mask>
<path
d="M7.77072 2.53377C7.1401 1.90312 6.28479 1.54883 5.39295 1.54883C4.50111 1.54883 3.6458 1.90312 3.01518 2.53377C2.38455 3.16442 2.03027 4.01977 2.03027 4.91164C2.03027 5.80351 2.38455 6.65886 3.01518 7.28951L5.39295 4.91164L7.77072 2.53377Z"
fill="#D9D9D9" stroke="#3387C5" stroke-width="2" mask="url(#path-2-inside-1_6364_2433)" />
<path
d="M15.0003 13.0786C15.0003 14.0394 14.0395 15.0002 13.0788 15.0002C12.118 15.0002 11.1572 14.0394 11.1572 13.0786C11.1572 12.1178 11.8778 11.3172 13.0788 9.71582C14.2797 11.3172 15.0003 12.1178 15.0003 13.0786Z"
fill="#3387C5" />
</symbol>
<!-- COLOR PICKER ICONS -->
</svg>

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 21 KiB

@@ -1,4 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 3.6709C17.2541 5.34249 18.3954 6.80132 19.4092 8.10742C20.6414 9.69492 21.6732 11.0484 22.5078 12.3213C24.1549 14.8333 25 16.9781 25 19.8184C24.9999 25.3957 20.5013 29 16 29C11.4987 29 7.0001 25.3957 7 19.8184C7 16.9781 7.84511 14.8333 9.49219 12.3213C10.3268 11.0484 11.3586 9.69492 12.5908 8.10742C13.6046 6.80132 14.7459 5.34249 16 3.6709Z" stroke="#3387C5" stroke-width="2"/>
<path d="M3 3L29 29" stroke="#3387C5" stroke-width="2"/>
</svg>

Before

Width:  |  Height:  |  Size: 553 B

@@ -1,4 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 3.27051C12.8606 4.4394 13.6453 5.46203 14.3428 6.37891C15.2052 7.5127 15.9265 8.47801 16.5098 9.38574C17.6602 11.1761 18.25 12.7038 18.25 14.7275C18.2499 18.7036 15.1113 21.25 12 21.25C8.88869 21.25 5.75014 18.7036 5.75 14.7275C5.75 12.7038 6.33976 11.1761 7.49023 9.38574C8.07355 8.47801 8.79477 7.5127 9.65723 6.37891C10.3547 5.46203 11.1394 4.4394 12 3.27051Z" stroke="#3387C5" stroke-width="1.5"/>
<path d="M2.87109 2.72266L21.6643 21.5159" stroke="#3387C5" stroke-width="1.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 599 B

@@ -1,11 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_6364_2471)">
<path d="M8 1.83594C8.627 2.67162 9.19823 3.40073 9.70508 4.05371C10.3211 4.8473 10.8367 5.52384 11.2539 6.16016C12.0775 7.41621 12.5 8.48898 12.5 9.90918C12.5 12.6978 10.2506 14.5 8 14.5C5.74937 14.5 3.50005 12.6978 3.5 9.90918C3.5 8.48898 3.92249 7.41621 4.74609 6.16016C5.16334 5.52384 5.67894 4.8473 6.29492 4.05371C6.80177 3.40073 7.373 2.67162 8 1.83594Z" stroke="#3387C5"/>
<path d="M1 1L15 15" stroke="#3387C5"/>
</g>
<defs>
<clipPath id="clip0_6364_2471">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 670 B

@@ -32,8 +32,6 @@
},
"dependencies": {
"@ariakit/react": "^0",
"@dbeaver/js-helpers": "workspace:*",
"@dbeaver/react-translate": "workspace:^",
"clsx": "^2",
"react": "^19",
"react-dom": "^19"

Some files were not shown because too many files have changed in this diff Show More