Copilot commented on code in PR #9201:
URL: https://github.com/apache/gravitino/pull/9201#discussion_r2563492581


##########
lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceTableOperations.java:
##########
@@ -192,6 +195,45 @@ public Response deregisterTable(
     }
   }
 
+  @POST
+  @Path("/exists")
+  @Timed(name = "table-exists." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
+  @ResponseMetered(name = "table-exists", absolute = true)
+  public Response tableExists(
+      @PathParam("id") String tableId,
+      @QueryParam("delimiter") @DefaultValue("$") String delimiter,
+      @Context HttpHeaders headers,
+      TableExistsRequest tableExistsRequest) {
+    try {
+      // True if exists, false if not found and, otherwise throw exception
+      boolean exists = lanceNamespace.asTableOps().tableExists(tableId, 
delimiter);
+      if (exists) {
+        return Response.status(Response.Status.OK).build();
+      }
+      return Response.status(Response.Status.NOT_FOUND).build();
+    } catch (Exception e) {
+      return LanceExceptionMapper.toRESTResponse(tableId, e);
+    }
+  }
+
+  @POST
+  @Path("/drop")
+  @Timed(name = "drop-table." + MetricNames.HTTP_PROCESS_DURATION, absolute = 
true)
+  @ResponseMetered(name = "drop-table", absolute = true)
+  public Response dropTable(
+      @PathParam("id") String tableId,
+      @QueryParam("delimiter") @DefaultValue("$") String delimiter,
+      @Context HttpHeaders headers,
+      DropTableRequest dropTableRequest) {
+    try {
+

Review Comment:
   There's an extra blank line after the `try {` statement. This should be 
removed for consistency with the codebase style seen in other methods.
   ```suggestion
   
   ```



##########
lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceTableOperations.java:
##########
@@ -266,6 +267,56 @@ public DeregisterTableResponse deregisterTable(String 
tableId, String delimiter)
     return response;
   }
 
+  @Override
+  public boolean tableExists(String tableId, String delimiter) {
+    ObjectIdentifier nsId = ObjectIdentifier.of(tableId, 
Pattern.quote(delimiter));
+    Preconditions.checkArgument(
+        nsId.levels() == 3, "Expected at 3-level namespace but got: %s", 
nsId.levels());
+
+    String catalogName = nsId.levelAtListPos(0);
+    Catalog catalog = 
namespaceWrapper.loadAndValidateLakehouseCatalog(catalogName);
+
+    NameIdentifier tableIdentifier =
+        NameIdentifier.of(nsId.levelAtListPos(1), nsId.levelAtListPos(2));
+
+    return catalog.asTableCatalog().tableExists(tableIdentifier);

Review Comment:
   The `tableExists` implementation has an inconsistency with how the REST 
endpoint and integration tests expect it to behave. The method returns a 
boolean (true/false), but:
   
   1. The REST endpoint (line 209-213 in LanceTableOperations.java) converts 
`false` to a 404 NOT_FOUND response
   2. The integration test (line 716-720 in LanceRESTServiceIT.java) expects a 
`LanceNamespaceException` to be thrown for non-existing tables
   
   The implementation should throw a `LanceNamespaceException` when the table 
doesn't exist (similar to `namespaceExists` pattern), rather than returning 
false. For example:
   
   ```java
   boolean exists = catalog.asTableCatalog().tableExists(tableIdentifier);
   if (!exists) {
     throw LanceNamespaceException.notFound(
         "Table not found: " + tableId,
         NoSuchTableException.class.getSimpleName(),
         tableId,
         CommonUtil.formatCurrentStackTrace());
   }
   ```
   ```suggestion
       boolean exists = catalog.asTableCatalog().tableExists(tableIdentifier);
       if (!exists) {
         throw LanceNamespaceException.notFound(
             "Table not found: " + tableId,
             NoSuchTableException.class.getSimpleName(),
             tableId,
             CommonUtil.formatCurrentStackTrace());
       }
       return true;
   ```



##########
lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/LanceTableOperations.java:
##########
@@ -94,4 +95,22 @@ RegisterTableResponse registerTable(
    * @return the response of the deregister table operation
    */
   DeregisterTableResponse deregisterTable(String tableId, String delimiter);
+
+  /**
+   * Check if a table exists.
+   *
+   * @param tableId table ids are in the format of 
"{namespace}{delimiter}{table_name}"
+   * @param delimiter the delimiter used in the namespace
+   * @return true if the table exists, false otherwise
+   */
+  boolean tableExists(String tableId, String delimiter);

Review Comment:
   The return type `boolean` is inconsistent with the expected behavior. Based 
on the integration test expectations (line 716-720 in LanceRESTServiceIT.java) 
and the pattern used by `namespaceExists`, this method should return `void` and 
throw a `LanceNamespaceException` when the table doesn't exist, rather than 
returning a boolean value.



##########
lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceTableOperations.java:
##########
@@ -192,6 +195,45 @@ public Response deregisterTable(
     }
   }
 
+  @POST
+  @Path("/exists")
+  @Timed(name = "table-exists." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
+  @ResponseMetered(name = "table-exists", absolute = true)
+  public Response tableExists(
+      @PathParam("id") String tableId,
+      @QueryParam("delimiter") @DefaultValue("$") String delimiter,
+      @Context HttpHeaders headers,
+      TableExistsRequest tableExistsRequest) {
+    try {
+      // True if exists, false if not found and, otherwise throw exception

Review Comment:
   The comment contains a grammatical error. Change "false if not found and, 
otherwise throw exception" to "false if not found, otherwise throws exception".
   ```suggestion
         // True if exists, false if not found, otherwise throws exception
   ```



##########
lance/lance-common/src/main/java/org/apache/gravitino/lance/common/ops/gravitino/GravitinoLanceTableOperations.java:
##########
@@ -266,6 +267,56 @@ public DeregisterTableResponse deregisterTable(String 
tableId, String delimiter)
     return response;
   }
 
+  @Override
+  public boolean tableExists(String tableId, String delimiter) {
+    ObjectIdentifier nsId = ObjectIdentifier.of(tableId, 
Pattern.quote(delimiter));
+    Preconditions.checkArgument(
+        nsId.levels() == 3, "Expected at 3-level namespace but got: %s", 
nsId.levels());
+
+    String catalogName = nsId.levelAtListPos(0);
+    Catalog catalog = 
namespaceWrapper.loadAndValidateLakehouseCatalog(catalogName);
+
+    NameIdentifier tableIdentifier =
+        NameIdentifier.of(nsId.levelAtListPos(1), nsId.levelAtListPos(2));
+
+    return catalog.asTableCatalog().tableExists(tableIdentifier);
+  }
+
+  @Override
+  public DropTableResponse dropTable(String tableId, String delimiter) {
+    ObjectIdentifier nsId = ObjectIdentifier.of(tableId, 
Pattern.quote(delimiter));
+    Preconditions.checkArgument(
+        nsId.levels() == 3, "Expected at 3-level namespace but got: %s", 
nsId.levels());
+
+    String catalogName = nsId.levelAtListPos(0);
+    Catalog catalog = 
namespaceWrapper.loadAndValidateLakehouseCatalog(catalogName);
+
+    NameIdentifier tableIdentifier =
+        NameIdentifier.of(nsId.levelAtListPos(1), nsId.levelAtListPos(2));
+
+    Table table;
+    try {
+      table = catalog.asTableCatalog().loadTable(tableIdentifier);
+    } catch (NoSuchTableException e) {
+      throw LanceNamespaceException.notFound(
+          "Table not found: " + tableId,
+          NoSuchTableException.class.getSimpleName(),
+          tableId,
+          CommonUtil.formatCurrentStackTrace());
+    }
+
+    catalog.asTableCatalog().purgeTable(tableIdentifier);

Review Comment:
   The `purgeTable` method typically returns a boolean indicating success or 
failure, but the return value is not being checked. If `purgeTable` returns 
`false` instead of throwing an exception, the table won't be deleted but the 
method will still return a successful response.
   
   Consider checking the return value and throwing an appropriate exception if 
the operation fails:
   
   ```java
   boolean deleted = catalog.asTableCatalog().purgeTable(tableIdentifier);
   if (!deleted) {
     throw LanceNamespaceException.internalServerError(
         "Failed to delete table: " + tableId,
         "InternalServerError",
         tableId,
         CommonUtil.formatCurrentStackTrace());
   }
   ```
   ```suggestion
       boolean deleted = catalog.asTableCatalog().purgeTable(tableIdentifier);
       if (!deleted) {
         throw LanceNamespaceException.internalServerError(
             "Failed to delete table: " + tableId,
             "InternalServerError",
             tableId,
             CommonUtil.formatCurrentStackTrace());
       }
   ```



##########
lance/lance-rest-server/src/test/java/org/apache/gravitino/lance/service/rest/TestLanceNamespaceOperations.java:
##########
@@ -665,4 +668,108 @@ void testDescribeTable() {
     Assertions.assertEquals("Runtime exception", errorResp.getError());
     Assertions.assertEquals(RuntimeException.class.getSimpleName(), 
errorResp.getType());
   }
+
+  @Test
+  void testTableExists() {
+    String tableIds = "catalog.scheme.table_exists";
+    String delimiter = ".";
+
+    doReturn(true).when(tableOps).tableExists(any(), any());
+
+    Response resp =
+        target(String.format("/v1/table/%s/exists", tableIds))
+            .queryParam("delimiter", delimiter)
+            .request(MediaType.APPLICATION_JSON_TYPE)
+            .post(null);
+
+    Assertions.assertEquals(Response.Status.OK.getStatusCode(), 
resp.getStatus());
+
+    // test throw exception
+    doThrow(
+            LanceNamespaceException.notFound(
+                "Table not found", "NoSuchTableException", tableIds, ""))
+        .when(tableOps)
+        .tableExists(any(), any());
+    resp =
+        target(String.format("/v1/table/%s/exists", tableIds))
+            .queryParam("delimiter", delimiter)
+            .request(MediaType.APPLICATION_JSON_TYPE)
+            .post(null);
+
+    Assertions.assertEquals(Response.Status.NOT_FOUND.getStatusCode(), 
resp.getStatus());
+    Assertions.assertEquals(MediaType.APPLICATION_JSON_TYPE, 
resp.getMediaType());
+
+    ErrorResponse errorResp = resp.readEntity(ErrorResponse.class);
+    Assertions.assertEquals(404, errorResp.getCode());
+    Assertions.assertEquals("Table not found", errorResp.getError());
+    Assertions.assertEquals("NoSuchTableException", errorResp.getType());
+
+    // Test runtime exception
+    Mockito.reset(tableOps);
+    doThrow(new RuntimeException("Runtime 
exception")).when(tableOps).tableExists(any(), any());
+    resp =
+        target(String.format("/v1/table/%s/exists", tableIds))
+            .queryParam("delimiter", delimiter)
+            .request(MediaType.APPLICATION_JSON_TYPE)
+            .post(null);
+    Assertions.assertEquals(
+        Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), 
resp.getStatus());
+    Assertions.assertEquals(MediaType.APPLICATION_JSON_TYPE, 
resp.getMediaType());
+  }

Review Comment:
   The unit test is missing coverage for the case where `tableExists` returns 
`false`. The test only covers:
   1. When it returns `true` (line 677)
   2. When it throws a `LanceNamespaceException` (line 688-692)  
   3. When it throws a `RuntimeException` (line 709)
   
   But the REST endpoint has logic to handle `false` by returning 404 (lines 
212-213 in LanceTableOperations.java). This test case should be added for 
completeness, or the false-handling code should be removed if the method is 
expected to throw exceptions instead of returning false.



##########
lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/service/rest/LanceTableOperations.java:
##########
@@ -192,6 +195,45 @@ public Response deregisterTable(
     }
   }
 
+  @POST
+  @Path("/exists")
+  @Timed(name = "table-exists." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
+  @ResponseMetered(name = "table-exists", absolute = true)
+  public Response tableExists(
+      @PathParam("id") String tableId,
+      @QueryParam("delimiter") @DefaultValue("$") String delimiter,
+      @Context HttpHeaders headers,
+      TableExistsRequest tableExistsRequest) {
+    try {
+      // True if exists, false if not found and, otherwise throw exception
+      boolean exists = lanceNamespace.asTableOps().tableExists(tableId, 
delimiter);
+      if (exists) {
+        return Response.status(Response.Status.OK).build();
+      }
+      return Response.status(Response.Status.NOT_FOUND).build();
+    } catch (Exception e) {
+      return LanceExceptionMapper.toRESTResponse(tableId, e);
+    }
+  }
+
+  @POST
+  @Path("/drop")
+  @Timed(name = "drop-table." + MetricNames.HTTP_PROCESS_DURATION, absolute = 
true)
+  @ResponseMetered(name = "drop-table", absolute = true)
+  public Response dropTable(
+      @PathParam("id") String tableId,
+      @QueryParam("delimiter") @DefaultValue("$") String delimiter,
+      @Context HttpHeaders headers,
+      DropTableRequest dropTableRequest) {

Review Comment:
   The `dropTable` method accepts a `DropTableRequest` parameter but never uses 
it. The table ID is taken from the path parameter instead. Consider either:
   1. Removing the unused parameter if it's not needed by the API contract
   2. Adding a validation method similar to `validateDeregisterTableRequest` to 
acknowledge the parameter is intentionally ignored
   
   This is inconsistent with the pattern used in other endpoints like 
`deregisterTable` which calls `validateDeregisterTableRequest`.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to