All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.trino.tests.product.deltalake.TestDeltaLakeIdentityColumnCompatibility Maven / Gradle / Ivy

The newest version!
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.trino.tests.product.deltalake;

import io.trino.tempto.assertions.QueryAssert;
import io.trino.testng.services.Flaky;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import static io.trino.tempto.assertions.QueryAssert.Row.row;
import static io.trino.tempto.assertions.QueryAssert.assertQueryFailure;
import static io.trino.testing.TestingNames.randomNameSuffix;
import static io.trino.tests.product.TestGroups.DELTA_LAKE_DATABRICKS_104;
import static io.trino.tests.product.TestGroups.DELTA_LAKE_DATABRICKS_113;
import static io.trino.tests.product.TestGroups.PROFILE_SPECIFIC_TESTS;
import static io.trino.tests.product.deltalake.util.DeltaLakeTestUtils.DATABRICKS_COMMUNICATION_FAILURE_ISSUE;
import static io.trino.tests.product.deltalake.util.DeltaLakeTestUtils.DATABRICKS_COMMUNICATION_FAILURE_MATCH;
import static io.trino.tests.product.deltalake.util.DeltaLakeTestUtils.dropDeltaTableWithRetry;
import static io.trino.tests.product.deltalake.util.DeltaLakeTestUtils.getColumnCommentOnDelta;
import static io.trino.tests.product.deltalake.util.DeltaLakeTestUtils.getColumnNamesOnDelta;
import static io.trino.tests.product.deltalake.util.DeltaLakeTestUtils.getTableCommentOnDelta;
import static io.trino.tests.product.utils.QueryExecutors.onDelta;
import static io.trino.tests.product.utils.QueryExecutors.onTrino;
import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;

public class TestDeltaLakeIdentityColumnCompatibility
        extends BaseTestDeltaLakeS3Storage
{
    @Test(groups = {DELTA_LAKE_DATABRICKS_104, PROFILE_SPECIFIC_TESTS})
    @Flaky(issue = DATABRICKS_COMMUNICATION_FAILURE_ISSUE, match = DATABRICKS_COMMUNICATION_FAILURE_MATCH)
    public void testIdentityColumn()
    {
        // Databricks 7.3 & 9.1 and OSS Delta Lake don't support identity columns https://github.com/delta-io/delta/issues/1100
        String tableName = "test_identity_column_" + randomNameSuffix();
        String tableDirectory = "databricks-compatibility-test-" + tableName;

        onDelta().executeQuery(format(
                """
                CREATE TABLE default.%s (a INT, b BIGINT GENERATED ALWAYS AS IDENTITY)
                USING DELTA LOCATION 's3://%s/%s'
                """,
                tableName,
                bucketName,
                tableDirectory));
        try {
            onTrino().executeQuery("COMMENT ON COLUMN delta.default." + tableName + ".b IS 'test column comment'");
            assertThat(getColumnCommentOnDelta("default", tableName, "b")).isEqualTo("test column comment");

            onTrino().executeQuery("COMMENT ON TABLE delta.default." + tableName + " IS 'test table comment'");
            assertThat(getTableCommentOnDelta("default", tableName)).isEqualTo("test table comment");

            onTrino().executeQuery("ALTER TABLE delta.default." + tableName + " ADD COLUMN c INT");
            assertThat(getColumnNamesOnDelta("default", tableName)).containsExactly("a", "b", "c");
            // Don't execute other column operations because column mapping mode 'none' doesn't support it. See other test methods in this class.

            assertThat((String) onDelta().executeQuery("SHOW CREATE TABLE default." + tableName).getOnlyValue())
                    .contains("b BIGINT GENERATED ALWAYS AS IDENTITY");
            onDelta().executeQuery("INSERT INTO default." + tableName + " (a, c) VALUES (0, 2)");

            QueryAssert.Row expected = row(0, 1, 2);
            assertThat(onTrino().executeQuery("SELECT * FROM delta.default." + tableName)).containsOnly(expected);
            assertThat(onDelta().executeQuery("SELECT * FROM default." + tableName)).containsOnly(expected);
        }
        finally {
            dropDeltaTableWithRetry("default." + tableName);
        }
    }

    @Test(groups = {DELTA_LAKE_DATABRICKS_113, PROFILE_SPECIFIC_TESTS}, dataProvider = "columnMappingDataProvider")
    @Flaky(issue = DATABRICKS_COMMUNICATION_FAILURE_ISSUE, match = DATABRICKS_COMMUNICATION_FAILURE_MATCH)
    public void testRenameIdentityColumn(String mode)
    {
        String tableName = "test_rename_identity_column_" + randomNameSuffix();

        onDelta().executeQuery("CREATE TABLE default." + tableName +
                "(data INT, col_identity BIGINT GENERATED ALWAYS AS IDENTITY)" +
                "USING DELTA " +
                "LOCATION 's3://" + bucketName + "/" + "databricks-compatibility-test-" + tableName + "'" +
                "TBLPROPERTIES ('delta.columnMapping.mode'='" + mode + "')");
        try {
            onDelta().executeQuery("ALTER TABLE default." + tableName + " RENAME COLUMN col_identity TO delta_col_identity");
            assertThat((String) onDelta().executeQuery("SHOW CREATE TABLE default." + tableName).getOnlyValue())
                    .contains("delta_col_identity BIGINT GENERATED ALWAYS AS IDENTITY");
            onDelta().executeQuery("INSERT INTO default." + tableName + " (data) VALUES 10");
            assertThat(onTrino().executeQuery("SELECT * FROM delta.default." + tableName))
                    .containsOnly(row(10, 1));
            assertThat(onDelta().executeQuery("SELECT * FROM default." + tableName))
                    .containsOnly(row(10, 1));

            onTrino().executeQuery("ALTER TABLE delta.default." + tableName + " RENAME COLUMN delta_col_identity TO trino_col_identity");
            assertThat((String) onDelta().executeQuery("SHOW CREATE TABLE default." + tableName).getOnlyValue())
                    .contains("trino_col_identity BIGINT GENERATED ALWAYS AS IDENTITY");
            assertQueryFailure(() -> onTrino().executeQuery("INSERT INTO delta.default." + tableName + " (data) VALUES (1)"))
                    .hasMessageContaining("Writing to tables with identity columns is not supported");
            onDelta().executeQuery("INSERT INTO default." + tableName + " (data) VALUES 20");
            assertThat(onTrino().executeQuery("SELECT * FROM delta.default." + tableName))
                    .containsOnly(row(10, 1), row(20, 2));
            assertThat(onDelta().executeQuery("SELECT * FROM default." + tableName))
                    .containsOnly(row(10, 1), row(20, 2));
        }
        finally {
            dropDeltaTableWithRetry("default." + tableName);
        }
    }

    @Test(groups = {DELTA_LAKE_DATABRICKS_113, PROFILE_SPECIFIC_TESTS}, dataProvider = "columnMappingDataProvider")
    @Flaky(issue = DATABRICKS_COMMUNICATION_FAILURE_ISSUE, match = DATABRICKS_COMMUNICATION_FAILURE_MATCH)
    public void testDropIdentityColumn(String mode)
    {
        String tableName = "test_drop_identity_column_" + randomNameSuffix();

        onDelta().executeQuery("CREATE TABLE default." + tableName +
                "(data INT, first_identity BIGINT GENERATED ALWAYS AS IDENTITY, second_identity BIGINT GENERATED ALWAYS AS IDENTITY)" +
                "USING DELTA " +
                "LOCATION 's3://" + bucketName + "/" + "databricks-compatibility-test-" + tableName + "'" +
                "TBLPROPERTIES ('delta.columnMapping.mode'='" + mode + "')");
        try {
            assertQueryFailure(() -> onTrino().executeQuery("INSERT INTO delta.default." + tableName + " (data) VALUES (1)"))
                    .hasMessageContaining("Writing to tables with identity columns is not supported");

            onDelta().executeQuery("ALTER TABLE default." + tableName + " DROP COLUMN first_identity");
            assertThat(getColumnNamesOnDelta("default", tableName))
                    .containsExactly("data", "second_identity");
            assertQueryFailure(() -> onTrino().executeQuery("INSERT INTO delta.default." + tableName + " (data) VALUES (1)"))
                    .hasMessageContaining("Writing to tables with identity columns is not supported");

            onTrino().executeQuery("ALTER TABLE delta.default." + tableName + " DROP COLUMN second_identity");
            assertThat(getColumnNamesOnDelta("default", tableName))
                    .containsExactly("data");

            onDelta().executeQuery("INSERT INTO default." + tableName + " VALUES 10");
            onTrino().executeQuery("INSERT INTO delta.default." + tableName + " VALUES 20");

            assertThat(onTrino().executeQuery("SELECT * FROM delta.default." + tableName))
                    .containsOnly(row(10), row(20));
            assertThat(onDelta().executeQuery("SELECT * FROM default." + tableName))
                    .containsOnly(row(10), row(20));
        }
        finally {
            dropDeltaTableWithRetry("default." + tableName);
        }
    }

    @Test(groups = {DELTA_LAKE_DATABRICKS_104, PROFILE_SPECIFIC_TESTS})
    @Flaky(issue = DATABRICKS_COMMUNICATION_FAILURE_ISSUE, match = DATABRICKS_COMMUNICATION_FAILURE_MATCH)
    public void testVacuumProcedureWithIdentityColumn()
    {
        String tableName = "test_vacuum_identity_column_" + randomNameSuffix();

        onDelta().executeQuery("CREATE TABLE default." + tableName +
                "(data INT, col_identity BIGINT GENERATED ALWAYS AS IDENTITY)" +
                "USING DELTA " +
                "LOCATION 's3://" + bucketName + "/" + "databricks-compatibility-test-" + tableName + "'");
        try {
            onDelta().executeQuery("INSERT INTO default." + tableName + " (data) VALUES 10");
            onDelta().executeQuery("INSERT INTO default." + tableName + " (data) VALUES 20");
            onDelta().executeQuery("DELETE FROM default." + tableName + " WHERE data = 20");

            onTrino().executeQuery("SET SESSION delta.vacuum_min_retention = '0s'");
            onTrino().executeQuery("CALL delta.system.vacuum('default', '" + tableName + "', '0s')");

            assertThat((String) onDelta().executeQuery("SHOW CREATE TABLE default." + tableName).getOnlyValue())
                    .contains("col_identity BIGINT GENERATED ALWAYS AS IDENTITY");

            assertThat(onTrino().executeQuery("SELECT * FROM delta.default." + tableName))
                    .containsOnly(row(10, 1));
            assertThat(onDelta().executeQuery("SELECT * FROM default." + tableName))
                    .containsOnly(row(10, 1));
        }
        finally {
            dropDeltaTableWithRetry("default." + tableName);
        }
    }

    @Test(groups = {DELTA_LAKE_DATABRICKS_104, PROFILE_SPECIFIC_TESTS})
    @Flaky(issue = DATABRICKS_COMMUNICATION_FAILURE_ISSUE, match = DATABRICKS_COMMUNICATION_FAILURE_MATCH)
    public void testIdentityColumnCheckpointInterval()
    {
        String tableName = "test_identity_column_checkpoint_interval_" + randomNameSuffix();

        onDelta().executeQuery("CREATE TABLE default." + tableName +
                "(data INT, col_identity BIGINT GENERATED ALWAYS AS IDENTITY)" +
                "USING DELTA " +
                "LOCATION 's3://" + bucketName + "/" + "databricks-compatibility-test-" + tableName + "'" +
                "TBLPROPERTIES ('delta.checkpointInterval' = 1)");
        try {
            onTrino().executeQuery("COMMENT ON COLUMN delta.default." + tableName + ".col_identity IS 'test column comment'");
            assertThat((String) onDelta().executeQuery("SHOW CREATE TABLE default." + tableName).getOnlyValue())
                    .contains("col_identity BIGINT GENERATED ALWAYS AS IDENTITY");
        }
        finally {
            dropDeltaTableWithRetry("default." + tableName);
        }
    }

    @DataProvider
    public Object[][] columnMappingDataProvider()
    {
        return new Object[][] {
                {"id"},
                {"name"},
        };
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy