diff --git a/e2e/src/test/java/com/arcadedb/e2e/RemoteDatabaseJavaApiTest.java b/e2e/src/test/java/com/arcadedb/e2e/RemoteDatabaseJavaApiTest.java index 79e322bd17..fe87805fa4 100644 --- a/e2e/src/test/java/com/arcadedb/e2e/RemoteDatabaseJavaApiTest.java +++ b/e2e/src/test/java/com/arcadedb/e2e/RemoteDatabaseJavaApiTest.java @@ -194,7 +194,6 @@ void createSchemaWithDynamicSqlScript() { """); Schema schema = database.getSchema(); - System.out.println("schema.toString() = " + schema.toString()); assertThat(schema.existsType("V1")).isTrue(); assertThat(schema.existsType("V2")).isTrue(); assertThat(schema.existsType("V3")).isTrue(); @@ -234,7 +233,7 @@ void multipleInsert() throws Exception { @Test @Disabled - void multipleInsertBAtched() throws Exception { + void multipleInsertBatched() throws Exception { database.command("sqlscript", """ create vertex type `TEXT_EMBEDDING` if not exists; create property TEXT_EMBEDDING.str if not exists STRING; diff --git a/grpc-client/src/main/java/com/arcadedb/remote/grpc/RemoteGrpcDatabase.java b/grpc-client/src/main/java/com/arcadedb/remote/grpc/RemoteGrpcDatabase.java index a6b8ce6183..01eed7c719 100644 --- a/grpc-client/src/main/java/com/arcadedb/remote/grpc/RemoteGrpcDatabase.java +++ b/grpc-client/src/main/java/com/arcadedb/remote/grpc/RemoteGrpcDatabase.java @@ -30,6 +30,7 @@ import com.arcadedb.exception.RecordNotFoundException; import com.arcadedb.exception.TimeoutException; import com.arcadedb.exception.TransactionException; +import com.arcadedb.log.LogManager; import com.arcadedb.query.sql.executor.InternalResultSet; import com.arcadedb.query.sql.executor.Result; import com.arcadedb.query.sql.executor.ResultInternal; @@ -93,9 +94,6 @@ import io.grpc.stub.ClientCallStreamObserver; import io.grpc.stub.ClientResponseObserver; import io.grpc.stub.StreamObserver; -import com.arcadedb.log.LogManager; -import java.util.logging.Level; -import java.util.stream.Collectors; import javax.annotation.Nullable; import java.util.ArrayList; @@ -120,6 +118,8 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.stream.Collectors; /** * Remote Database implementation using gRPC protocol instead of HTTP. Extends @@ -131,7 +131,6 @@ */ public class RemoteGrpcDatabase extends RemoteDatabase { - private final ArcadeDbServiceGrpc.ArcadeDbServiceBlockingV2Stub blockingStub; private final ArcadeDbServiceGrpc.ArcadeDbServiceStub asyncStub; private final RemoteSchema schema; @@ -282,7 +281,8 @@ public void commit() { CommitTransactionResponse response = blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS) .commitTransaction(request); - LogManager.instance().log(this, Level.FINE, "[After commit] Success: %s Committed: %s", response.getSuccess(), response.getCommitted()); + LogManager.instance() + .log(this, Level.FINE, "[After commit] Success: %s Committed: %s", response.getSuccess(), response.getCommitted()); if (!response.getSuccess()) { throw new TransactionException("Failed to commit transaction: " + response.getMessage()); @@ -332,7 +332,8 @@ public void rollback() { RollbackTransactionResponse response = blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS) .rollbackTransaction(request); - LogManager.instance().log(this, Level.FINE, "[After rollback] Success: %s Committed: %s", response.getSuccess(), response.getRolledBack()); + LogManager.instance() + .log(this, Level.FINE, "[After rollback] Success: %s Committed: %s", response.getSuccess(), response.getRolledBack()); if (!response.getSuccess()) { throw new TransactionException("Failed to rollback transaction: " + response.getMessage()); @@ -369,7 +370,8 @@ public void deleteRecord(final Record record) { try { if (LogManager.instance().isDebugEnabled()) { - LogManager.instance().log(this, Level.FINE, "CLIENT deleteRecord: db=%s, tx=%s, rid=%s", getName(), (transactionId != null), record.getIdentity()); + LogManager.instance().log(this, Level.FINE, "CLIENT deleteRecord: db=%s, tx=%s, rid=%s", getName(), (transactionId != null), + record.getIdentity()); } final DeleteRecordResponse resp = callUnary("DeleteRecord", @@ -397,7 +399,9 @@ public boolean deleteRecord(final String rid, final long timeoutMs) { try { if (LogManager.instance().isDebugEnabled()) { - LogManager.instance().log(this, Level.FINE, "CLIENT deleteRecord: db=%s, tx=%s, rid=%s, timeoutMs=%s", getName(), (transactionId != null), rid, timeoutMs); + LogManager.instance() + .log(this, Level.FINE, "CLIENT deleteRecord: db=%s, tx=%s, rid=%s, timeoutMs=%s", getName(), (transactionId != null), + rid, timeoutMs); } final DeleteRecordResponse res = callUnary("DeleteRecord", @@ -452,8 +456,12 @@ public ResultSet command(final String language, final String command, final Map< private ResultSet commandInternal(final String language, final String command, final Map params) { - ExecuteCommandRequest.Builder requestBuilder = ExecuteCommandRequest.newBuilder().setDatabase(getName()).setCommand(command) - .setLanguage(language).setCredentials(buildCredentials()); + ExecuteCommandRequest.Builder requestBuilder = ExecuteCommandRequest.newBuilder() + .setDatabase(getName()) + .setCommand(command) + .setLanguage(language) + .setReturnRows(true) + .setCredentials(buildCredentials()); if (transactionId != null) { requestBuilder.setTransaction(TransactionContext.newBuilder().setTransactionId(transactionId).setDatabase(getName()).build()); @@ -463,15 +471,12 @@ private ResultSet commandInternal(final String language, final String command, f requestBuilder.putAllParameters(convertParamsToGrpcValue(params)); } - boolean returnRows = true; - - requestBuilder.setReturnRows(returnRows); - try { if (LogManager.instance().isDebugEnabled()) - LogManager.instance().log(this, Level.FINE, "CLIENT executeCommand: db=%s, tx=%s, cmdLen=%s, params=%s", getName(), (transactionId != null), - requestBuilder.getCommand().length(), requestBuilder.getParametersCount()); + LogManager.instance() + .log(this, Level.FINE, "CLIENT executeCommand: db=%s, tx=%s, cmdLen=%s, params=%s", getName(), (transactionId != null), + requestBuilder.getCommand().length(), requestBuilder.getParametersCount()); final ExecuteCommandResponse response = callUnary("ExecuteCommand", () -> blockingStub.withDeadlineAfter(getTimeout(), TimeUnit.MILLISECONDS).executeCommand(requestBuilder.build())); @@ -485,21 +490,7 @@ private ResultSet commandInternal(final String language, final String command, f ResultSet resultSet; - if (returnRows) { - resultSet = createGrpcResultSet(response); - } else { - - resultSet = new InternalResultSet(); - - if (response.getAffectedRecords() > 0) { - - Map result = new HashMap<>(); - result.put("affected", response.getAffectedRecords()); - result.put("executionTime", response.getExecutionTimeMs()); - - ((InternalResultSet) resultSet).add(new ResultInternal(result)); - } - } + resultSet = createGrpcResultSet(response); return resultSet; } catch (StatusRuntimeException | StatusException e) { @@ -562,8 +553,9 @@ public ResultSet query(final String language, final String query, RemoteGrpcConf try { if (LogManager.instance().isDebugEnabled()) { - LogManager.instance().log(this, Level.FINE, "CLIENT executeQuery: db=%s, tx=%s, queryLen=%s, params=%s", getName(), (transactionId != null), - requestBuilder.getQuery().length(), requestBuilder.getParametersCount()); + LogManager.instance() + .log(this, Level.FINE, "CLIENT executeQuery: db=%s, tx=%s, queryLen=%s, params=%s", getName(), (transactionId != null), + requestBuilder.getQuery().length(), requestBuilder.getParametersCount()); } final ExecuteQueryRequest req = requestBuilder.build(); @@ -998,9 +990,10 @@ public String createRecord(final String cls, final Map props, fi try { if (LogManager.instance().isDebugEnabled()) { - LogManager.instance().log(this, Level.FINE, "CLIENT createRecord: db=%s, txOpen=%s, type=%s, propCount=%s, timeoutMs=%s", getName(), - (transactionId != null), - cls, props.size(), timeoutMs); + LogManager.instance() + .log(this, Level.FINE, "CLIENT createRecord: db=%s, txOpen=%s, type=%s, propCount=%s, timeoutMs=%s", getName(), + (transactionId != null), + cls, props.size(), timeoutMs); } final CreateRecordResponse res = callUnary("CreateRecord", @@ -1058,9 +1051,11 @@ private boolean updateRecord(final String rid, final PropertiesUpdate partial, f try { if (LogManager.instance().isDebugEnabled()) { - LogManager.instance().log(this, Level.FINE, "CLIENT updateRecord(partial): db=%s, txOpen=%s, rid=%s, timeoutMs=%s", getName(), (transactionId != null), - rid, - timeoutMs); + LogManager.instance() + .log(this, Level.FINE, "CLIENT updateRecord(partial): db=%s, txOpen=%s, rid=%s, timeoutMs=%s", getName(), + (transactionId != null), + rid, + timeoutMs); } final UpdateRecordResponse res = callUnary("UpdateRecord", @@ -1089,7 +1084,8 @@ private boolean updateRecordFull(final String rid, final GrpcRecord record, fina try { if (LogManager.instance().isDebugEnabled()) { - LogManager.instance().log(this, Level.FINE, "CLIENT updateRecord(full): db=%s, txOpen=%s, rid=%s, timeoutMs=%s", getName(), (transactionId != null), rid, + LogManager.instance().log(this, Level.FINE, "CLIENT updateRecord(full): db=%s, txOpen=%s, rid=%s, timeoutMs=%s", getName(), + (transactionId != null), rid, timeoutMs); } @@ -1156,9 +1152,10 @@ public Record lookupByRID(final RID rid, final boolean loadContent) { try { if (LogManager.instance().isDebugEnabled()) { - LogManager.instance().log(this, Level.FINE, "CLIENT lookupByRID: db=%s, txOpen=%s, rid=%s, loadContent=%s, timeoutMs=%s", getName(), - (transactionId != null), - rid, loadContent, getTimeout()); + LogManager.instance() + .log(this, Level.FINE, "CLIENT lookupByRID: db=%s, txOpen=%s, rid=%s, loadContent=%s, timeoutMs=%s", getName(), + (transactionId != null), + rid, loadContent, getTimeout()); } final LookupByRidResponse resp = callUnary("LookupByRid", @@ -1220,7 +1217,9 @@ public InsertSummary insertBulk(final InsertOptions options, final List InsertSummary ingestBidiCore(final List rows, final InsertOptions final List protoRows = rows.stream().map(mapper).collect(Collectors.toList()); if (LogManager.instance().isDebugEnabled()) { - LogManager.instance().log(this, Level.FINE, "CLIENT ingestBidi start: rows=%s, chunkSize=%s, maxInflight=%s, timeoutMs=%s", protoRows.size(), chunkSize, - maxInflight, timeoutMs); + LogManager.instance() + .log(this, Level.FINE, "CLIENT ingestBidi start: rows=%s, chunkSize=%s, maxInflight=%s, timeoutMs=%s", protoRows.size(), + chunkSize, + maxInflight, timeoutMs); } // --- streaming state @@ -1837,7 +1840,8 @@ private Map convertParamsToGrpcValue(Map para private Object grpcValueToObject(GrpcValue grpcValue) { Object out = ProtoUtils.fromGrpcValue(grpcValue); if (LogManager.instance().isDebugEnabled()) - LogManager.instance().log(this, Level.FINE, "CLIENT decode grpcValueToObject: %s -> %s", summarize(grpcValue), summarize(out)); + LogManager.instance() + .log(this, Level.FINE, "CLIENT decode grpcValueToObject: %s -> %s", summarize(grpcValue), summarize(out)); return out; } @@ -1961,7 +1965,8 @@ private void logTx(String phase, String rpcOp) { if (debugTx == null || !LogManager.instance().isDebugEnabled()) return; TxDebug d = debugTx; - LogManager.instance().log(this, Level.FINE, "TXDBG %s db=%s tx#%s label=%s owner=%s now=%s rpcOp=%s rpcSeq=%s beginSent=%s committed=%s rolledBack=%s", phase, + LogManager.instance().log(this, Level.FINE, + "TXDBG %s db=%s tx#%s label=%s owner=%s now=%s rpcOp=%s rpcSeq=%s beginSent=%s committed=%s rolledBack=%s", phase, d.dbName, d.id, d.txLabel, tidName(d.ownerThread), tidName(Thread.currentThread()), rpcOp, d.rpcSeq.get(), d.beginRpcSent, d.committed, @@ -1974,8 +1979,10 @@ void checkCrossThreadUse(String where) { return; Thread now = Thread.currentThread(); if (now != d.ownerThread) { - LogManager.instance().log(this, Level.WARNING, "TXDBG CROSS-THREAD %s db=%s tx#%s owner=%s now=%s label=%s (begin site follows)", where, d.dbName, d.id, - tidName(d.ownerThread), tidName(now), d.txLabel, d.beginSite); + LogManager.instance() + .log(this, Level.WARNING, "TXDBG CROSS-THREAD %s db=%s tx#%s owner=%s now=%s label=%s (begin site follows)", where, + d.dbName, d.id, + tidName(d.ownerThread), tidName(now), d.txLabel, d.beginSite); } } diff --git a/grpc-client/src/test/java/com/arcadedb/remote/grpc/GrpcServerPluginIT.java b/grpc-client/src/test/java/com/arcadedb/remote/grpc/GrpcServerPluginIT.java index 460c5cd091..c968ebd228 100644 --- a/grpc-client/src/test/java/com/arcadedb/remote/grpc/GrpcServerPluginIT.java +++ b/grpc-client/src/test/java/com/arcadedb/remote/grpc/GrpcServerPluginIT.java @@ -19,9 +19,11 @@ package com.arcadedb.remote.grpc; import com.arcadedb.GlobalConfiguration; +import com.arcadedb.query.sql.executor.Result; import com.arcadedb.query.sql.executor.ResultSet; import com.arcadedb.test.BaseGraphServerTest; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.List; @@ -40,20 +42,7 @@ public void setTestConfiguration() { "GRPC:com.arcadedb.server.grpc.GrpcServerPlugin"); } - @AfterEach - @Override - public void endTest() { - GlobalConfiguration.SERVER_PLUGINS.setValue(""); - super.endTest(); - } - - @Test - void testGrpcQueryWithAliasesAndMetadata() { - - server = new RemoteGrpcServer("localhost", 50051, "root", DEFAULT_PASSWORD_FOR_TESTS, true, List.of()); - - database = new RemoteGrpcDatabase(server, "localhost", 50051, 2480, getDatabaseName(), "root", DEFAULT_PASSWORD_FOR_TESTS); - + private void createSchema() { database.command("sqlscript", """ CREATE VERTEX TYPE article IF NOT EXISTS BUCKETS 8; CREATE PROPERTY article.id IF NOT EXISTS LONG; @@ -95,6 +84,31 @@ void testGrpcQueryWithAliasesAndMetadata() { "tags": ["tag2", "tag3"] }; """); + } + + @BeforeEach + @Override + public void beginTest() { + super.beginTest(); + server = new RemoteGrpcServer("localhost", 50051, "root", DEFAULT_PASSWORD_FOR_TESTS, true, List.of()); + + database = new RemoteGrpcDatabase(server, "localhost", 50051, 2480, getDatabaseName(), "root", DEFAULT_PASSWORD_FOR_TESTS); + createSchema(); + + } + + @AfterEach + @Override + public void endTest() { + GlobalConfiguration.SERVER_PLUGINS.setValue(""); + + database.close(); + server.close(); + super.endTest(); + } + + @Test + void testGrpcQueryWithAliasesAndMetadata() { String query = "SELECT *, @rid, @type, author AS _author FROM article"; ResultSet resultSet = database.query("sql", query); @@ -107,4 +121,74 @@ void testGrpcQueryWithAliasesAndMetadata() { ); } + + @Test + void testGrpcUpdateWithAlas() { + String update = """ + UPDATE article SET title = "My third article updated" RETURN AFTER *, author AS _author WHERE id = 3 + """; + + database.transaction(() -> { + + ResultSet updated = database.command("sql", update); + + assertThat(updated.hasNext()); + Result r = updated.next(); + assertThat(r.getProperty("_author")).isEqualTo("John Doe"); + }); + + } + + @Test + void testGrpcInsertWithReturn() { + String command = """ + INSERT INTO article CONTENT { + "id": 4, + "created": "2021-01-01 00:00:00", + "updated": "2021-01-01 00:00:00", + "title": "My fourth article", + "content": "This is the content of my fourth article", + "author": "John Doe", + "tags": ["tag1", "tag2"] + } + RETURN @this; + """; + + database.transaction(() -> { + + ResultSet updated = database.command("sql", command); + + assertThat(updated.hasNext()); + Result r = updated.next(); + assertThat(r.getProperty("id")).isEqualTo(4); + assertThat(r.getProperty("title")).isEqualTo("My fourth article"); + }); + + } + + @Test + void testGrpcCreateVertexWithReturn() { + String command = """ + CREATE VERTEX article CONTENT { + "id": 4, + "created": "2021-01-01 00:00:00", + "updated": "2021-01-01 00:00:00", + "title": "My fourth article", + "content": "This is the content of my fourth article", + "author": "John Doe", + "tags": ["tag1", "tag2"] + }; + """; + + database.transaction(() -> { + + ResultSet updated = database.command("sql", command); + + assertThat(updated.hasNext()); + Result r = updated.next(); + assertThat(r.getProperty("id")).isEqualTo(4); + assertThat(r.getProperty("title")).isEqualTo("My fourth article"); + }); + + } } diff --git a/grpcw/src/main/java/com/arcadedb/server/grpc/ArcadeDbGrpcService.java b/grpcw/src/main/java/com/arcadedb/server/grpc/ArcadeDbGrpcService.java index c8851178aa..48776a819f 100644 --- a/grpcw/src/main/java/com/arcadedb/server/grpc/ArcadeDbGrpcService.java +++ b/grpcw/src/main/java/com/arcadedb/server/grpc/ArcadeDbGrpcService.java @@ -55,6 +55,7 @@ import io.grpc.StatusRuntimeException; import io.grpc.stub.ServerCallStreamObserver; import io.grpc.stub.StreamObserver; +import org.jspecify.annotations.NonNull; import java.math.BigDecimal; import java.math.BigInteger; @@ -174,7 +175,9 @@ public void executeCommand(ExecuteCommandRequest req, StreamObserver 0 ? req.getMaxRows() : DEFAULT_MAX_COMMAND_ROWS; - ExecuteCommandResponse.Builder out = ExecuteCommandResponse.newBuilder().setSuccess(true).setMessage("OK"); + ExecuteCommandResponse.Builder out = ExecuteCommandResponse.newBuilder() + .setSuccess(true) + .setMessage("OK"); // Execute the command @@ -191,40 +194,27 @@ public void executeCommand(ExecuteCommandRequest req, StreamObserver responseObserver) { final String reqDb = request.getDatabase(); @@ -2380,7 +2375,6 @@ private GrpcRecord convertResultToGrpcRecord(Result result, Database db, Project LogManager.instance().log(this, Level.FINE, "ENC-RES DONE rid=%s type=%s props=%s", builder.getRid(), builder.getType(), builder.getPropertiesCount()); - return builder.build(); }