Dbeaver/pro#7975 Added row identifier data (#4056)

* dbeaver/pro#7975 added pk info to api

* dbeaver/pro#7975 added row id state

* dbeaver/pro#7975 update doc

* dbeaver/pro#7975 update api

* dbeaver/pro#7975 added copyright

* dbeaver/pro#7975-pk-info extended row id api, added tests

* dbeaver/pro#7975-pk-info added attribute

* dbeaver/pro#7975-pk-info renamed type

* dbeaver/pro#7975-pk-info removed excess params

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
This commit is contained in:
Dmitrii Barnukov
2026-01-27 19:02:18 +01:00
committed by GitHub
co-authored by mr-anton-t
parent d3b0b96bae
commit f82598ad66
5 changed files with 159 additions and 6 deletions
@@ -126,7 +126,11 @@ type SQLResultSet {
# server always returns hasMoreData = false
hasMoreData: Boolean!
"Identifies if result set has row identifier. If true then it is possible update data or load LOB files"
hasRowIdentifier: Boolean!
hasRowIdentifier: Boolean! @deprecated (reason: "use rowIdentifierState (25.3.4)")
"State of a table row identifier: METADATA_NOT_FOUND, NONE, PRIMARY_KEY, VIRTUAL_KEY"
rowIdentifierState: SQLRowIdentifierState @since(version: "25.3.4")
"Contains info about a result set row identifier. Present if rowIdentifierState is PRIMARY_KEY"
rowIdentifier: SQLRowIdentifier @since(version: "25.3.4")
"Identifies if result has children collections. If true then children collections can be read from the result set"
hasChildrenCollection: Boolean!
"Identifies if result set supports dynamic trace. If true then dynamic trace can be read from the result set"
@@ -139,6 +143,25 @@ type SQLResultSet {
readOnlyStatus: String @since(version: "25.0.1")
}
enum SQLRowIdentifierState {
METADATA_NOT_FOUND,
NONE,
PRIMARY_KEY,
VIRTUAL_KEY
}
type SQLRowIdentifier {
"Single or multiple attributes acting as a row identifier"
attributes: [SQLRowIdentifierAttribute!]!
"Type of a row identifier constraint"
constraintType: String!
}
type SQLRowIdentifierAttribute {
name: String!
ordinalPosition: Int!
}
type SQLQueryResults {
title: String
updateRowCount: Float
@@ -376,7 +399,7 @@ extend type Mutation {
resultId: ID,
filter: SQLDataFilter,
dataFormat: ResultDataFormat, # requested data format. May be ignored by server
readLogs: Boolean # added 23.2.1
readLogs: Boolean, # added 23.2.1
isInteractive: Boolean # Support WS events requested by server, e.g. confirmations. Added in 23.2.3
): AsyncTaskInfo!
@@ -17,6 +17,7 @@
package io.cloudbeaver.service.sql;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.service.sql.WebSQLResultSetRowIdentifier.WebSQLResultSetRowIdentifierState;
import io.cloudbeaver.utils.ServletAppUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
@@ -33,10 +34,14 @@ import org.jkiss.dbeaver.model.meta.MetaData;
import org.jkiss.dbeaver.model.sql.DBQuotaException;
import org.jkiss.dbeaver.model.struct.DBSDataContainer;
import org.jkiss.dbeaver.model.struct.DBSEntity;
import org.jkiss.dbeaver.model.struct.DBSEntityConstraintType;
import org.jkiss.utils.CommonUtils;
import java.lang.reflect.Method;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class WebSQLQueryDataReceiver implements DBDDataReceiver {
@@ -182,9 +187,26 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver {
webResultSet.setSingleEntity(isSingleEntity);
Set<DBDRowIdentifier> rowIdentifiers = resultsInfo.getRowIdentifiers();
boolean hasRowIdentifier = rowIdentifiers.stream().allMatch(DBDRowIdentifier::isValidIdentifier);
webResultSet.setHasRowIdentifier(!rowIdentifiers.isEmpty() && hasRowIdentifier);
DBDRowIdentifier rowIdentifier = resultsInfo.getDefaultRowIdentifier();
if (rowIdentifier == null) {
webResultSet.setRowIdentifierState(WebSQLResultSetRowIdentifierState.METADATA_NOT_FOUND);
} else if (rowIdentifier.getUniqueKey().getConstraintType().equals(DBSEntityConstraintType.VIRTUAL_KEY)) {
webResultSet.setRowIdentifierState(WebSQLResultSetRowIdentifierState.VIRTUAL_KEY);
} else if (!rowIdentifier.isIncomplete() && rowIdentifier.isValidIdentifier()) {
webResultSet.setHasRowIdentifier(true);
webResultSet.setRowIdentifierState(WebSQLResultSetRowIdentifierState.PRIMARY_KEY);
List<WebSQLResultSetRowIdentifierAttribute> attributes = rowIdentifier.getAttributes().stream()
.map(a -> new WebSQLResultSetRowIdentifierAttribute(
a.getName(), a.getOrdinalPosition(), a.getLabel(), a.getDescription()
))
.toList();
String constraintTypeName = rowIdentifier.getUniqueKey().getConstraintType().getName();
webResultSet.setRowIdentifier(
new WebSQLResultSetRowIdentifier(constraintTypeName, attributes)
);
} else {
webResultSet.setRowIdentifierState(WebSQLResultSetRowIdentifierState.NONE);
}
}
private void convertComplexValuesToRelationalView(DBCSession session) {
@@ -16,7 +16,9 @@
*/
package io.cloudbeaver.service.sql;
import io.cloudbeaver.service.sql.WebSQLResultSetRowIdentifier.WebSQLResultSetRowIdentifierState;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
import org.jkiss.dbeaver.model.exec.DBCExecutionContext;
@@ -39,6 +41,8 @@ public class WebSQLQueryResultSet {
private WebSQLResultsInfo resultsInfo;
private boolean singleEntity = true;
private boolean hasRowIdentifier;
private WebSQLResultSetRowIdentifierState rowIdentifierState;
private WebSQLResultSetRowIdentifier rowIdentifier;
private boolean hasChildrenCollection;
private boolean isSupportsDataFilter;
@@ -121,6 +125,25 @@ public class WebSQLQueryResultSet {
this.hasRowIdentifier = hasRowIdentifier;
}
@Property
@Nullable
public WebSQLResultSetRowIdentifier getRowIdentifier() {
return rowIdentifier;
}
public void setRowIdentifier(@NotNull WebSQLResultSetRowIdentifier rowIdentifier) {
this.rowIdentifier = rowIdentifier;
}
@Property
public WebSQLResultSetRowIdentifierState getRowIdentifierState() {
return rowIdentifierState;
}
public void setRowIdentifierState(WebSQLResultSetRowIdentifierState rowIdentifierState) {
this.rowIdentifierState = rowIdentifierState;
}
@Property
public boolean isHasChildrenCollection() {
return hasChildrenCollection;
@@ -0,0 +1,55 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2026 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;
import org.jkiss.code.NotNull;
import java.util.List;
public class WebSQLResultSetRowIdentifier {
@NotNull
private final String constraintType;
@NotNull
private final List<WebSQLResultSetRowIdentifierAttribute> attributes;
public WebSQLResultSetRowIdentifier(
@NotNull String type,
@NotNull List<WebSQLResultSetRowIdentifierAttribute> attributes
) {
this.constraintType = type;
this.attributes = attributes;
}
@NotNull
public String getConstraintType() {
return constraintType;
}
@NotNull
public List<WebSQLResultSetRowIdentifierAttribute> getAttributes() {
return attributes;
}
public enum WebSQLResultSetRowIdentifierState {
METADATA_NOT_FOUND,
NONE,
PRIMARY_KEY,
VIRTUAL_KEY
}
}
@@ -0,0 +1,30 @@
package io.cloudbeaver.service.sql;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
public class WebSQLResultSetRowIdentifierAttribute {
@NotNull
private final String name;
private final int ordinalPosition;
public WebSQLResultSetRowIdentifierAttribute(
@NotNull String name,
int ordinalPosition,
@NotNull String label,
@Nullable String description
) {
this.name = name;
this.ordinalPosition = ordinalPosition;
}
@NotNull
public String getName() {
return name;
}
public int getOrdinalPosition() {
return ordinalPosition;
}
}