diff --git a/.github/workflows/access-control-integration-test.yml b/.github/workflows/access-control-integration-test.yml index dc8acd60678..fc2daf2159d 100644 --- a/.github/workflows/access-control-integration-test.yml +++ b/.github/workflows/access-control-integration-test.yml @@ -92,7 +92,7 @@ jobs: ./gradlew -PtestMode=deploy -PjdbcBackend=postgresql -PjdkVersion=${{ matrix.java-version }} -PskipDockerTests=false :authorizations:test - name: Upload integrate tests reports - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ (failure() && steps.integrationTest.outcome == 'failure') || contains(github.event.pull_request.labels.*.name, 'upload log') }} with: name: authorizations-integrate-test-reports-${{ matrix.java-version }} diff --git a/.github/workflows/backend-integration-test-action.yml b/.github/workflows/backend-integration-test-action.yml index b15c5d226ca..2932d56cb39 100644 --- a/.github/workflows/backend-integration-test-action.yml +++ b/.github/workflows/backend-integration-test-action.yml @@ -64,7 +64,7 @@ jobs: -x :authorizations:authorization-chain:test -x :authorizations:authorization-ranger:test - name: Upload integrate tests reports - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ (failure() && steps.integrationTest.outcome == 'failure') || contains(github.event.pull_request.labels.*.name, 'upload log') }} with: name: integrate-test-reports-${{ inputs.java-version }}-${{ inputs.test-mode }}-${{ inputs.backend }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e60f77ba67..c1a27c5f71f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -90,7 +90,7 @@ jobs: ./gradlew :spark-connector:spark-3.5:build -PscalaVersion=2.13 -PskipITs -PskipDockerTests=false - name: Upload unit tests report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: unit test report @@ -129,7 +129,7 @@ jobs: run: ./gradlew build -PskipITs -PjdkVersion=${{ matrix.java-version }} -PskipDockerTests=false -x :clients:client-python:build - name: Upload unit tests report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: unit test report diff --git a/.github/workflows/cron-integration-test.yml b/.github/workflows/cron-integration-test.yml index db805fe6da9..51797c436b3 100644 --- a/.github/workflows/cron-integration-test.yml +++ b/.github/workflows/cron-integration-test.yml @@ -87,7 +87,7 @@ jobs: ./gradlew test -PskipTests -PtestMode=${{ matrix.test-mode }} -PjdkVersion=${{ matrix.java-version }} -PskipDockerTests=false - name: Upload integrate tests reports - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ failure() && steps.integrationTest.outcome == 'failure' }} with: name: integrate test reports diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index e5f1f699de1..337504170ce 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -32,6 +32,11 @@ on: description: 'Publish Docker token' required: true type: string + publish-latest-tag: + description: 'Whether to update the latest tag. This operation is only applicable to official releases and should not be used for Release Candidate (RC).' + required: false + type: boolean + default: false jobs: publish-docker-image: @@ -83,6 +88,12 @@ jobs: echo "image_type=iceberg-rest-server" >> $GITHUB_ENV echo "image_name=apache/gravitino-iceberg-rest" >> $GITHUB_ENV fi + + if [ "${{ github.event.inputs.publish-latest-tag }}" == "true" ]; then + echo "publish_latest=true" >> $GITHUB_ENV + else + echo "publish_latest=false" >> $GITHUB_ENV + fi - name: Check publish Docker token run: | @@ -115,8 +126,16 @@ jobs: sudo rm -rf /usr/local/lib/android sudo rm -rf /opt/hostedtoolcache/CodeQL - if [[ "${image_type}" == "gravitino" || "${image_type}" == "iceberg-rest-server" ]]; then - ./dev/docker/build-docker.sh --platform all --type ${image_type} --image ${image_name} --tag ${{ github.event.inputs.version }} --latest + if [[ -n "${tag_name}" ]]; then + full_tag_name="${tag_name}-${{ github.event.inputs.version }}" + else + full_tag_name="${{ github.event.inputs.version }}" + fi + + if [[ "${publish_latest}" == "true" ]]; then + echo "Publish tag ${full_tag_name}, and update latest too." + ./dev/docker/build-docker.sh --platform all --type ${image_type} --image ${image_name} --tag ${full_tag_name} --latest else - ./dev/docker/build-docker.sh --platform all --type ${image_type} --image ${image_name} --tag "${tag_name}-${{ github.event.inputs.version }}" - fi \ No newline at end of file + echo "Publish tag ${full_tag_name}." + ./dev/docker/build-docker.sh --platform all --type ${image_type} --image ${image_name} --tag ${full_tag_name} + fi diff --git a/.github/workflows/flink-integration-test-action.yml b/.github/workflows/flink-integration-test-action.yml index f22308c1951..d91de4064e3 100644 --- a/.github/workflows/flink-integration-test-action.yml +++ b/.github/workflows/flink-integration-test-action.yml @@ -52,7 +52,7 @@ jobs: ./gradlew -PskipTests -PtestMode=deploy -PjdkVersion=${{ inputs.java-version }} -PskipDockerTests=false :flink-connector:flink:test --tests "org.apache.gravitino.flink.connector.integration.test.**" - name: Upload integrate tests reports - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ (failure() && steps.integrationTest.outcome == 'failure') || contains(github.event.pull_request.labels.*.name, 'upload log') }} with: name: flink-connector-integrate-test-reports-${{ inputs.java-version }} diff --git a/.github/workflows/frontend-integration-test.yml b/.github/workflows/frontend-integration-test.yml index 2dd2bee6474..046656fd9b1 100644 --- a/.github/workflows/frontend-integration-test.yml +++ b/.github/workflows/frontend-integration-test.yml @@ -88,7 +88,7 @@ jobs: ./gradlew -PskipTests -PtestMode=deploy -PjdkVersion=${{ matrix.java-version }} -PskipDockerTests=false :web:integration-test:test - name: Upload integrate tests reports - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ (failure() && steps.integrationTest.outcome == 'failure') || contains(github.event.pull_request.labels.*.name, 'upload log') }} with: name: integrate-test-reports-${{ matrix.java-version }} diff --git a/.github/workflows/gvfs-fuse-build-test.yml b/.github/workflows/gvfs-fuse-build-test.yml index 4fe7b66e09d..f1e97e5e88d 100644 --- a/.github/workflows/gvfs-fuse-build-test.yml +++ b/.github/workflows/gvfs-fuse-build-test.yml @@ -88,7 +88,7 @@ jobs: dev/ci/util_free_space.sh - name: Upload tests reports - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ (failure() && steps.integrationTest.outcome == 'failure') || contains(github.event.pull_request.labels.*.name, 'upload log') }} with: name: Gvfs-fuse integrate-test-reports-${{ matrix.java-version }} diff --git a/.github/workflows/python-integration-test.yml b/.github/workflows/python-integration-test.yml index 546aa928584..5d997bba08d 100644 --- a/.github/workflows/python-integration-test.yml +++ b/.github/workflows/python-integration-test.yml @@ -82,7 +82,7 @@ jobs: done - name: Upload integrate tests reports - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ failure() && steps.integrationTest.outcome == 'failure' }} with: name: integrate test reports diff --git a/.github/workflows/spark-integration-test-action.yml b/.github/workflows/spark-integration-test-action.yml index 873877bc29c..30c7ddd8eb3 100644 --- a/.github/workflows/spark-integration-test-action.yml +++ b/.github/workflows/spark-integration-test-action.yml @@ -63,7 +63,7 @@ jobs: ./gradlew -PskipTests -PtestMode=${{ inputs.test-mode }} -PjdkVersion=${{ inputs.java-version }} -PscalaVersion=${{ inputs.scala-version }} -PskipDockerTests=false :spark-connector:spark-3.5:test --tests "org.apache.gravitino.spark.connector.integration.test.**" - name: Upload integrate tests reports - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ (failure() && steps.integrationTest.outcome == 'failure') || contains(github.event.pull_request.labels.*.name, 'upload log') }} with: name: spark-connector-integrate-test-reports-${{ inputs.java-version }}-${{ inputs.test-mode }} diff --git a/.github/workflows/trino-integration-test.yml b/.github/workflows/trino-integration-test.yml index fb881a732af..cc383622831 100644 --- a/.github/workflows/trino-integration-test.yml +++ b/.github/workflows/trino-integration-test.yml @@ -90,7 +90,7 @@ jobs: trino-connector/integration-test/trino-test-tools/run_test.sh - name: Upload integrate tests reports - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ (failure() && steps.integrationTest.outcome == 'failure') || contains(github.event.pull_request.labels.*.name, 'upload log') }} with: name: trino-connector-integrate-test-reports-${{ matrix.java-version }} diff --git a/.gitignore b/.gitignore index eae3d3c952c..eb0adf56118 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ replay_pid* **/.gradle **/.idea !/.idea/icon.svg +!.idea/vcs.xml **/build gen **/.DS_Store diff --git a/authorizations/authorization-common/src/main/java/org/apache/gravitino/authorization/common/PathBasedMetadataObject.java b/authorizations/authorization-common/src/main/java/org/apache/gravitino/authorization/common/PathBasedMetadataObject.java index 9720b813fa5..a1963f64539 100644 --- a/authorizations/authorization-common/src/main/java/org/apache/gravitino/authorization/common/PathBasedMetadataObject.java +++ b/authorizations/authorization-common/src/main/java/org/apache/gravitino/authorization/common/PathBasedMetadataObject.java @@ -122,8 +122,13 @@ public int hashCode() { @Override public String toString() { - return "MetadataObject: [fullName=" + fullName() + "], [path=" + path == null - ? "null" - : path + "], [type=" + type + "]"; + String strPath = path == null ? "null" : path; + return "MetadataObject: [fullName=" + + fullName() + + "], [path=" + + strPath + + "], [type=" + + type + + "]"; } } diff --git a/authorizations/authorization-common/src/main/java/org/apache/gravitino/authorization/jdbc/JdbcAuthorizationPlugin.java b/authorizations/authorization-common/src/main/java/org/apache/gravitino/authorization/jdbc/JdbcAuthorizationPlugin.java index d0a1b0897ec..2d74bd050e3 100644 --- a/authorizations/authorization-common/src/main/java/org/apache/gravitino/authorization/jdbc/JdbcAuthorizationPlugin.java +++ b/authorizations/authorization-common/src/main/java/org/apache/gravitino/authorization/jdbc/JdbcAuthorizationPlugin.java @@ -110,8 +110,13 @@ public Boolean onMetadataUpdated(MetadataObjectChange... changes) throws Runtime @Override public Boolean onRoleCreated(Role role) throws AuthorizationPluginException { List sqls = getCreateRoleSQL(role.name()); + boolean createdNewly = false; for (String sql : sqls) { - executeUpdateSQL(sql, "already exists"); + createdNewly = executeUpdateSQL(sql, "already exists"); + } + + if (!createdNewly) { + return true; } if (role.securableObjects() != null) { @@ -140,7 +145,6 @@ public Boolean onRoleDeleted(Role role) throws AuthorizationPluginException { @Override public Boolean onRoleUpdated(Role role, RoleChange... changes) throws AuthorizationPluginException { - onRoleCreated(role); for (RoleChange change : changes) { if (change instanceof RoleChange.AddSecurableObject) { SecurableObject object = ((RoleChange.AddSecurableObject) change).getSecurableObject(); @@ -381,14 +385,15 @@ protected AuthorizationPluginException toAuthorizationPluginException(SQLExcepti "JDBC authorization plugin fail to execute SQL, error code: %d", se.getErrorCode()); } - public void executeUpdateSQL(String sql, String ignoreErrorMsg) { + public boolean executeUpdateSQL(String sql, String ignoreErrorMsg) { try (final Connection connection = getConnection()) { try (final Statement statement = connection.createStatement()) { statement.executeUpdate(sql); + return true; } } catch (SQLException se) { if (ignoreErrorMsg != null && se.getMessage().contains(ignoreErrorMsg)) { - return; + return false; } LOG.error("JDBC authorization plugin exception: ", se); throw toAuthorizationPluginException(se); diff --git a/authorizations/authorization-common/src/test/java/org/apache/gravitino/authorization/common/TestPathBasedMetadataObject.java b/authorizations/authorization-common/src/test/java/org/apache/gravitino/authorization/common/TestPathBasedMetadataObject.java index 3f604b5f389..d2a2291a356 100644 --- a/authorizations/authorization-common/src/test/java/org/apache/gravitino/authorization/common/TestPathBasedMetadataObject.java +++ b/authorizations/authorization-common/src/test/java/org/apache/gravitino/authorization/common/TestPathBasedMetadataObject.java @@ -47,4 +47,31 @@ public void PathBasedMetadataObjectNotEquals() { Assertions.assertNotEquals(pathBasedMetadataObject1, pathBasedMetadataObject2); } + + @Test + void testToString() { + PathBasedMetadataObject pathBasedMetadataObject1 = + new PathBasedMetadataObject("parent", "name", "path", PathBasedMetadataObject.Type.PATH); + Assertions.assertEquals( + "MetadataObject: [fullName=parent.name], [path=path], [type=PATH]", + pathBasedMetadataObject1.toString()); + + PathBasedMetadataObject pathBasedMetadataObject2 = + new PathBasedMetadataObject("parent", "name", null, PathBasedMetadataObject.Type.PATH); + Assertions.assertEquals( + "MetadataObject: [fullName=parent.name], [path=null], [type=PATH]", + pathBasedMetadataObject2.toString()); + + PathBasedMetadataObject pathBasedMetadataObject3 = + new PathBasedMetadataObject(null, "name", null, PathBasedMetadataObject.Type.PATH); + Assertions.assertEquals( + "MetadataObject: [fullName=name], [path=null], [type=PATH]", + pathBasedMetadataObject3.toString()); + + PathBasedMetadataObject pathBasedMetadataObject4 = + new PathBasedMetadataObject(null, "name", "path", PathBasedMetadataObject.Type.PATH); + Assertions.assertEquals( + "MetadataObject: [fullName=name], [path=path], [type=PATH]", + pathBasedMetadataObject4.toString()); + } } diff --git a/authorizations/authorization-common/src/test/java/org/apache/gravitino/authorization/jdbc/TestJdbcAuthorizationPlugin.java b/authorizations/authorization-common/src/test/java/org/apache/gravitino/authorization/jdbc/TestJdbcAuthorizationPlugin.java index 6f9f7e29c37..8d3788617cd 100644 --- a/authorizations/authorization-common/src/test/java/org/apache/gravitino/authorization/jdbc/TestJdbcAuthorizationPlugin.java +++ b/authorizations/authorization-common/src/test/java/org/apache/gravitino/authorization/jdbc/TestJdbcAuthorizationPlugin.java @@ -74,9 +74,10 @@ public List getSetOwnerSQL( return Collections.emptyList(); } - public void executeUpdateSQL(String sql, String ignoreErrorMsg) { + public boolean executeUpdateSQL(String sql, String ignoreErrorMsg) { Assertions.assertEquals(expectSQLs.get(currentSQLIndex), sql); currentSQLIndex++; + return true; } }; @@ -148,23 +149,21 @@ public void testPermissionManagement() { // Test metalake object and different role change resetSQLIndex(); - expectSQLs = Lists.newArrayList("CREATE ROLE tmp", "GRANT SELECT ON TABLE *.* TO ROLE tmp"); + expectSQLs = Lists.newArrayList("GRANT SELECT ON TABLE *.* TO ROLE tmp"); SecurableObject metalakeObject = SecurableObjects.ofMetalake("metalake", Lists.newArrayList(Privileges.SelectTable.allow())); RoleChange roleChange = RoleChange.addSecurableObject("tmp", metalakeObject); plugin.onRoleUpdated(role, roleChange); resetSQLIndex(); - expectSQLs = Lists.newArrayList("CREATE ROLE tmp", "REVOKE SELECT ON TABLE *.* FROM ROLE tmp"); + expectSQLs = Lists.newArrayList("REVOKE SELECT ON TABLE *.* FROM ROLE tmp"); roleChange = RoleChange.removeSecurableObject("tmp", metalakeObject); plugin.onRoleUpdated(role, roleChange); resetSQLIndex(); expectSQLs = Lists.newArrayList( - "CREATE ROLE tmp", - "REVOKE SELECT ON TABLE *.* FROM ROLE tmp", - "GRANT CREATE ON TABLE *.* TO ROLE tmp"); + "REVOKE SELECT ON TABLE *.* FROM ROLE tmp", "GRANT CREATE ON TABLE *.* TO ROLE tmp"); SecurableObject newMetalakeObject = SecurableObjects.ofMetalake("metalake", Lists.newArrayList(Privileges.CreateTable.allow())); roleChange = RoleChange.updateSecurableObject("tmp", metalakeObject, newMetalakeObject); @@ -175,7 +174,7 @@ public void testPermissionManagement() { SecurableObject catalogObject = SecurableObjects.ofCatalog("catalog", Lists.newArrayList(Privileges.SelectTable.allow())); roleChange = RoleChange.addSecurableObject("tmp", catalogObject); - expectSQLs = Lists.newArrayList("CREATE ROLE tmp", "GRANT SELECT ON TABLE *.* TO ROLE tmp"); + expectSQLs = Lists.newArrayList("GRANT SELECT ON TABLE *.* TO ROLE tmp"); plugin.onRoleUpdated(role, roleChange); // Test schema object @@ -184,8 +183,7 @@ public void testPermissionManagement() { SecurableObjects.ofSchema( catalogObject, "schema", Lists.newArrayList(Privileges.SelectTable.allow())); roleChange = RoleChange.addSecurableObject("tmp", schemaObject); - expectSQLs = - Lists.newArrayList("CREATE ROLE tmp", "GRANT SELECT ON TABLE schema.* TO ROLE tmp"); + expectSQLs = Lists.newArrayList("GRANT SELECT ON TABLE schema.* TO ROLE tmp"); plugin.onRoleUpdated(role, roleChange); // Test table object @@ -194,8 +192,18 @@ public void testPermissionManagement() { SecurableObjects.ofTable( schemaObject, "table", Lists.newArrayList(Privileges.SelectTable.allow())); roleChange = RoleChange.addSecurableObject("tmp", tableObject); - expectSQLs = - Lists.newArrayList("CREATE ROLE tmp", "GRANT SELECT ON TABLE schema.table TO ROLE tmp"); + expectSQLs = Lists.newArrayList("GRANT SELECT ON TABLE schema.table TO ROLE tmp"); + plugin.onRoleUpdated(role, roleChange); + + // Test the role with objects + resetSQLIndex(); + role = + RoleEntity.builder() + .withId(-1L) + .withName("tmp") + .withSecurableObjects(Lists.newArrayList(tableObject)) + .withAuditInfo(AuditInfo.EMPTY) + .build(); plugin.onRoleUpdated(role, roleChange); } diff --git a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerClientExtension.java b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerClientExtension.java index a554559ea5c..e1e9f6955d2 100644 --- a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerClientExtension.java +++ b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerClientExtension.java @@ -100,12 +100,14 @@ public RangerClientExtension(String hostName, String authType, String username, } } + @Override public RangerPolicy createPolicy(RangerPolicy policy) throws RangerServiceException { Preconditions.checkArgument( policy.getResources().size() > 0, "Ranger policy resources can not be empty!"); return super.createPolicy(policy); } + @Override public RangerPolicy updatePolicy(long policyId, RangerPolicy policy) throws RangerServiceException { Preconditions.checkArgument( diff --git a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/reference/VXGroup.java b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/reference/VXGroup.java index 3a58f5c95a0..611127ec3f2 100644 --- a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/reference/VXGroup.java +++ b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/reference/VXGroup.java @@ -60,6 +60,7 @@ public VXGroup() { * * @return formatedStr */ + @Override public String toString() { String str = "VXGroup={"; str += super.toString(); diff --git a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/reference/VXUser.java b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/reference/VXUser.java index f605d987de0..3dbc2b0236b 100644 --- a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/reference/VXUser.java +++ b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/reference/VXUser.java @@ -75,6 +75,7 @@ public String getName() { * * @return formatedStr */ + @Override public String toString() { String str = "VXUser={"; str += super.toString(); diff --git a/catalogs/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/operation/OceanBaseTableOperations.java b/catalogs/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/operation/OceanBaseTableOperations.java index 77c97290927..98f2d174f1a 100644 --- a/catalogs/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/operation/OceanBaseTableOperations.java +++ b/catalogs/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/operation/OceanBaseTableOperations.java @@ -185,6 +185,7 @@ protected Map getTableProperties(Connection connection, String t } } + @Override protected void correctJdbcTableFields( Connection connection, String databaseName, String tableName, JdbcTable.Builder tableBuilder) throws SQLException { diff --git a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java index 7b27438d2e5..aef42044c0c 100644 --- a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java +++ b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java @@ -58,6 +58,7 @@ import org.apache.gravitino.rel.TableCatalog; import org.apache.gravitino.rel.TableChange; import org.apache.gravitino.rel.expressions.distributions.Distribution; +import org.apache.gravitino.rel.expressions.distributions.Distributions; import org.apache.gravitino.rel.expressions.sorts.SortOrder; import org.apache.gravitino.rel.expressions.transforms.Transform; import org.apache.gravitino.rel.indexes.Index; @@ -513,6 +514,13 @@ public Table createTable( .build()) .toArray(IcebergColumn[]::new); + // Gravitino NONE distribution means the client side doesn't specify distribution, which is + // not the same as none distribution in Iceberg. + if (Distributions.NONE.equals(distribution)) { + distribution = + getIcebergDefaultDistribution(sortOrders.length > 0, partitioning.length > 0); + } + IcebergTable createdTable = IcebergTable.builder() .withName(tableIdent.name()) @@ -588,6 +596,16 @@ public void testConnection( } } + private static Distribution getIcebergDefaultDistribution( + boolean isSorted, boolean isPartitioned) { + if (isSorted) { + return Distributions.RANGE; + } else if (isPartitioned) { + return Distributions.HASH; + } + return Distributions.NONE; + } + private static String currentUser() { return PrincipalUtils.getCurrentUserName(); } diff --git a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergTable.java b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergTable.java index 27e3c429e7c..3f2f54c1b3e 100644 --- a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergTable.java +++ b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergTable.java @@ -152,21 +152,6 @@ public static IcebergTable fromIcebergTable(TableMetadata table, String tableNam Schema schema = table.schema(); Transform[] partitionSpec = FromIcebergPartitionSpec.fromPartitionSpec(table.spec(), schema); SortOrder[] sortOrder = FromIcebergSortOrder.fromSortOrder(table.sortOrder()); - Distribution distribution = Distributions.NONE; - String distributionName = properties.get(IcebergTablePropertiesMetadata.DISTRIBUTION_MODE); - if (null != distributionName) { - switch (DistributionMode.fromName(distributionName)) { - case HASH: - distribution = Distributions.HASH; - break; - case RANGE: - distribution = Distributions.RANGE; - break; - default: - // do nothing - break; - } - } IcebergColumn[] icebergColumns = schema.columns().stream().map(ConvertUtil::fromNestedField).toArray(IcebergColumn[]::new); return IcebergTable.builder() @@ -178,7 +163,7 @@ public static IcebergTable fromIcebergTable(TableMetadata table, String tableNam .withAuditInfo(AuditInfo.EMPTY) .withPartitioning(partitionSpec) .withSortOrders(sortOrder) - .withDistribution(distribution) + .withDistribution(getDistribution(properties)) .build(); } @@ -236,4 +221,23 @@ protected IcebergTable internalBuild() { public static Builder builder() { return new Builder(); } + + private static Distribution getDistribution(Map properties) { + Distribution distribution = Distributions.NONE; + String distributionName = properties.get(IcebergTablePropertiesMetadata.DISTRIBUTION_MODE); + if (null != distributionName) { + switch (DistributionMode.fromName(distributionName)) { + case HASH: + distribution = Distributions.HASH; + break; + case RANGE: + distribution = Distributions.RANGE; + break; + default: + // do nothing + break; + } + } + return distribution; + } } diff --git a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/CatalogIcebergBaseIT.java b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/CatalogIcebergBaseIT.java index fd37441b459..f0162a6ec88 100644 --- a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/CatalogIcebergBaseIT.java +++ b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/CatalogIcebergBaseIT.java @@ -379,6 +379,76 @@ void testCreateTableWithNullComment() { Assertions.assertNull(loadTable.comment()); } + @Test + void testCreateTableWithNoneDistribution() { + // Create table from Gravitino API + Column[] columns = createColumns(); + + NameIdentifier tableIdentifier = NameIdentifier.of(schemaName, tableName); + Distribution distribution = Distributions.NONE; + + final SortOrder[] sortOrders = + new SortOrder[] { + SortOrders.of( + NamedReference.field(ICEBERG_COL_NAME2), + SortDirection.DESCENDING, + NullOrdering.NULLS_FIRST) + }; + + Transform[] partitioning = new Transform[] {Transforms.day(columns[1].name())}; + Map properties = createProperties(); + TableCatalog tableCatalog = catalog.asTableCatalog(); + Table tableWithPartitionAndSortorder = + tableCatalog.createTable( + tableIdentifier, + columns, + table_comment, + properties, + partitioning, + distribution, + sortOrders); + Assertions.assertEquals(tableName, tableWithPartitionAndSortorder.name()); + Assertions.assertEquals(Distributions.RANGE, tableWithPartitionAndSortorder.distribution()); + + Table loadTable = tableCatalog.loadTable(tableIdentifier); + Assertions.assertEquals(tableName, loadTable.name()); + Assertions.assertEquals(Distributions.RANGE, loadTable.distribution()); + tableCatalog.dropTable(tableIdentifier); + + Table tableWithPartition = + tableCatalog.createTable( + tableIdentifier, + columns, + table_comment, + properties, + partitioning, + distribution, + new SortOrder[0]); + Assertions.assertEquals(tableName, tableWithPartition.name()); + Assertions.assertEquals(Distributions.HASH, tableWithPartition.distribution()); + + loadTable = tableCatalog.loadTable(tableIdentifier); + Assertions.assertEquals(tableName, loadTable.name()); + Assertions.assertEquals(Distributions.HASH, loadTable.distribution()); + tableCatalog.dropTable(tableIdentifier); + + Table tableWithoutPartitionAndSortOrder = + tableCatalog.createTable( + tableIdentifier, + columns, + table_comment, + properties, + new Transform[0], + distribution, + new SortOrder[0]); + Assertions.assertEquals(tableName, tableWithoutPartitionAndSortOrder.name()); + Assertions.assertEquals(Distributions.NONE, tableWithoutPartitionAndSortOrder.distribution()); + + loadTable = tableCatalog.loadTable(tableIdentifier); + Assertions.assertEquals(tableName, loadTable.name()); + Assertions.assertEquals(Distributions.NONE, loadTable.distribution()); + } + @Test void testCreateAndLoadIcebergTable() { // Create table from Gravitino API @@ -968,9 +1038,9 @@ public void testTableDistribution() { columns, table_comment, properties, - partitioning, + new Transform[0], distribution, - sortOrders); + new SortOrder[0]); Table loadTable = tableCatalog.loadTable(tableIdentifier); @@ -981,8 +1051,8 @@ public void testTableDistribution() { Arrays.asList(columns), properties, distribution, - sortOrders, - partitioning, + new SortOrder[0], + new Transform[0], loadTable); Assertions.assertDoesNotThrow(() -> tableCatalog.dropTable(tableIdentifier)); @@ -1179,7 +1249,7 @@ public void testTableSortOrder() { Column[] columns = createColumns(); NameIdentifier tableIdentifier = NameIdentifier.of(schemaName, tableName); - Distribution distribution = Distributions.NONE; + Distribution distribution = Distributions.HASH; final SortOrder[] sortOrders = new SortOrder[] { diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/AreYouSure.java b/clients/cli/src/main/java/org/apache/gravitino/cli/AreYouSure.java index a0893dbad4f..a6d1450c1bf 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/AreYouSure.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/AreYouSure.java @@ -32,16 +32,20 @@ public class AreYouSure { * @return {@code true} if the action is to continue {@code false} otherwise. */ public static boolean really(boolean force) { - Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8.name()); - /* force option for scripting */ if (force) { return true; } - System.out.println( - "This command could result in data loss or other issues. Are you sure you want to do this? (Y/N)"); - String answer = scanner.next(); - return answer.equals("Y"); + try (Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8.name())) { + System.out.println( + "This command could result in data loss or other issues. Are you sure you want to do this? (Y/N)"); + String answer = scanner.next(); + return "Y".equals(answer); + } catch (Exception e) { + System.err.println("Error while reading user input: " + e.getMessage()); + Main.exit(-1); + } + return false; } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java b/clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java index 7a9481cb95b..f6de0213ed6 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java @@ -41,6 +41,20 @@ public FullName(CommandLine line) { this.line = line; } + /** + * Retrieves the level of the full name. + * + * @return The level of the full name, or -1 if line does not contain a {@code --name} option. + */ + public int getLevel() { + if (line.hasOption(GravitinoOptions.NAME)) { + String[] names = line.getOptionValue(GravitinoOptions.NAME).split("\\."); + return names.length; + } + + return -1; + } + /** * Retrieves the metalake name from the command line options, the GRAVITINO_METALAKE environment * variable or the Gravitino config file. diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java index aaeb8f0184f..47f9914233d 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java @@ -109,7 +109,7 @@ public Options options() { options.addOption(createArgOption(DEFAULT, "default column value")); options.addOption(createSimpleOption("o", OWNER, "display entity owner")); options.addOption(createArgOption(COLUMNFILE, "CSV file describing columns")); - options.addOption(createSimpleOption(null, ALL, "all operation for --enable")); + options.addOption(createSimpleOption(null, ALL, "on all entities")); // model options options.addOption(createArgOption(null, URI, "model version artifact")); diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/GroupCommandHandler.java b/clients/cli/src/main/java/org/apache/gravitino/cli/GroupCommandHandler.java index e336003e6b5..7542b17974b 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/GroupCommandHandler.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/GroupCommandHandler.java @@ -130,14 +130,23 @@ private void handleDeleteCommand() { /** Handles the "REVOKE" command. */ private void handleRevokeCommand() { - String[] revokeRoles = line.getOptionValues(GravitinoOptions.ROLE); - for (String role : revokeRoles) { + boolean revokeAll = line.hasOption(GravitinoOptions.ALL); + if (revokeAll) { gravitinoCommandLine - .newRemoveRoleFromGroup(url, ignore, metalake, group, role) + .newRemoveAllRoles(url, ignore, metalake, group, CommandEntities.GROUP) .validate() .handle(); + System.out.printf("Removed all roles from group %s%n", group); + } else { + String[] revokeRoles = line.getOptionValues(GravitinoOptions.ROLE); + for (String role : revokeRoles) { + gravitinoCommandLine + .newRemoveRoleFromGroup(url, ignore, metalake, group, role) + .validate() + .handle(); + } + System.out.printf("Removed roles %s from group %s%n", COMMA_JOINER.join(revokeRoles), group); } - System.out.printf("Remove roles %s from group %s%n", COMMA_JOINER.join(revokeRoles), group); } /** Handles the "GRANT" command. */ diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/RoleCommandHandler.java b/clients/cli/src/main/java/org/apache/gravitino/cli/RoleCommandHandler.java index cf48783b8dc..327ef21fdd8 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/RoleCommandHandler.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/RoleCommandHandler.java @@ -137,11 +137,19 @@ url, ignore, metalake, getOneRole(), new FullName(line), privileges) } private void handleRevokeCommand() { - gravitinoCommandLine - .newRevokePrivilegesFromRole( - url, ignore, metalake, getOneRole(), new FullName(line), privileges) - .validate() - .handle(); + boolean removeAll = line.hasOption(GravitinoOptions.ALL); + if (removeAll) { + gravitinoCommandLine + .newRevokeAllPrivileges(url, ignore, metalake, getOneRole(), new FullName(line)) + .validate() + .handle(); + } else { + gravitinoCommandLine + .newRevokePrivilegesFromRole( + url, ignore, metalake, getOneRole(), new FullName(line), privileges) + .validate() + .handle(); + } } private String getOneRole() { diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java b/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java index d9e3b9288ee..b229ef16aa3 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java @@ -86,6 +86,7 @@ import org.apache.gravitino.cli.commands.ModelDetails; import org.apache.gravitino.cli.commands.OwnerDetails; import org.apache.gravitino.cli.commands.RegisterModel; +import org.apache.gravitino.cli.commands.RemoveAllRoles; import org.apache.gravitino.cli.commands.RemoveAllTags; import org.apache.gravitino.cli.commands.RemoveCatalogProperty; import org.apache.gravitino.cli.commands.RemoveFilesetProperty; @@ -96,6 +97,7 @@ import org.apache.gravitino.cli.commands.RemoveTableProperty; import org.apache.gravitino.cli.commands.RemoveTagProperty; import org.apache.gravitino.cli.commands.RemoveTopicProperty; +import org.apache.gravitino.cli.commands.RevokeAllPrivileges; import org.apache.gravitino.cli.commands.RevokePrivilegesFromRole; import org.apache.gravitino.cli.commands.RoleAudit; import org.apache.gravitino.cli.commands.RoleDetails; @@ -457,6 +459,11 @@ protected RemoveRoleFromGroup newRemoveRoleFromGroup( return new RemoveRoleFromGroup(url, ignore, metalake, group, role); } + protected RemoveAllRoles newRemoveAllRoles( + String url, boolean ignore, String metalake, String entity, String entityType) { + return new RemoveAllRoles(url, ignore, metalake, entity, entityType); + } + protected AddRoleToGroup newAddRoleToGroup( String url, boolean ignore, String metalake, String group, String role) { return new AddRoleToGroup(url, ignore, metalake, group, role); @@ -895,6 +902,11 @@ protected RevokePrivilegesFromRole newRevokePrivilegesFromRole( return new RevokePrivilegesFromRole(url, ignore, metalake, role, entity, privileges); } + protected RevokeAllPrivileges newRevokeAllPrivileges( + String url, boolean ignore, String metalake, String role, FullName entity) { + return new RevokeAllPrivileges(url, ignore, metalake, role, entity); + } + protected MetalakeEnable newMetalakeEnable( String url, boolean ignore, String metalake, boolean enableAllCatalogs) { return new MetalakeEnable(url, ignore, metalake, enableAllCatalogs); diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/UserCommandHandler.java b/clients/cli/src/main/java/org/apache/gravitino/cli/UserCommandHandler.java index 9a8374ec342..23698aa5cf8 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/UserCommandHandler.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/UserCommandHandler.java @@ -150,14 +150,23 @@ private void handleDeleteCommand() { /** Handles the "REVOKE" command. */ private void handleRevokeCommand() { - String[] revokeRoles = line.getOptionValues(GravitinoOptions.ROLE); - for (String role : revokeRoles) { - this.gravitinoCommandLine - .newRemoveRoleFromUser(this.url, this.ignore, this.metalake, user, role) + boolean removeAll = line.hasOption(GravitinoOptions.ALL); + if (removeAll) { + gravitinoCommandLine + .newRemoveAllRoles(url, ignore, metalake, user, CommandEntities.USER) .validate() .handle(); + System.out.printf("Removed all roles from user %s%n", user); + } else { + String[] revokeRoles = line.getOptionValues(GravitinoOptions.ROLE); + for (String role : revokeRoles) { + this.gravitinoCommandLine + .newRemoveRoleFromUser(this.url, this.ignore, this.metalake, user, role) + .validate() + .handle(); + } + System.out.printf("Removed roles %s from user %s%n", COMMA_JOINER.join(revokeRoles), user); } - System.out.printf("Remove roles %s from user %s%n", COMMA_JOINER.join(revokeRoles), user); } /** Handles the "GRANT" command. */ diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/AllMetalakeDetails.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/AllMetalakeDetails.java index 07d61dcaa7c..b76138cb5c9 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/AllMetalakeDetails.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/AllMetalakeDetails.java @@ -55,6 +55,6 @@ public void handle() { String all = Joiner.on(System.lineSeparator()).join(metalakeDetails); - System.out.print(all.toString()); + System.out.print(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/GroupDetails.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/GroupDetails.java index 7217d5ad3bd..58188c38a3f 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/GroupDetails.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/GroupDetails.java @@ -62,6 +62,6 @@ public void handle() { String all = roles.isEmpty() ? "The group has no roles." : String.join(",", roles); - System.out.println(all.toString()); + System.out.println(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListAllTags.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListAllTags.java index cded12808d9..e3bd42ae04c 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListAllTags.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListAllTags.java @@ -55,6 +55,6 @@ public void handle() { String all = tags.length == 0 ? "No tags exist." : String.join(",", tags); - System.out.println(all.toString()); + System.out.println(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListColumns.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListColumns.java index f3e8e0125cf..e1d8eac2086 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListColumns.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListColumns.java @@ -94,6 +94,6 @@ public void handle() { + System.lineSeparator()); } - System.out.print(all.toString()); + System.out.print(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListEntityTags.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListEntityTags.java index a1c316fbdf2..90b1000fa05 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListEntityTags.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListEntityTags.java @@ -20,15 +20,18 @@ package org.apache.gravitino.cli.commands; import org.apache.gravitino.Catalog; -import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.Schema; import org.apache.gravitino.cli.ErrorMessages; import org.apache.gravitino.cli.FullName; +import org.apache.gravitino.cli.utils.FullNameUtil; import org.apache.gravitino.client.GravitinoClient; import org.apache.gravitino.exceptions.NoSuchCatalogException; import org.apache.gravitino.exceptions.NoSuchMetalakeException; import org.apache.gravitino.exceptions.NoSuchSchemaException; import org.apache.gravitino.exceptions.NoSuchTableException; +import org.apache.gravitino.file.Fileset; +import org.apache.gravitino.messaging.Topic; +import org.apache.gravitino.model.Model; import org.apache.gravitino.rel.Table; /* Lists all tags in a metalake. */ @@ -58,17 +61,34 @@ public void handle() { try { GravitinoClient client = buildClient(metalake); - // TODO fileset and topic - if (name.hasTableName()) { + if (name.getLevel() == 3) { String catalog = name.getCatalogName(); - String schema = name.getSchemaName(); - String table = name.getTableName(); - Table gTable = - client - .loadCatalog(catalog) - .asTableCatalog() - .loadTable(NameIdentifier.of(schema, table)); - tags = gTable.supportsTags().listTags(); + Catalog catalogObject = client.loadCatalog(catalog); + switch (catalogObject.type()) { + case RELATIONAL: + Table gTable = catalogObject.asTableCatalog().loadTable(FullNameUtil.toTable(name)); + tags = gTable.supportsTags().listTags(); + break; + + case MODEL: + Model gModel = catalogObject.asModelCatalog().getModel(FullNameUtil.toModel(name)); + tags = gModel.supportsTags().listTags(); + break; + + case FILESET: + Fileset fileset = + catalogObject.asFilesetCatalog().loadFileset(FullNameUtil.toFileset(name)); + tags = fileset.supportsTags().listTags(); + break; + + case MESSAGING: + Topic topic = catalogObject.asTopicCatalog().loadTopic(FullNameUtil.toTopic(name)); + tags = topic.supportsTags().listTags(); + break; + + default: + break; + } } else if (name.hasSchemaName()) { String catalog = name.getCatalogName(); String schema = name.getSchemaName(); @@ -93,6 +113,6 @@ public void handle() { String all = String.join(",", tags); - System.out.println(all.toString()); + System.out.println(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListFilesets.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListFilesets.java index d00ba3e6ba5..cb46b7953c3 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListFilesets.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListFilesets.java @@ -73,6 +73,6 @@ public void handle() { String all = filesets.length == 0 ? "No filesets exist." : Joiner.on(",").join(filesets); - System.out.println(all.toString()); + System.out.println(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListGroups.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListGroups.java index a517b4daed8..844b8e21d09 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListGroups.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListGroups.java @@ -55,6 +55,6 @@ public void handle() { String all = groups.length == 0 ? "No groups exist." : String.join(",", groups); - System.out.println(all.toString()); + System.out.println(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListProperties.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListProperties.java index 56c8fb8ba78..a7d08ba36e2 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListProperties.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListProperties.java @@ -51,6 +51,6 @@ public void printProperties(Map properties) { all.append(property.getKey() + "," + property.getValue() + System.lineSeparator()); } - System.out.print(all.toString()); + System.out.print(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListRoles.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListRoles.java index 2ecb35bd093..760fe21e3bc 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListRoles.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListRoles.java @@ -55,6 +55,6 @@ public void handle() { String all = roles.length == 0 ? "No roles exist." : String.join(",", roles); - System.out.println(all.toString()); + System.out.println(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListSchema.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListSchema.java index 110a6477a62..695f44d42d7 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListSchema.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListSchema.java @@ -62,6 +62,6 @@ public void handle() { String all = schemas.length == 0 ? "No schemas exist." : Joiner.on(",").join(schemas); - System.out.println(all.toString()); + System.out.println(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListTables.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListTables.java index 41a71e87c00..515fb28678c 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListTables.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListTables.java @@ -66,6 +66,6 @@ public void handle() { ? "No tables exist." : Joiner.on(System.lineSeparator()).join(tableNames); - System.out.println(all.toString()); + System.out.println(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListUsers.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListUsers.java index a70176dcfcb..3df4b7ca6bc 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListUsers.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/ListUsers.java @@ -53,8 +53,8 @@ public void handle() { exitWithError(exp.getMessage()); } - String all = String.join(",", users); + String all = users.length == 0 ? "No users exist." : String.join(",", users); - System.out.println(all.toString()); + System.out.println(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/OwnerDetails.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/OwnerDetails.java index 8485a587560..a815d6ba14d 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/OwnerDetails.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/OwnerDetails.java @@ -68,7 +68,7 @@ public OwnerDetails( /** Displays the owner of an entity. */ @Override public void handle() { - Optional owner = null; + Optional owner = Optional.empty(); MetadataObject metadata = MetadataObjects.parse(entity, entityType); try { diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RemoveAllRoles.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RemoveAllRoles.java new file mode 100644 index 00000000000..eb7b354223d --- /dev/null +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RemoveAllRoles.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.gravitino.cli.commands; + +import java.util.List; +import org.apache.gravitino.authorization.Group; +import org.apache.gravitino.authorization.User; +import org.apache.gravitino.cli.CommandEntities; +import org.apache.gravitino.cli.ErrorMessages; +import org.apache.gravitino.client.GravitinoClient; +import org.apache.gravitino.exceptions.NoSuchGroupException; +import org.apache.gravitino.exceptions.NoSuchMetalakeException; +import org.apache.gravitino.exceptions.NoSuchUserException; + +/** Removes all roles from a group or user. */ +public class RemoveAllRoles extends Command { + protected final String metalake; + protected final String entity; + protected final String entityType; + + /** + * Removes all roles from a group or user. + * + * @param url The URL of the Gravitino server. + * @param ignoreVersions If true don't check the client/server versions match. + * @param metalake The name of the metalake. + * @param entity the name of the group or user. + * @param entityType The type of the entity (group or user). + */ + public RemoveAllRoles( + String url, boolean ignoreVersions, String metalake, String entity, String entityType) { + super(url, ignoreVersions); + this.metalake = metalake; + this.entity = entity; + this.entityType = entityType; + } + + /** Removes all roles from a group or user. */ + @Override + public void handle() { + if (CommandEntities.GROUP.equals(entityType)) { + revokeAllRolesFromGroup(); + } else { + revokeAllRolesFromUser(); + } + } + + /** Removes all roles from a group. */ + private void revokeAllRolesFromGroup() { + List roles; + try { + GravitinoClient client = buildClient(metalake); + Group group = client.getGroup(entity); + roles = group.roles(); + client.revokeRolesFromGroup(roles, entity); + } catch (NoSuchMetalakeException e) { + exitWithError(ErrorMessages.UNKNOWN_METALAKE); + } catch (NoSuchGroupException e) { + exitWithError(ErrorMessages.UNKNOWN_GROUP); + } catch (Exception e) { + exitWithError(e.getMessage()); + } + } + + /** Removes all roles from a user. */ + private void revokeAllRolesFromUser() { + List roles; + try { + GravitinoClient client = buildClient(metalake); + User user = client.getUser(entity); + roles = user.roles(); + client.revokeRolesFromUser(roles, entity); + } catch (NoSuchMetalakeException e) { + exitWithError(ErrorMessages.UNKNOWN_METALAKE); + } catch (NoSuchUserException e) { + exitWithError(ErrorMessages.UNKNOWN_USER); + } catch (Exception e) { + exitWithError(e.getMessage()); + } + } +} diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RemoveRoleFromGroup.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RemoveRoleFromGroup.java index dd7ccc0e79d..7f2f7e2b2c4 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RemoveRoleFromGroup.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RemoveRoleFromGroup.java @@ -34,7 +34,7 @@ public class RemoveRoleFromGroup extends Command { protected String role; /** - * Removes a role from a group. + * Remove a role from a group. * * @param url The URL of the Gravitino server. * @param ignoreVersions If true don't check the client/server versions match. @@ -50,7 +50,7 @@ public RemoveRoleFromGroup( this.role = role; } - /** Adds a role to a group. */ + /** Remove a role from a group. */ @Override public void handle() { try { diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RevokeAllPrivileges.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RevokeAllPrivileges.java new file mode 100644 index 00000000000..3167c4b6c37 --- /dev/null +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RevokeAllPrivileges.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.gravitino.cli.commands; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.gravitino.MetadataObject; +import org.apache.gravitino.authorization.Privilege; +import org.apache.gravitino.authorization.Role; +import org.apache.gravitino.authorization.SecurableObject; +import org.apache.gravitino.cli.ErrorMessages; +import org.apache.gravitino.cli.FullName; +import org.apache.gravitino.client.GravitinoClient; +import org.apache.gravitino.exceptions.NoSuchMetalakeException; +import org.apache.gravitino.exceptions.NoSuchRoleException; + +/** Revokes all privileges from a role to an entity or all entities. */ +public class RevokeAllPrivileges extends MetadataCommand { + + protected final String metalake; + protected final String role; + protected final FullName entity; + + /** + * Revokes all privileges from a role to an entity or all entities. + * + * @param url The URL of the Gravitino server. + * @param ignoreVersions If true don't check the client/server versions match. + * @param metalake The name of the metalake. + * @param role The name of the role. + * @param entity The name of the entity. + */ + public RevokeAllPrivileges( + String url, boolean ignoreVersions, String metalake, String role, FullName entity) { + super(url, ignoreVersions); + this.metalake = metalake; + this.role = role; + this.entity = entity; + } + + /** Revokes all privileges from a role to an entity or all entities. */ + @Override + public void handle() { + List matchedObjects; + Map> revokedPrivileges = Maps.newHashMap(); + + try { + GravitinoClient client = buildClient(metalake); + matchedObjects = getMatchedObjects(client); + + for (SecurableObject securableObject : matchedObjects) { + String objectFullName = securableObject.fullName(); + Set privileges = new HashSet<>(securableObject.privileges()); + MetadataObject metadataObject = constructMetadataObject(entity, client); + client.revokePrivilegesFromRole(role, metadataObject, privileges); + + revokedPrivileges.put(objectFullName, privileges); + } + } catch (NoSuchMetalakeException e) { + exitWithError(ErrorMessages.UNKNOWN_METALAKE); + } catch (NoSuchRoleException e) { + exitWithError(ErrorMessages.UNKNOWN_ROLE); + } catch (Exception e) { + exitWithError(e.getMessage()); + } + + if (revokedPrivileges.isEmpty()) outputRevokedPrivileges("No privileges revoked."); + outputRevokedPrivileges(revokedPrivileges); + } + + private List getMatchedObjects(GravitinoClient client) { + Role gRole = client.getRole(role); + return gRole.securableObjects().stream() + .filter(s -> s.fullName().equals(entity.getName())) + .collect(Collectors.toList()); + } + + private void outputRevokedPrivileges(Map> revokedPrivileges) { + List revokedInfoList = Lists.newArrayList(); + + for (Map.Entry> entry : revokedPrivileges.entrySet()) { + List revokedPrivilegesList = + entry.getValue().stream().map(Privilege::simpleString).collect(Collectors.toList()); + revokedInfoList.add(entry.getKey() + ": " + COMMA_JOINER.join(revokedPrivilegesList)); + } + + System.out.println("Revoked privileges:"); + for (String info : revokedInfoList) { + System.out.println(info); + } + } + + private void outputRevokedPrivileges(String message) { + System.out.println(message); + } + + /** + * verify the arguments. + * + * @return Returns itself via argument validation, otherwise exits. + */ + @Override + public Command validate() { + if (!entity.hasName()) exitWithError(ErrorMessages.MISSING_NAME); + return super.validate(); + } +} diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RevokePrivilegesFromRole.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RevokePrivilegesFromRole.java index fbf273ce0d8..5907c494c05 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RevokePrivilegesFromRole.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/RevokePrivilegesFromRole.java @@ -87,17 +87,13 @@ public void handle() { MetadataObject metadataObject = constructMetadataObject(entity, client); client.revokePrivilegesFromRole(role, metadataObject, privilegesSet); } catch (NoSuchMetalakeException err) { - System.err.println(ErrorMessages.UNKNOWN_METALAKE); - return; + exitWithError(ErrorMessages.UNKNOWN_METALAKE); } catch (NoSuchRoleException err) { - System.err.println(ErrorMessages.UNKNOWN_ROLE); - return; + exitWithError(ErrorMessages.UNKNOWN_ROLE); } catch (NoSuchMetadataObjectException err) { - System.err.println(ErrorMessages.UNKNOWN_USER); - return; + exitWithError(ErrorMessages.UNKNOWN_USER); } catch (Exception exp) { - System.err.println(exp.getMessage()); - return; + exitWithError(exp.getMessage()); } String all = String.join(",", privileges); diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/TagEntity.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/TagEntity.java index 4a06918850d..3b97778818e 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/TagEntity.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/TagEntity.java @@ -20,16 +20,19 @@ package org.apache.gravitino.cli.commands; import org.apache.gravitino.Catalog; -import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.Schema; import org.apache.gravitino.cli.ErrorMessages; import org.apache.gravitino.cli.FullName; +import org.apache.gravitino.cli.utils.FullNameUtil; import org.apache.gravitino.client.GravitinoClient; import org.apache.gravitino.exceptions.NoSuchCatalogException; import org.apache.gravitino.exceptions.NoSuchMetalakeException; import org.apache.gravitino.exceptions.NoSuchSchemaException; import org.apache.gravitino.exceptions.NoSuchTableException; import org.apache.gravitino.exceptions.TagAlreadyAssociatedException; +import org.apache.gravitino.file.Fileset; +import org.apache.gravitino.messaging.Topic; +import org.apache.gravitino.model.Model; import org.apache.gravitino.rel.Table; public class TagEntity extends Command { @@ -63,18 +66,42 @@ public void handle() { try { GravitinoClient client = buildClient(metalake); - // TODO fileset and topic - if (name.hasTableName()) { + if (name.getLevel() == 3) { String catalog = name.getCatalogName(); - String schema = name.getSchemaName(); - String table = name.getTableName(); - Table gTable = - client - .loadCatalog(catalog) - .asTableCatalog() - .loadTable(NameIdentifier.of(schema, table)); - tagsToAdd = gTable.supportsTags().associateTags(tags, null); - entity = table; + Catalog catalogObject = client.loadCatalog(catalog); + switch (catalogObject.type()) { + case RELATIONAL: + String table = name.getTableName(); + entity = table; + Table gTable = catalogObject.asTableCatalog().loadTable(FullNameUtil.toTable(name)); + tagsToAdd = gTable.supportsTags().associateTags(tags, null); + break; + + case MODEL: + String model = name.getModelName(); + entity = model; + Model gModel = catalogObject.asModelCatalog().getModel(FullNameUtil.toModel(name)); + tagsToAdd = gModel.supportsTags().associateTags(tags, null); + break; + + case FILESET: + String fileset = name.getFilesetName(); + entity = fileset; + Fileset gFileset = + catalogObject.asFilesetCatalog().loadFileset(FullNameUtil.toFileset(name)); + gFileset.supportsTags().associateTags(tags, null); + break; + + case MESSAGING: + String topic = name.getTopicName(); + entity = topic; + Topic gTopic = catalogObject.asTopicCatalog().loadTopic(FullNameUtil.toTopic(name)); + gTopic.supportsTags().associateTags(tags, null); + break; + + default: + break; + } } else if (name.hasSchemaName()) { String catalog = name.getCatalogName(); String schema = name.getSchemaName(); diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UntagEntity.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UntagEntity.java index 3503d5eb7bf..205242135b4 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UntagEntity.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UntagEntity.java @@ -19,21 +19,22 @@ package org.apache.gravitino.cli.commands; -import com.google.common.base.Joiner; import org.apache.gravitino.Catalog; -import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.Schema; import org.apache.gravitino.cli.ErrorMessages; import org.apache.gravitino.cli.FullName; +import org.apache.gravitino.cli.utils.FullNameUtil; import org.apache.gravitino.client.GravitinoClient; import org.apache.gravitino.exceptions.NoSuchCatalogException; import org.apache.gravitino.exceptions.NoSuchMetalakeException; import org.apache.gravitino.exceptions.NoSuchSchemaException; import org.apache.gravitino.exceptions.NoSuchTableException; +import org.apache.gravitino.file.Fileset; +import org.apache.gravitino.messaging.Topic; +import org.apache.gravitino.model.Model; import org.apache.gravitino.rel.Table; public class UntagEntity extends Command { - public static final Joiner COMMA_JOINER = Joiner.on(", ").skipNulls(); protected final String metalake; protected final FullName name; protected final String[] tags; @@ -64,18 +65,42 @@ public void handle() { try { GravitinoClient client = buildClient(metalake); - // TODO fileset and topic - if (name.hasTableName()) { + if (name.getLevel() == 3) { String catalog = name.getCatalogName(); - String schema = name.getSchemaName(); - String table = name.getTableName(); - Table gTable = - client - .loadCatalog(catalog) - .asTableCatalog() - .loadTable(NameIdentifier.of(schema, table)); - removeTags = gTable.supportsTags().associateTags(null, tags); - entity = table; + Catalog catalogObject = client.loadCatalog(catalog); + switch (catalogObject.type()) { + case RELATIONAL: + String table = name.getTableName(); + entity = table; + Table gTable = catalogObject.asTableCatalog().loadTable(FullNameUtil.toTable(name)); + removeTags = gTable.supportsTags().associateTags(null, tags); + break; + + case MODEL: + String model = name.getModelName(); + entity = model; + Model gModel = catalogObject.asModelCatalog().getModel(FullNameUtil.toModel(name)); + removeTags = gModel.supportsTags().associateTags(null, tags); + break; + + case FILESET: + String fileset = name.getFilesetName(); + entity = fileset; + Fileset gFileset = + catalogObject.asFilesetCatalog().loadFileset(FullNameUtil.toFileset(name)); + removeTags = gFileset.supportsTags().associateTags(null, tags); + break; + + case MESSAGING: + String topic = name.getTopicName(); + entity = topic; + Topic gTopic = catalogObject.asTopicCatalog().loadTopic(FullNameUtil.toTopic(name)); + removeTags = gTopic.supportsTags().associateTags(null, tags); + break; + + default: + break; + } } else if (name.hasSchemaName()) { String catalog = name.getCatalogName(); String schema = name.getSchemaName(); diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UserDetails.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UserDetails.java index e37f8e6f139..d89597d1698 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UserDetails.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UserDetails.java @@ -62,6 +62,6 @@ public void handle() { String all = roles.isEmpty() ? "The user has no roles." : String.join(",", roles); - System.out.println(all.toString()); + System.out.println(all); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/TableFormat.java b/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/TableFormat.java index c9f502069b9..a3c99756524 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/TableFormat.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/outputs/TableFormat.java @@ -111,10 +111,10 @@ static final class TableFormatImpl { Pattern.compile( "[\u1100-\u115F\u2E80-\uA4CF\uAC00-\uD7A3\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE6F\uFF00-\uFF60\uFFE0-\uFFE6]"); private int[][] elementOutputWidths; - private final String horizontalDelimiter = "-"; - private final String verticalDelimiter = "|"; - private final String crossDelimiter = "+"; - private final String indent = " "; + private static final String horizontalDelimiter = "-"; + private static final String verticalDelimiter = "|"; + private static final String crossDelimiter = "+"; + private static final String indent = " "; public void debug() { System.out.println(); diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/utils/FullNameUtil.java b/clients/cli/src/main/java/org/apache/gravitino/cli/utils/FullNameUtil.java new file mode 100644 index 00000000000..6017b4246db --- /dev/null +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/utils/FullNameUtil.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.gravitino.cli.utils; + +import org.apache.gravitino.NameIdentifier; +import org.apache.gravitino.cli.FullName; + +/** Utility class for helping with creating NameIdentifiers from {@link FullName}. */ +public class FullNameUtil { + + /** + * Returns a NameIdentifier for a model. + * + * @param fullName the {@link FullName} of the model. + * @return a NameIdentifier for the model. + */ + public static NameIdentifier toModel(FullName fullName) { + String schema = fullName.getSchemaName(); + String model = fullName.getModelName(); + + return NameIdentifier.of(schema, model); + } + + /** + * Returns a NameIdentifier for a table. + * + * @param fullName the {@link FullName} of the table. + * @return a NameIdentifier for the table. + */ + public static NameIdentifier toTable(FullName fullName) { + String schema = fullName.getSchemaName(); + String table = fullName.getTableName(); + + return NameIdentifier.of(schema, table); + } + + /** + * Returns a NameIdentifier for a fileset. + * + * @param fullName the {@link FullName} of the fileset. + * @return a NameIdentifier for the fileset. + */ + public static NameIdentifier toFileset(FullName fullName) { + String schema = fullName.getSchemaName(); + String fileset = fullName.getFilesetName(); + + return NameIdentifier.of(schema, fileset); + } + + /** + * Returns a NameIdentifier for a topic. + * + * @param fullName the {@link FullName} of the topic. + * @return a NameIdentifier for the topic. + */ + public static NameIdentifier toTopic(FullName fullName) { + String schema = fullName.getSchemaName(); + String topic = fullName.getTopicName(); + + return NameIdentifier.of(schema, topic); + } +} diff --git a/clients/cli/src/main/resources/group_help.txt b/clients/cli/src/main/resources/group_help.txt index cbac95747af..a5badc82265 100644 --- a/clients/cli/src/main/resources/group_help.txt +++ b/clients/cli/src/main/resources/group_help.txt @@ -18,3 +18,9 @@ gcli group details --group new_group --audit Delete a group gcli group delete --group new_group + +Revoke a role from a group +gcli group revoke --group new_group --role admin + +Revoke all roles from a group +gcli group revoke --group new_group --all \ No newline at end of file diff --git a/clients/cli/src/main/resources/role_help.txt b/clients/cli/src/main/resources/role_help.txt index d0838cfa403..6c51980d1c5 100644 --- a/clients/cli/src/main/resources/role_help.txt +++ b/clients/cli/src/main/resources/role_help.txt @@ -42,3 +42,6 @@ gcli role grant --name catalog_postgres --role admin --privilege create_table mo Revoke a privilege gcli role revoke --metalake metalake_demo --name catalog_postgres --role admin --privilege create_table modify_table + +Revoke all privileges +gcli role revoke --metalake metalake_demo --name catalog_postgres --role admin --all \ No newline at end of file diff --git a/clients/cli/src/main/resources/user_help.txt b/clients/cli/src/main/resources/user_help.txt index bd08bea59fb..00add299d27 100644 --- a/clients/cli/src/main/resources/user_help.txt +++ b/clients/cli/src/main/resources/user_help.txt @@ -19,3 +19,8 @@ gcli user details --user new_user --audit Delete a user gcli user delete --user new_user +Revoke a user's role +gcli user revoke --user new_user --role admin + +Revoke all roles from a user +gcli user revoke --user new_user --all \ No newline at end of file diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestAreYouSure.java b/clients/cli/src/test/java/org/apache/gravitino/cli/TestAreYouSure.java new file mode 100644 index 00000000000..78a1c2d67d4 --- /dev/null +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestAreYouSure.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.gravitino.cli; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestAreYouSure { + + @Test + void testCommandWithForce() { + Assertions.assertTrue(AreYouSure.really(true)); + } + + @Test + void testCommandWithInputY() { + ByteArrayInputStream inputStream = + new ByteArrayInputStream("Y".getBytes(StandardCharsets.UTF_8)); + System.setIn(inputStream); + + Assertions.assertTrue(AreYouSure.really(false)); + } + + @Test + void testCommandWithInputN() { + ByteArrayInputStream inputStream = + new ByteArrayInputStream("N".getBytes(StandardCharsets.UTF_8)); + System.setIn(inputStream); + + Assertions.assertFalse(AreYouSure.really(false)); + } + + @Test + void testCommandWithInputInvalid() { + ByteArrayInputStream inputStream = + new ByteArrayInputStream("Invalid".getBytes(StandardCharsets.UTF_8)); + System.setIn(inputStream); + + Assertions.assertFalse(AreYouSure.really(false)); + } +} diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestFullNameUtil.java b/clients/cli/src/test/java/org/apache/gravitino/cli/TestFullNameUtil.java new file mode 100644 index 00000000000..8123bb33420 --- /dev/null +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestFullNameUtil.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.gravitino.cli; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.gravitino.NameIdentifier; +import org.apache.gravitino.cli.utils.FullNameUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class TestFullNameUtil { + private Options options; + + @BeforeEach + public void setUp() { + Main.useExit = false; + options = new GravitinoOptions().options(); + } + + @Test + void testToModel() throws ParseException { + String[] args = {"table", "list", "-i", "--name", "Model_catalog.schema.model"}; + CommandLine commandLine = new DefaultParser().parse(options, args); + FullName fullName = new FullName(commandLine); + NameIdentifier modelIdent = FullNameUtil.toModel(fullName); + Assertions.assertEquals("model", modelIdent.name()); + Assertions.assertArrayEquals(new String[] {"schema"}, modelIdent.namespace().levels()); + } + + @Test + void testToTable() throws ParseException { + String[] args = {"table", "list", "-i", "--name", "Table_catalog.schema.table"}; + CommandLine commandLine = new DefaultParser().parse(options, args); + FullName fullName = new FullName(commandLine); + NameIdentifier tableIdent = FullNameUtil.toTable(fullName); + Assertions.assertEquals("table", tableIdent.name()); + Assertions.assertArrayEquals(new String[] {"schema"}, tableIdent.namespace().levels()); + } + + @Test + void testToFileset() throws ParseException { + String[] args = {"fileset", "list", "-i", "--name", "Fileset_catalog.schema.fileset"}; + CommandLine commandLine = new DefaultParser().parse(options, args); + FullName fullName = new FullName(commandLine); + NameIdentifier filesetIdent = FullNameUtil.toFileset(fullName); + Assertions.assertEquals("fileset", filesetIdent.name()); + Assertions.assertArrayEquals(new String[] {"schema"}, filesetIdent.namespace().levels()); + } + + @Test + void testToTopic() throws ParseException { + String[] args = {"topic", "list", "-i", "--name", "Topic_catalog.schema.topic"}; + CommandLine commandLine = new DefaultParser().parse(options, args); + FullName fullName = new FullName(commandLine); + NameIdentifier topicIdent = FullNameUtil.toTopic(fullName); + Assertions.assertEquals("topic", topicIdent.name()); + Assertions.assertArrayEquals(new String[] {"schema"}, topicIdent.namespace().levels()); + } +} diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestFulllName.java b/clients/cli/src/test/java/org/apache/gravitino/cli/TestFulllName.java index f13d6e09201..c2e54c81d5e 100644 --- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestFulllName.java +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestFulllName.java @@ -235,4 +235,40 @@ public void testGetMetalakeWithoutMetalakeOption() throws ParseException { String errOutput = new String(errContent.toByteArray(), StandardCharsets.UTF_8).trim(); assertEquals(errOutput, ErrorMessages.MISSING_METALAKE); } + + @Test + @SuppressWarnings("DefaultCharset") + void testGetLevelFromCatalog() throws ParseException { + String[] args = {"table", "list", "-i", "--name", "Hive_catalog"}; + CommandLine commandLine = new DefaultParser().parse(options, args); + FullName fullName = new FullName(commandLine); + assertEquals(1, fullName.getLevel()); + } + + @Test + @SuppressWarnings("DefaultCharset") + void testGetLevelFromSchema() throws ParseException { + String[] args = {"table", "list", "-i", "--name", "Hive_catalog.default"}; + CommandLine commandLine = new DefaultParser().parse(options, args); + FullName fullName = new FullName(commandLine); + assertEquals(2, fullName.getLevel()); + } + + @Test + @SuppressWarnings("DefaultCharset") + void testGetLevelFromTable() throws ParseException { + String[] args = {"table", "list", "-i", "--name", "Hive_catalog.default.sales"}; + CommandLine commandLine = new DefaultParser().parse(options, args); + FullName fullName = new FullName(commandLine); + assertEquals(3, fullName.getLevel()); + } + + @Test + @SuppressWarnings("DefaultCharset") + void testGetLevelFromColumn() throws ParseException { + String[] args = {"table", "list", "-i", "--name", "Hive_catalog.default.sales.columns"}; + CommandLine commandLine = new DefaultParser().parse(options, args); + FullName fullName = new FullName(commandLine); + assertEquals(4, fullName.getLevel()); + } } diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestGroupCommands.java b/clients/cli/src/test/java/org/apache/gravitino/cli/TestGroupCommands.java index ce7a8956821..cae8402d8bb 100644 --- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestGroupCommands.java +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestGroupCommands.java @@ -39,6 +39,7 @@ import org.apache.gravitino.cli.commands.GroupAudit; import org.apache.gravitino.cli.commands.GroupDetails; import org.apache.gravitino.cli.commands.ListGroups; +import org.apache.gravitino.cli.commands.RemoveAllRoles; import org.apache.gravitino.cli.commands.RemoveRoleFromGroup; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -260,6 +261,32 @@ void testRemoveRolesFromGroupCommand() { verify(mockRemoveSecondRole).handle(); } + @Test + void testRemoveAllRolesFromGroupCommand() { + RemoveAllRoles mock = mock(RemoveAllRoles.class); + when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo"); + when(mockCommandLine.hasOption(GravitinoOptions.GROUP)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.GROUP)).thenReturn("groupA"); + when(mockCommandLine.hasOption(GravitinoOptions.ALL)).thenReturn(true); + GravitinoCommandLine commandLine = + spy( + new GravitinoCommandLine( + mockCommandLine, mockOptions, CommandEntities.GROUP, CommandActions.REVOKE)); + + doReturn(mock) + .when(commandLine) + .newRemoveAllRoles( + GravitinoCommandLine.DEFAULT_URL, + false, + "metalake_demo", + "groupA", + CommandEntities.GROUP); + doReturn(mock).when(mock).validate(); + commandLine.handleCommandLine(); + verify(mock).handle(); + } + @Test void testAddRolesToGroupCommand() { AddRoleToGroup mockAddFirstRole = mock(AddRoleToGroup.class); diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestRoleCommands.java b/clients/cli/src/test/java/org/apache/gravitino/cli/TestRoleCommands.java index e95d2975f7d..be463019fd3 100644 --- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestRoleCommands.java +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestRoleCommands.java @@ -34,11 +34,14 @@ import java.io.PrintStream; import java.nio.charset.StandardCharsets; import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; import org.apache.gravitino.cli.commands.CreateRole; import org.apache.gravitino.cli.commands.DeleteRole; import org.apache.gravitino.cli.commands.GrantPrivilegesToRole; import org.apache.gravitino.cli.commands.ListRoles; +import org.apache.gravitino.cli.commands.RevokeAllPrivileges; import org.apache.gravitino.cli.commands.RevokePrivilegesFromRole; import org.apache.gravitino.cli.commands.RoleAudit; import org.apache.gravitino.cli.commands.RoleDetails; @@ -48,6 +51,7 @@ class TestRoleCommands { private CommandLine mockCommandLine; + private Options options; private Options mockOptions; private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); @@ -58,6 +62,7 @@ class TestRoleCommands { void setUp() { mockCommandLine = mock(CommandLine.class); mockOptions = mock(Options.class); + options = new GravitinoOptions().options(); System.setOut(new PrintStream(outContent)); System.setErr(new PrintStream(errContent)); } @@ -99,6 +104,7 @@ void testRoleDetailsCommand() { doReturn(mockDetails) .when(commandLine) .newRoleDetails(GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", "admin"); + doReturn(mockDetails).when(mockDetails).validate(); commandLine.handleCommandLine(); verify(mockDetails).handle(); @@ -333,6 +339,51 @@ void testRevokePrivilegesFromRole() { verify(mockRevoke).handle(); } + @Test + void testRevokeAllPrivilegesFromRole() { + RevokeAllPrivileges mockRevoke = mock(RevokeAllPrivileges.class); + when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo"); + when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)).thenReturn("catalog"); + when(mockCommandLine.hasOption(GravitinoOptions.ROLE)).thenReturn(true); + when(mockCommandLine.getOptionValues(GravitinoOptions.ROLE)).thenReturn(new String[] {"admin"}); + when(mockCommandLine.hasOption(GravitinoOptions.ALL)).thenReturn(true); + GravitinoCommandLine commandLine = + spy( + new GravitinoCommandLine( + mockCommandLine, mockOptions, CommandEntities.ROLE, CommandActions.REVOKE)); + doReturn(mockRevoke) + .when(commandLine) + .newRevokeAllPrivileges( + eq(GravitinoCommandLine.DEFAULT_URL), + eq(false), + eq("metalake_demo"), + eq("admin"), + any()); + doReturn(mockRevoke).when(mockRevoke).validate(); + commandLine.handleCommandLine(); + verify(mockRevoke).handle(); + } + + @Test + void testRevokeAllPrivilegesFromRoleWithoutName() throws ParseException { + Main.useExit = false; + String[] args = {"role", "revoke", "-m", "metalake_demo", "--role", "admin", "--all"}; + CommandLine commandLine = new DefaultParser().parse(options, args); + FullName fullName = new FullName(commandLine); + + RevokeAllPrivileges spyRevokeAllPrivileges = + spy( + new RevokeAllPrivileges( + GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", "admin", fullName)); + + assertThrows(RuntimeException.class, spyRevokeAllPrivileges::validate); + verify(spyRevokeAllPrivileges, never()).handle(); + String errOutput = new String(errContent.toByteArray(), StandardCharsets.UTF_8).trim(); + assertEquals(ErrorMessages.MISSING_NAME, errOutput); + } + @Test void testRevokePrivilegesFromRoleWithoutPrivileges() { Main.useExit = false; diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestUserCommands.java b/clients/cli/src/test/java/org/apache/gravitino/cli/TestUserCommands.java index c7612f6c870..47f003c96b4 100644 --- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestUserCommands.java +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestUserCommands.java @@ -37,6 +37,7 @@ import org.apache.gravitino.cli.commands.CreateUser; import org.apache.gravitino.cli.commands.DeleteUser; import org.apache.gravitino.cli.commands.ListUsers; +import org.apache.gravitino.cli.commands.RemoveAllRoles; import org.apache.gravitino.cli.commands.RemoveRoleFromUser; import org.apache.gravitino.cli.commands.UserAudit; import org.apache.gravitino.cli.commands.UserDetails; @@ -261,6 +262,27 @@ void testRemoveRolesFromUserCommand() { verify(mockRemoveFirstRole).handle(); } + @Test + void removeAllRolesFromUserCommand() { + RemoveAllRoles mockRemove = mock(RemoveAllRoles.class); + when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.METALAKE)).thenReturn("metalake_demo"); + when(mockCommandLine.hasOption(GravitinoOptions.USER)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.USER)).thenReturn("user"); + when(mockCommandLine.hasOption(GravitinoOptions.ALL)).thenReturn(true); + GravitinoCommandLine commandLine = + spy( + new GravitinoCommandLine( + mockCommandLine, mockOptions, CommandEntities.USER, CommandActions.REVOKE)); + doReturn(mockRemove) + .when(commandLine) + .newRemoveAllRoles( + GravitinoCommandLine.DEFAULT_URL, false, "metalake_demo", "user", CommandEntities.USER); + doReturn(mockRemove).when(mockRemove).validate(); + commandLine.handleCommandLine(); + verify(mockRemove).handle(); + } + @Test void testAddRolesToUserCommand() { AddRoleToUser mockAddFirstRole = mock(AddRoleToUser.class); diff --git a/core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java b/core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java index 14b1912b4d6..444e89062a6 100644 --- a/core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java +++ b/core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java @@ -208,8 +208,15 @@ public void initAuthorizationPluginInstance(IsolatedClassLoader classLoader) { try { BaseAuthorization authorization = BaseAuthorization.createAuthorization(classLoader, authorizationProvider); + + // Load the authorization plugin with the class loader of the catalog. + // Because the JDBC authorization plugin may load JDBC driver using the class loader. authorizationPlugin = - authorization.newPlugin(entity.namespace().level(0), provider(), this.conf); + classLoader.withClassLoader( + cl -> + authorization.newPlugin( + entity.namespace().level(0), provider(), this.conf)); + } catch (Exception e) { LOG.error("Failed to load authorization with class loader", e); throw new RuntimeException(e); diff --git a/core/src/main/java/org/apache/gravitino/credential/CatalogCredentialContext.java b/core/src/main/java/org/apache/gravitino/credential/CatalogCredentialContext.java index 6ac0c498c02..2a7955d08ad 100644 --- a/core/src/main/java/org/apache/gravitino/credential/CatalogCredentialContext.java +++ b/core/src/main/java/org/apache/gravitino/credential/CatalogCredentialContext.java @@ -47,7 +47,7 @@ public boolean equals(Object o) { if (this == o) { return true; } - if (o == null || !(o instanceof CatalogCredentialContext)) { + if (!(o instanceof CatalogCredentialContext)) { return false; } return Objects.equal(userName, ((CatalogCredentialContext) o).userName); diff --git a/core/src/main/java/org/apache/gravitino/credential/CredentialCacheKey.java b/core/src/main/java/org/apache/gravitino/credential/CredentialCacheKey.java index 1d0d8f7b3b6..635f530b073 100644 --- a/core/src/main/java/org/apache/gravitino/credential/CredentialCacheKey.java +++ b/core/src/main/java/org/apache/gravitino/credential/CredentialCacheKey.java @@ -43,7 +43,7 @@ public boolean equals(Object o) { if (this == o) { return true; } - if (o == null || !(o instanceof CredentialCacheKey)) { + if (!(o instanceof CredentialCacheKey)) { return false; } CredentialCacheKey that = (CredentialCacheKey) o; diff --git a/core/src/main/java/org/apache/gravitino/credential/PathBasedCredentialContext.java b/core/src/main/java/org/apache/gravitino/credential/PathBasedCredentialContext.java index 06d17b134ba..2c015b0c569 100644 --- a/core/src/main/java/org/apache/gravitino/credential/PathBasedCredentialContext.java +++ b/core/src/main/java/org/apache/gravitino/credential/PathBasedCredentialContext.java @@ -67,7 +67,7 @@ public boolean equals(Object o) { if (this == o) { return true; } - if (o == null || !(o instanceof PathBasedCredentialContext)) { + if (!(o instanceof PathBasedCredentialContext)) { return false; } PathBasedCredentialContext that = (PathBasedCredentialContext) o; diff --git a/core/src/main/java/org/apache/gravitino/hook/MetalakeHookDispatcher.java b/core/src/main/java/org/apache/gravitino/hook/MetalakeHookDispatcher.java index 26f31a88396..aa53b8800f8 100644 --- a/core/src/main/java/org/apache/gravitino/hook/MetalakeHookDispatcher.java +++ b/core/src/main/java/org/apache/gravitino/hook/MetalakeHookDispatcher.java @@ -116,6 +116,7 @@ public void disableMetalake(NameIdentifier ident) throws NoSuchMetalakeException dispatcher.disableMetalake(ident); } + @Override public boolean dropMetalake(NameIdentifier ident) { // For metalake, we don't clear all the privileges of catalog authorization plugin. // we just remove metalake. diff --git a/core/src/main/java/org/apache/gravitino/listener/api/event/CreateTablePreEvent.java b/core/src/main/java/org/apache/gravitino/listener/api/event/CreateTablePreEvent.java index 6c01d614f3c..dd6b8cc123b 100644 --- a/core/src/main/java/org/apache/gravitino/listener/api/event/CreateTablePreEvent.java +++ b/core/src/main/java/org/apache/gravitino/listener/api/event/CreateTablePreEvent.java @@ -43,6 +43,7 @@ public TableInfo createTableRequest() { return createTableRequest; } + @Override public OperationType operationType() { return OperationType.CREATE_TABLE; } diff --git a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/CatalogMetaPostgreSQLProvider.java b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/CatalogMetaPostgreSQLProvider.java index abaf2c59af9..77bf3c4e285 100644 --- a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/CatalogMetaPostgreSQLProvider.java +++ b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/CatalogMetaPostgreSQLProvider.java @@ -76,6 +76,7 @@ public String insertCatalogMetaOnDuplicateKeyUpdate(CatalogPO catalogPO) { + " deleted_at = #{catalogMeta.deletedAt}"; } + @Override public String updateCatalogMeta( @Param("newCatalogMeta") CatalogPO newCatalogPO, @Param("oldCatalogMeta") CatalogPO oldCatalogPO) { diff --git a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/MetalakeMetaPostgreSQLProvider.java b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/MetalakeMetaPostgreSQLProvider.java index a95d7f09fe3..06dde29751c 100644 --- a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/MetalakeMetaPostgreSQLProvider.java +++ b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/MetalakeMetaPostgreSQLProvider.java @@ -62,6 +62,7 @@ public String insertMetalakeMetaOnDuplicateKeyUpdate(MetalakePO metalakePO) { + " deleted_at = #{metalakeMeta.deletedAt}"; } + @Override public String updateMetalakeMeta( @Param("newMetalakeMeta") MetalakePO newMetalakePO, @Param("oldMetalakeMeta") MetalakePO oldMetalakePO) { diff --git a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/SecurableObjectPostgreSQLProvider.java b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/SecurableObjectPostgreSQLProvider.java index 92352bcd95a..6de57dbdc48 100644 --- a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/SecurableObjectPostgreSQLProvider.java +++ b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/postgresql/SecurableObjectPostgreSQLProvider.java @@ -32,6 +32,7 @@ import org.apache.ibatis.annotations.Param; public class SecurableObjectPostgreSQLProvider extends SecurableObjectBaseSQLProvider { + @Override public String batchSoftDeleteSecurableObjects( @Param("securableObjects") List securableObjectPOs) { return "