Skip to content

Commit 8a685ef

Browse files
DenisSinelnikovteunlaoEvgeniaBzzz
authored
Cb 1036 server output log sql editor (dbeaver#1999)
* CB-2551. Fixed additional auth for disabled user * CB-3923. Added output reader logs * CB-3923. Added output reader logs * CB-3923. Refactor after review * CB-3923. Refactor after review * CB-3923. Refactor after review * CB-3923. Refactor after review * CB-3923. Refactor after review * CB-3923. Refactor after review * CB-3923. Refactor after review * CB-3923. Refactor after review * CB-3923. Refactor after review * CB-3924: add show output button * CB-3924: add output event handler and resource * CB-3924: add server output logs panel * CB-3924: fix bugs * CB-3923. Fixed checkstyle * CB-3924: fixes after review * CB-3923. Refactor after review * CB-3924: fixes after review * CB-3924: add line wrapper extension for editor * CB-3923. Refactor after review * CB-3924: show output logs for current context * CB-3924: fixes after review * CB-3924: add output logs icon * CB-3924: fixes after review * CB-3924: fixes after review * CB-3924: fixes after review * CB-3924: fix bug with not updated menus * CB-3924. Fix boolean type --------- Co-authored-by: Dmitry Osipov <teunlao@gmail.com> Co-authored-by: EvgeniaBzzz <139753579+EvgeniaBzzz@users.noreply.github.com>
1 parent a981d37 commit 8a685ef

49 files changed

Lines changed: 908 additions & 61 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

server/bundles/io.cloudbeaver.server/schema/service.events.graphqls

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ enum CBServerEventId {
2828
cb_rm_project_removed,
2929

3030
cb_object_permissions_updated,
31-
cb_subject_permissions_updated
31+
cb_subject_permissions_updated,
32+
33+
cb_database_output_log_updated
3234
}
3335

3436
# Events sent by client
@@ -48,7 +50,8 @@ enum CBEventTopic {
4850
cb_scripts,
4951
cb_projects,
5052
cb_object_permissions,
51-
cb_subject_permissions
53+
cb_subject_permissions,
54+
cb_database_output_log
5255
}
5356

5457
# Base server event interface
@@ -153,6 +156,22 @@ type CBProjectsActiveEvent implements CBClientEvent {
153156
projectIds: [String!]! # list of active projects
154157
}
155158

159+
# Database output log event
160+
type CBDatabaseOutputLogEvent implements CBServerEvent {
161+
id: CBServerEventId!
162+
topicId: CBEventTopic
163+
contextId: String!
164+
messages: [WSOutputLogInfo!]!
165+
eventTimestamp: Int!
166+
}
167+
168+
# Define the type for WSOutputLogInfo
169+
type WSOutputLogInfo {
170+
severity: String
171+
message: String
172+
# Add more fields as needed
173+
}
174+
156175
extend type Query {
157176
emptyEvent: Boolean
158177
}

server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,8 @@ extend type Mutation {
289289
sql: String!,
290290
resultId: ID,
291291
filter: SQLDataFilter,
292-
dataFormat: ResultDataFormat # requested data format. May be ignored by server
292+
dataFormat: ResultDataFormat, # requested data format. May be ignored by server
293+
readLogs: Boolean # added 23.2.1
293294
): AsyncTaskInfo!
294295

295296
# Read data from table
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* DBeaver - Universal Database Manager
3+
* Copyright (C) 2010-2023 DBeaver Corp and others
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package io.cloudbeaver.server.jobs;
18+
19+
import io.cloudbeaver.model.session.WebSession;
20+
import org.eclipse.core.runtime.IStatus;
21+
import org.eclipse.core.runtime.Status;
22+
import org.jkiss.code.NotNull;
23+
import org.jkiss.code.Nullable;
24+
import org.jkiss.dbeaver.Log;
25+
import org.jkiss.dbeaver.model.exec.DBCException;
26+
import org.jkiss.dbeaver.model.exec.DBCExecutionContext;
27+
import org.jkiss.dbeaver.model.exec.DBCStatement;
28+
import org.jkiss.dbeaver.model.exec.output.DBCOutputSeverity;
29+
import org.jkiss.dbeaver.model.exec.output.DBCOutputWriter;
30+
import org.jkiss.dbeaver.model.exec.output.DBCServerOutputReader;
31+
import org.jkiss.dbeaver.model.runtime.AbstractJob;
32+
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
33+
import org.jkiss.dbeaver.model.websocket.event.WSOutputLogInfo;
34+
import org.jkiss.dbeaver.model.websocket.event.session.WSOutputDBLogEvent;
35+
import org.jkiss.dbeaver.runtime.DBWorkbench;
36+
37+
import java.util.ArrayList;
38+
import java.util.List;
39+
40+
41+
public class SqlOutputLogReaderJob extends AbstractJob {
42+
43+
private static final Log log = Log.getLog(SqlOutputLogReaderJob.class);
44+
45+
@NotNull
46+
private final WebSession webSession;
47+
@NotNull
48+
private final DBCExecutionContext dbcExecutionContext;
49+
@NotNull
50+
private final DBCStatement dbcStatement;
51+
@NotNull
52+
private final DBCServerOutputReader dbcServerOutputReader;
53+
@NotNull
54+
private final String contextInfoId;
55+
56+
public SqlOutputLogReaderJob(@NotNull WebSession webSession,
57+
@NotNull DBCExecutionContext dbcExecutionContext,
58+
@NotNull DBCStatement dbcStatement,
59+
@NotNull DBCServerOutputReader dbcServerOutputReader,
60+
@NotNull String contextInfoId) {
61+
super("Sql log state job");
62+
this.webSession = webSession;
63+
this.dbcExecutionContext = dbcExecutionContext;
64+
this.dbcStatement = dbcStatement;
65+
this.dbcServerOutputReader = dbcServerOutputReader;
66+
this.contextInfoId = contextInfoId;
67+
}
68+
69+
@Override
70+
protected IStatus run(DBRProgressMonitor monitor) {
71+
if (!DBWorkbench.getPlatform().isShuttingDown()) {
72+
try {
73+
if (!dbcStatement.isStatementClosed()) {
74+
dumpOutput(monitor);
75+
schedule(100);
76+
}
77+
} catch (Exception e) {
78+
log.debug("Failed to execute job " + e.getMessage(), e);
79+
}
80+
}
81+
return Status.OK_STATUS;
82+
}
83+
84+
private void dumpOutput(DBRProgressMonitor monitor) {
85+
if (!monitor.isCanceled()) {
86+
if (dbcServerOutputReader.isAsyncOutputReadSupported()) {
87+
try {
88+
if (!dbcStatement.isStatementClosed()) {
89+
List<WSOutputLogInfo> messages = new ArrayList<>();
90+
dbcServerOutputReader.readServerOutput(monitor, dbcExecutionContext, null, dbcStatement, new DBCOutputWriter() {
91+
@Override
92+
public void println(@Nullable DBCOutputSeverity severity, @Nullable String message) {
93+
if (message != null && severity != null) {
94+
messages.add(new WSOutputLogInfo(severity.getName(), message));
95+
}
96+
}
97+
98+
@Override
99+
public void flush() {
100+
messages.clear();
101+
}
102+
});
103+
webSession.addSessionEvent(new WSOutputDBLogEvent(
104+
contextInfoId,
105+
messages,
106+
System.currentTimeMillis()));
107+
}
108+
} catch (DBCException e) {
109+
log.error(e);
110+
}
111+
}
112+
}
113+
}
114+
}

server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ WebAsyncTaskInfo asyncExecuteQuery(
9494
@NotNull String sql,
9595
@Nullable String resultId,
9696
@Nullable WebSQLDataFilter filter,
97-
@Nullable WebDataFormat dataFormat) throws DBException;
97+
@Nullable WebDataFormat dataFormat,
98+
boolean readLogs,
99+
@NotNull WebSession webSession) throws DBException;
98100

99101
@WebAction
100102
WebAsyncTaskInfo asyncReadDataFromContainer(

server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLProcessor.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io.cloudbeaver.model.WebConnectionInfo;
2121
import io.cloudbeaver.model.session.WebSession;
2222
import io.cloudbeaver.model.session.WebSessionProvider;
23+
import io.cloudbeaver.server.jobs.SqlOutputLogReaderJob;
2324
import org.eclipse.jface.text.Document;
2425
import org.jkiss.code.NotNull;
2526
import org.jkiss.code.Nullable;
@@ -31,10 +32,12 @@
3132
import org.jkiss.dbeaver.model.data.*;
3233
import org.jkiss.dbeaver.model.edit.DBEPersistAction;
3334
import org.jkiss.dbeaver.model.exec.*;
35+
import org.jkiss.dbeaver.model.exec.output.DBCServerOutputReader;
3436
import org.jkiss.dbeaver.model.exec.plan.DBCPlan;
3537
import org.jkiss.dbeaver.model.exec.plan.DBCQueryPlanner;
3638
import org.jkiss.dbeaver.model.exec.plan.DBCQueryPlannerConfiguration;
3739
import org.jkiss.dbeaver.model.impl.AbstractExecutionSource;
40+
import org.jkiss.dbeaver.model.impl.DefaultServerOutputReader;
3841
import org.jkiss.dbeaver.model.navigator.DBNDatabaseItem;
3942
import org.jkiss.dbeaver.model.navigator.DBNNode;
4043
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
@@ -152,7 +155,9 @@ public WebSQLExecuteInfo processQuery(
152155
@NotNull String sql,
153156
@Nullable String resultId,
154157
@Nullable WebSQLDataFilter filter,
155-
@Nullable WebDataFormat dataFormat) throws DBWebException {
158+
@Nullable WebDataFormat dataFormat,
159+
@NotNull WebSession webSession,
160+
boolean readLogs) throws DBWebException {
156161
if (filter == null) {
157162
// Use default filter
158163
filter = new WebSQLDataFilter();
@@ -203,6 +208,17 @@ public WebSQLExecuteInfo processQuery(
203208
webDataFilter.getOffset(),
204209
webDataFilter.getLimit()))
205210
{
211+
SqlOutputLogReaderJob sqlOutputLogReaderJob = null;
212+
if (readLogs) {
213+
DBPDataSource dataSource = context.getDataSource();
214+
DBCServerOutputReader dbcServerOutputReader = DBUtils.getAdapter(DBCServerOutputReader.class, dataSource);
215+
if (dbcServerOutputReader == null) {
216+
dbcServerOutputReader = new DefaultServerOutputReader();
217+
}
218+
sqlOutputLogReaderJob = new SqlOutputLogReaderJob(
219+
webSession, context, dbStat, dbcServerOutputReader, contextInfo.getId());
220+
sqlOutputLogReaderJob.schedule();
221+
}
206222
// Set query timeout
207223
int queryTimeout = (int) session.getDataSource().getContainer().getPreferenceStore()
208224
.getDouble(WebSQLConstants.QUOTA_PROP_SQL_QUERY_TIMEOUT);
@@ -220,6 +236,11 @@ public WebSQLExecuteInfo processQuery(
220236
}
221237

222238
boolean hasResultSet = dbStat.executeStatement();
239+
240+
// Wait SqlLogStateJob, if its starts
241+
if (sqlOutputLogReaderJob != null) {
242+
sqlOutputLogReaderJob.join();
243+
}
223244
fillQueryResults(contextInfo, dataContainer, dbStat, hasResultSet, executeInfo, webDataFilter, dataFilter, dataFormat);
224245
} catch (DBException e) {
225246
throw new InvocationTargetException(e);

server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,9 @@ public void bindWiring(DBWBindingContext model) throws DBWebException {
160160
env.getArgument("sql"),
161161
env.getArgument("resultId"),
162162
getDataFilter(env),
163-
getDataFormat(env)))
163+
getDataFormat(env),
164+
CommonUtils.toBoolean(env.getArgument("readLogs")),
165+
getWebSession(env)))
164166
.dataFetcher("asyncReadDataFromContainer", env ->
165167
getService(env).asyncReadDataFromContainer(
166168
getSQLContext(env),

server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,16 +363,18 @@ public WebAsyncTaskInfo asyncExecuteQuery(
363363
@NotNull String sql,
364364
@Nullable String resultId,
365365
@Nullable WebSQLDataFilter filter,
366-
@Nullable WebDataFormat dataFormat)
366+
@Nullable WebDataFormat dataFormat,
367+
boolean readLogs,
368+
@NotNull WebSession webSession)
367369
{
368-
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<String>() {
370+
WebAsyncTaskProcessor<String> runnable = new WebAsyncTaskProcessor<>() {
369371
@Override
370-
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
372+
public void run(DBRProgressMonitor monitor) throws InvocationTargetException {
371373
try {
372374
monitor.beginTask("Execute query", 1);
373375
monitor.subTask("Process query " + sql);
374376
WebSQLExecuteInfo executeResults = contextInfo.getProcessor().processQuery(
375-
monitor, contextInfo, sql, resultId, filter, dataFormat);
377+
monitor, contextInfo, sql, resultId, filter, dataFormat, webSession, readLogs);
376378
this.result = executeResults.getStatusMessage();
377379
this.extendedResults = executeResults;
378380
} catch (Throwable e) {

webapp/packages/core-blocks/src/FormControls/InputField.m.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@
2929
height: 100%;
3030
}
3131
}
32+
33+
.customIconContainer {
34+
composes: iconContainer;
35+
right: 4px;
36+
width: 24px;
37+
height: 24px;
38+
cursor: auto;
39+
}
40+
3241
.input[disabled] + .iconContainer {
3342
cursor: auto;
3443
opacity: 0.8;

webapp/packages/core-blocks/src/FormControls/InputField.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* you may not use this file except in compliance with the License.
77
*/
88
import { observer } from 'mobx-react-lite';
9-
import { forwardRef, useCallback, useContext, useLayoutEffect, useRef, useState } from 'react';
9+
import React, { forwardRef, useCallback, useContext, useLayoutEffect, useRef, useState } from 'react';
1010
import styled, { use } from 'reshadow';
1111

1212
import type { ComponentStyle } from '@cloudbeaver/core-theming';
@@ -41,6 +41,7 @@ type BaseProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' |
4141
style?: ComponentStyle;
4242
canShowPassword?: boolean;
4343
onCustomCopy?: () => void;
44+
icon?: React.ReactElement;
4445
};
4546

4647
type ControlledProps = BaseProps & {
@@ -90,6 +91,7 @@ export const InputField: InputFieldType = observer(
9091
canShowPassword = true,
9192
onChange,
9293
onCustomCopy,
94+
icon,
9395
...rest
9496
}: ControlledProps | ObjectProps<any, any>,
9597
ref,
@@ -196,6 +198,7 @@ export const InputField: InputFieldType = observer(
196198
<Icon name="copy" viewBox="0 0 32 32" className={styles.icon} />
197199
</div>
198200
)}
201+
{icon && <div data-testid="icon-container" className={styles.customIconContainer}>{icon}</div>}
199202
</div>
200203
{(description || passwordType) && (
201204
<div data-testid="field-description" className={s(styles, { fieldDescription: true, valid: !error, invalid: error })}>

webapp/packages/core-sdk/src/queries/grid/asyncSqlExecuteQuery.gql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mutation asyncSqlExecuteQuery(
55
$resultId: ID
66
$filter: SQLDataFilter
77
$dataFormat: ResultDataFormat
8+
$readLogs: Boolean
89
) {
910
taskInfo: asyncSqlExecuteQuery(
1011
connectionId: $connectionId
@@ -13,6 +14,7 @@ mutation asyncSqlExecuteQuery(
1314
resultId: $resultId
1415
filter: $filter
1516
dataFormat: $dataFormat
17+
readLogs: $readLogs
1618
) {
1719
...AsyncTaskInfo
1820
}

0 commit comments

Comments
 (0)