diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/MCTransaction.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/MCTransaction.java index 76a3c84ebb7602..5f60f856497a85 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/MCTransaction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/MCTransaction.java @@ -71,6 +71,10 @@ public void updateMCCommitData(List commitDataList) { public void beginInsert(ExternalTable dorisTable, Optional ctx) throws UserException { this.table = (MaxComputeExternalTable) dorisTable; + if (table.isUnsupportedOdpsTable()) { + throw new UserException("Writing MaxCompute external table or logical view is not supported: " + + table.getDbName() + "." + table.getName()); + } try { TableIdentifier tableId = catalog.getOdpsTableIdentifier(table.getDbName(), table.getName()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/MaxComputeExternalCatalog.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/MaxComputeExternalCatalog.java index 75cf2d201d6f10..75a6190d6960c5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/MaxComputeExternalCatalog.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/MaxComputeExternalCatalog.java @@ -28,6 +28,7 @@ import org.apache.doris.transaction.TransactionManagerFactory; import com.aliyun.odps.Odps; +import com.aliyun.odps.OdpsException; import com.aliyun.odps.Partition; import com.aliyun.odps.account.AccountFormat; import com.aliyun.odps.table.TableIdentifier; @@ -52,7 +53,6 @@ public class MaxComputeExternalCatalog extends ExternalCatalog { // you can ref : https://help.aliyun.com/zh/maxcompute/user-guide/endpoints private static final String endpointTemplate = "http://service.{}.maxcompute.aliyun-inc.com/api"; - private Map props; private Odps odps; private String endpoint; @@ -206,6 +206,9 @@ protected void initLocalObjectsImpl() { odps = MCUtils.createMcClient(props); odps.setDefaultProject(defaultProject); odps.setEndpoint(endpoint); + odps.getRestClient().setConnectTimeout(connectTimeout); + odps.getRestClient().setReadTimeout(readTimeout); + odps.getRestClient().setRetryTimes(retryTimes); String accountFormatProp = props.getOrDefault(MCProperties.ACCOUNT_FORMAT, MCProperties.DEFAULT_ACCOUNT_FORMAT); if (accountFormatProp.equals(MCProperties.ACCOUNT_FORMAT_NAME)) { @@ -233,6 +236,69 @@ protected void initLocalObjectsImpl() { transactionManager = TransactionManagerFactory.createMCTransactionManager(this); } + @Override + public void checkWhenCreating() throws DdlException { + boolean testConnection = Boolean.parseBoolean(catalogProperty.getOrDefault(TEST_CONNECTION, + String.valueOf(DEFAULT_TEST_CONNECTION))); + if (!testConnection) { + return; + } + // MaxCompute has no MetastoreProperties-backed connectivity tester yet, + // so run its catalog-specific test directly under the common test_connection switch. + boolean enableNamespaceSchema = Boolean.parseBoolean( + catalogProperty.getOrDefault(MCProperties.ENABLE_NAMESPACE_SCHEMA, + MCProperties.DEFAULT_ENABLE_NAMESPACE_SCHEMA)); + try { + initLocalObjects(); + validateMaxComputeConnection(enableNamespaceSchema); + } catch (Exception e) { + throw new DdlException(e.getMessage(), e); + } + } + + protected void validateMaxComputeConnection(boolean enableNamespaceSchema) { + if (enableNamespaceSchema) { + validateMaxComputeProjectAndNamespaceSchema(); + } else { + validateMaxComputeProject(); + } + } + + private void validateMaxComputeProject() { + boolean projectExists; + try { + projectExists = maxComputeProjectExists(defaultProject); + } catch (Exception e) { + throw new RuntimeException("Failed to validate MaxCompute project '" + defaultProject + + "'. Check " + MCProperties.PROJECT + ", " + MCProperties.ENDPOINT + + " and credentials. Cause: " + e.getMessage(), e); + } + if (!projectExists) { + throw new RuntimeException("Failed to validate MaxCompute project '" + defaultProject + + "'. Check " + MCProperties.PROJECT + ", " + MCProperties.ENDPOINT + + " and credentials. Cause: project does not exist or is not accessible"); + } + } + + private void validateMaxComputeProjectAndNamespaceSchema() { + try { + validateMaxComputeNamespaceSchemaAccess(defaultProject); + } catch (Exception e) { + throw new RuntimeException("Failed to validate MaxCompute project '" + defaultProject + + "' with namespace schema. Check " + MCProperties.PROJECT + ", " + MCProperties.ENDPOINT + + ", credentials, and whether the schema list is accessible for the namespace schema " + + "configuration. Cause: " + e.getMessage(), e); + } + } + + protected boolean maxComputeProjectExists(String projectName) throws OdpsException { + return odps.projects().exists(projectName); + } + + protected void validateMaxComputeNamespaceSchemaAccess(String projectName) throws OdpsException { + odps.schemas().iterator(projectName).hasNext(); + } + public Odps getClient() { makeSureInitialized(); return odps; @@ -401,7 +467,7 @@ public void checkProperties() throws DdlException { MCProperties.DEFAULT_SPLIT_BYTE_SIZE)); if (splitByteSize < 10485760L) { - throw new DdlException(MCProperties.SPLIT_ROW_COUNT + " must be greater than or equal to 10485760"); + throw new DdlException(MCProperties.SPLIT_BYTE_SIZE + " must be greater than or equal to 10485760"); } } else if (splitStrategy.equals(MCProperties.SPLIT_BY_ROW_COUNT_STRATEGY)) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/MaxComputeExternalTable.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/MaxComputeExternalTable.java index 839995ca5f61e4..ec6e7f79d6df83 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/MaxComputeExternalTable.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/MaxComputeExternalTable.java @@ -54,6 +54,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -328,6 +329,16 @@ public Table getOdpsTable() { .orElse(null); } + public boolean isUnsupportedOdpsTable() { + Table odpsTable = getOdpsTable(); + return isUnsupportedOdpsTable(odpsTable); + } + + public static boolean isUnsupportedOdpsTable(Table odpsTable) { + Objects.requireNonNull(odpsTable, "MaxCompute table metadata is not initialized"); + return odpsTable.isExternalTable() || odpsTable.isVirtualView(); + } + @Override public boolean isPartitionedTable() { makeSureInitialized(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/source/MaxComputeScanNode.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/source/MaxComputeScanNode.java index d79e43aed74a3e..ae297d99c441e4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/source/MaxComputeScanNode.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/maxcompute/source/MaxComputeScanNode.java @@ -707,6 +707,11 @@ public List getSplits(int numBackends) throws UserException { long getOdpsTableTime = System.currentTimeMillis(); LOG.info("MaxComputeScanNode getSplits: getOdpsTable cost {} ms", getOdpsTableTime - startTime); + if (MaxComputeExternalTable.isUnsupportedOdpsTable(odpsTable)) { + throw new UserException("Reading MaxCompute external table or logical view is not supported: " + + table.getDbName() + "." + table.getName()); + } + if (desc.getSlots().isEmpty() || odpsTable.getFileNum() <= 0) { return result; } diff --git a/fe/fe-core/src/test/java/org/apache/doris/datasource/maxcompute/MCTransactionTest.java b/fe/fe-core/src/test/java/org/apache/doris/datasource/maxcompute/MCTransactionTest.java new file mode 100644 index 00000000000000..e76f192a858917 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/datasource/maxcompute/MCTransactionTest.java @@ -0,0 +1,54 @@ +// 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.doris.datasource.maxcompute; + +import org.apache.doris.common.UserException; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Optional; + +public class MCTransactionTest { + @Test + public void testBeginInsertRejectsOdpsExternalTable() { + assertBeginInsertRejectsUnsupportedOdpsTable("mc_external_table"); + } + + @Test + public void testBeginInsertRejectsOdpsLogicalView() { + assertBeginInsertRejectsUnsupportedOdpsTable("mc_logical_view"); + } + + private void assertBeginInsertRejectsUnsupportedOdpsTable(String tableName) { + MaxComputeExternalCatalog catalog = Mockito.mock(MaxComputeExternalCatalog.class); + MaxComputeExternalTable table = Mockito.mock(MaxComputeExternalTable.class); + Mockito.when(table.isUnsupportedOdpsTable()).thenReturn(true); + Mockito.when(table.getDbName()).thenReturn("default"); + Mockito.when(table.getName()).thenReturn(tableName); + + MCTransaction transaction = new MCTransaction(catalog); + + UserException exception = Assert.assertThrows(UserException.class, + () -> transaction.beginInsert(table, Optional.empty())); + Assert.assertTrue(exception.getMessage().contains( + "Writing MaxCompute external table or logical view is not supported: default." + tableName)); + Mockito.verify(catalog, Mockito.never()).getOdpsTableIdentifier(Mockito.anyString(), Mockito.anyString()); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/datasource/maxcompute/MaxComputeExternalCatalogTest.java b/fe/fe-core/src/test/java/org/apache/doris/datasource/maxcompute/MaxComputeExternalCatalogTest.java new file mode 100644 index 00000000000000..dfe22f136b5ca4 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/datasource/maxcompute/MaxComputeExternalCatalogTest.java @@ -0,0 +1,146 @@ +// 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.doris.datasource.maxcompute; + +import org.apache.doris.common.DdlException; +import org.apache.doris.common.maxcompute.MCProperties; +import org.apache.doris.datasource.ExternalCatalog; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class MaxComputeExternalCatalogTest { + @Test + public void testSplitByteSizeErrorMessage() { + Map props = new HashMap<>(); + addRequiredProperties(props); + props.put(MCProperties.SPLIT_STRATEGY, MCProperties.SPLIT_BY_BYTE_SIZE_STRATEGY); + props.put(MCProperties.SPLIT_BYTE_SIZE, "1048576"); + + MaxComputeExternalCatalog catalog = new MaxComputeExternalCatalog(1L, "mc_catalog", null, props, ""); + + DdlException exception = Assert.assertThrows(DdlException.class, catalog::checkProperties); + Assert.assertTrue(exception.getMessage().contains( + MCProperties.SPLIT_BYTE_SIZE + " must be greater than or equal to 10485760")); + Assert.assertFalse(exception.getMessage().contains(MCProperties.SPLIT_ROW_COUNT)); + } + + @Test + public void testCheckWhenCreatingSkipsValidationByDefault() throws DdlException { + Map props = createRequiredProperties(true); + TestMaxComputeExternalCatalog catalog = new TestMaxComputeExternalCatalog(props); + + catalog.checkWhenCreating(); + + Assert.assertNull(catalog.checkedProjectName); + Assert.assertNull(catalog.checkedNamespaceSchemaProjectName); + } + + @Test + public void testCheckWhenCreatingValidatesProjectWhenValidationEnabled() throws DdlException { + Map props = createRequiredProperties(false); + props.put(ExternalCatalog.TEST_CONNECTION, "true"); + TestMaxComputeExternalCatalog catalog = new TestMaxComputeExternalCatalog(props); + + catalog.checkWhenCreating(); + + Assert.assertEquals("mc_project", catalog.checkedProjectName); + Assert.assertNull(catalog.checkedNamespaceSchemaProjectName); + } + + @Test + public void testCheckWhenCreatingValidatesSchemaWhenNamespaceSchemaEnabled() throws DdlException { + Map props = createRequiredProperties(true); + props.put(ExternalCatalog.TEST_CONNECTION, "true"); + TestMaxComputeExternalCatalog catalog = new TestMaxComputeExternalCatalog(props); + + catalog.checkWhenCreating(); + + Assert.assertNull(catalog.checkedProjectName); + Assert.assertEquals("mc_project", catalog.checkedNamespaceSchemaProjectName); + } + + @Test + public void testCheckWhenCreatingReportsInaccessibleProject() { + Map props = createRequiredProperties(false); + props.put(ExternalCatalog.TEST_CONNECTION, "true"); + TestMaxComputeExternalCatalog catalog = new TestMaxComputeExternalCatalog(props); + catalog.projectExists = false; + + DdlException exception = Assert.assertThrows(DdlException.class, catalog::checkWhenCreating); + + Assert.assertTrue(exception.getMessage().contains("Failed to validate MaxCompute project 'mc_project'")); + Assert.assertTrue(exception.getMessage().contains("does not exist or is not accessible")); + Assert.assertNull(catalog.checkedNamespaceSchemaProjectName); + } + + @Test + public void testCheckWhenCreatingReportsInaccessibleNamespaceSchema() { + Map props = createRequiredProperties(true); + props.put(ExternalCatalog.TEST_CONNECTION, "true"); + TestMaxComputeExternalCatalog catalog = new TestMaxComputeExternalCatalog(props); + catalog.threeTierModel = false; + + DdlException exception = Assert.assertThrows(DdlException.class, catalog::checkWhenCreating); + + Assert.assertTrue(exception.getMessage().contains("Failed to validate MaxCompute project 'mc_project'")); + Assert.assertTrue(exception.getMessage().contains("schema list is accessible")); + } + + private static Map createRequiredProperties(boolean enableNamespaceSchema) { + Map props = new HashMap<>(); + addRequiredProperties(props); + props.put(MCProperties.ENABLE_NAMESPACE_SCHEMA, Boolean.toString(enableNamespaceSchema)); + return props; + } + + private static void addRequiredProperties(Map props) { + props.put(MCProperties.PROJECT, "mc_project"); + props.put(MCProperties.ENDPOINT, "http://service.cn-beijing.maxcompute.aliyun-inc.com/api"); + props.put(MCProperties.ACCESS_KEY, "access_key"); + props.put(MCProperties.SECRET_KEY, "secret_key"); + } + + private static class TestMaxComputeExternalCatalog extends MaxComputeExternalCatalog { + private boolean projectExists = true; + private boolean threeTierModel = true; + private String checkedProjectName; + private String checkedNamespaceSchemaProjectName; + + private TestMaxComputeExternalCatalog(Map props) { + super(1L, "mc_catalog", null, props, ""); + } + + @Override + protected boolean maxComputeProjectExists(String projectName) { + checkedProjectName = projectName; + return projectExists; + } + + @Override + protected void validateMaxComputeNamespaceSchemaAccess(String projectName) { + checkedNamespaceSchemaProjectName = projectName; + if (!threeTierModel) { + throw new RuntimeException("schema list is not accessible"); + } + } + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/datasource/maxcompute/source/MaxComputeScanNodeTest.java b/fe/fe-core/src/test/java/org/apache/doris/datasource/maxcompute/source/MaxComputeScanNodeTest.java index cd3f30b0f5d3a0..4989c2c53f21cb 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/datasource/maxcompute/source/MaxComputeScanNodeTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/datasource/maxcompute/source/MaxComputeScanNodeTest.java @@ -27,6 +27,7 @@ import org.apache.doris.analysis.TupleId; import org.apache.doris.catalog.Column; import org.apache.doris.catalog.PrimitiveType; +import org.apache.doris.common.UserException; import org.apache.doris.datasource.maxcompute.MaxComputeExternalCatalog; import org.apache.doris.datasource.maxcompute.MaxComputeExternalTable; import org.apache.doris.datasource.maxcompute.source.MaxComputeSplit.SplitType; @@ -436,4 +437,27 @@ public void testGetSplits_noLimit_normalPath() throws Exception { Assert.assertFalse(result.isEmpty()); } + + @Test + public void testGetSplitsRejectsOdpsExternalTable() { + assertGetSplitsRejectsUnsupportedOdpsTable(true, false, "mc_external_table"); + } + + @Test + public void testGetSplitsRejectsOdpsLogicalView() { + assertGetSplitsRejectsUnsupportedOdpsTable(false, true, "mc_logical_view"); + } + + private void assertGetSplitsRejectsUnsupportedOdpsTable(boolean isExternalTable, boolean isVirtualView, + String tableName) { + Mockito.when(odpsTable.isExternalTable()).thenReturn(isExternalTable); + Mockito.when(odpsTable.isVirtualView()).thenReturn(isVirtualView); + Mockito.when(table.getDbName()).thenReturn("default"); + Mockito.when(table.getName()).thenReturn(tableName); + + UserException exception = Assert.assertThrows(UserException.class, () -> node.getSplits(1)); + Assert.assertTrue(exception.getMessage().contains( + "Reading MaxCompute external table or logical view is not supported: default." + tableName)); + Mockito.verify(odpsTable, Mockito.never()).getFileNum(); + } } diff --git a/fe/pom.xml b/fe/pom.xml index c5e88ad3d5a217..0b6802887c016d 100644 --- a/fe/pom.xml +++ b/fe/pom.xml @@ -1435,7 +1435,7 @@ under the License. ${maxcompute.version} - + com.aliyun tea 1.4.1 diff --git a/regression-test/suites/external_table_p2/maxcompute/test_external_catalog_maxcompute.groovy b/regression-test/suites/external_table_p2/maxcompute/test_external_catalog_maxcompute.groovy index 6c83b7735d4ce3..9f434da244fca3 100644 --- a/regression-test/suites/external_table_p2/maxcompute/test_external_catalog_maxcompute.groovy +++ b/regression-test/suites/external_table_p2/maxcompute/test_external_catalog_maxcompute.groovy @@ -341,7 +341,8 @@ suite("test_external_catalog_maxcompute", "p2,external") { "mc.default.project" = "${mc_db}", "mc.access_key" = "${ak}", "mc.secret_key" = "${sk}", - "mc.endpoint" = "http://service.cn-beijing-vpc.maxcompute.aliyun-inc.com/api" + "mc.endpoint" = "http://service.cn-beijing-vpc.maxcompute.aliyun-inc.com/api", + "test_connection" = "true" ); """ @@ -379,7 +380,8 @@ suite("test_external_catalog_maxcompute", "p2,external") { "mc.default.project" = "${mc_db}", "mc.access_key" = "${ak}", "mc.secret_key" = "${sk}", - "mc.endpoint" = "http://service.cn-beijing-vpc.maxcompute.aliyun-inc.com/api" + "mc.endpoint" = "http://service.cn-beijing-vpc.maxcompute.aliyun-inc.com/api", + "test_connection" = "true" ); """ sql """ switch `${mc_catalog_name}`; """ diff --git a/regression-test/suites/external_table_p2/maxcompute/test_max_compute_schema.groovy b/regression-test/suites/external_table_p2/maxcompute/test_max_compute_schema.groovy index 75d278e9fadb5d..7f8f3bce332498 100644 --- a/regression-test/suites/external_table_p2/maxcompute/test_max_compute_schema.groovy +++ b/regression-test/suites/external_table_p2/maxcompute/test_max_compute_schema.groovy @@ -106,7 +106,8 @@ suite("test_max_compute_schema", "p2,external") { "mc.access_key" = "${ak}", "mc.secret_key" = "${sk}", "mc.endpoint" = "http://service.cn-beijing-vpc.maxcompute.aliyun-inc.com/api", - "mc.enable.namespace.schema" = "true" + "mc.enable.namespace.schema" = "true", + "test_connection" = "true" ); """ @@ -236,4 +237,4 @@ suite("test_max_compute_schema", "p2,external") { qt_mc_join_q34 """SELECT p.product_name, e.department, e.salary FROM analytics.product_sales p JOIN iot.employee_salary e ON p.id=e.id ORDER BY p.id;""" qt_mc_join_q35 """SELECT o.order_id, p.product_name, e.emp_name FROM `default`.order_detail o JOIN analytics.product_sales p ON o.id=p.id JOIN iot.employee_salary e ON o.id=e.id WHERE o.ds1='202510' ORDER BY o.id;""" } -} \ No newline at end of file +} diff --git a/regression-test/suites/external_table_p2/maxcompute/test_max_compute_validate_connection.groovy b/regression-test/suites/external_table_p2/maxcompute/test_max_compute_validate_connection.groovy new file mode 100644 index 00000000000000..d88e65a2340216 --- /dev/null +++ b/regression-test/suites/external_table_p2/maxcompute/test_max_compute_validate_connection.groovy @@ -0,0 +1,102 @@ +// 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. + +suite("test_max_compute_validate_connection", "p2,external") { + String enabled = context.config.otherConfigs.get("enableMaxComputeTest") + if (enabled == null || !enabled.equalsIgnoreCase("true")) { + logger.info("disable MaxCompute test.") + return + } + + String ak = context.config.otherConfigs.get("ak") + String sk = context.config.otherConfigs.get("sk") + String defaultProject = "mc_datalake" + String invalidEndpoint = "http://127.0.0.1:1/api" + String catalogDefaultFalse = "test_mc_validate_default_false" + String catalogExplicitFalse = "test_mc_validate_explicit_false" + String catalogValidateProject = "test_mc_validate_project" + String catalogValidateSchema = "test_mc_validate_schema" + + sql """drop catalog if exists ${catalogDefaultFalse}""" + sql """drop catalog if exists ${catalogExplicitFalse}""" + sql """drop catalog if exists ${catalogValidateProject}""" + sql """drop catalog if exists ${catalogValidateSchema}""" + + sql """ + create catalog ${catalogDefaultFalse} properties ( + "type" = "max_compute", + "mc.default.project" = "${defaultProject}", + "mc.access_key" = "${ak}", + "mc.secret_key" = "${sk}", + "mc.endpoint" = "${invalidEndpoint}", + "mc.connect_timeout" = "1", + "mc.read_timeout" = "1", + "mc.retry_count" = "1" + ); + """ + sql """drop catalog if exists ${catalogDefaultFalse}""" + + sql """ + create catalog ${catalogExplicitFalse} properties ( + "type" = "max_compute", + "mc.default.project" = "${defaultProject}", + "mc.access_key" = "${ak}", + "mc.secret_key" = "${sk}", + "mc.endpoint" = "${invalidEndpoint}", + "mc.connect_timeout" = "1", + "mc.read_timeout" = "1", + "mc.retry_count" = "1", + "test_connection" = "false" + ); + """ + sql """drop catalog if exists ${catalogExplicitFalse}""" + + test { + sql """ + create catalog ${catalogValidateProject} properties ( + "type" = "max_compute", + "mc.default.project" = "${defaultProject}", + "mc.access_key" = "${ak}", + "mc.secret_key" = "${sk}", + "mc.endpoint" = "${invalidEndpoint}", + "mc.connect_timeout" = "1", + "mc.read_timeout" = "1", + "mc.retry_count" = "1", + "test_connection" = "true" + ); + """ + exception "Failed to validate MaxCompute project" + } + + test { + sql """ + create catalog ${catalogValidateSchema} properties ( + "type" = "max_compute", + "mc.default.project" = "${defaultProject}", + "mc.access_key" = "${ak}", + "mc.secret_key" = "${sk}", + "mc.endpoint" = "${invalidEndpoint}", + "mc.connect_timeout" = "1", + "mc.read_timeout" = "1", + "mc.retry_count" = "1", + "mc.enable.namespace.schema" = "true", + "test_connection" = "true" + ); + """ + exception "with namespace schema" + } +}