diff --git a/plugin/trino-memsql/pom.xml b/plugin/trino-memsql/pom.xml
index e75be50df81e..02cc5f36fb14 100644
--- a/plugin/trino-memsql/pom.xml
+++ b/plugin/trino-memsql/pom.xml
@@ -61,7 +61,6 @@
org.mariadb.jdbc
mariadb-java-client
- 2.7.2
diff --git a/plugin/trino-mysql/pom.xml b/plugin/trino-mysql/pom.xml
index af55465b9d74..997b87ccf091 100644
--- a/plugin/trino-mysql/pom.xml
+++ b/plugin/trino-mysql/pom.xml
@@ -160,6 +160,19 @@
test
+
+
+ org.mariadb.jdbc
+ mariadb-java-client
+ test
+
+
+
+ org.testcontainers
+ mariadb
+ test
+
+
org.testcontainers
mysql
diff --git a/plugin/trino-mysql/src/main/java/io/trino/plugin/mysql/MySqlPlugin.java b/plugin/trino-mysql/src/main/java/io/trino/plugin/mysql/MySqlPlugin.java
index 2f13de05a9c5..688eb8208570 100644
--- a/plugin/trino-mysql/src/main/java/io/trino/plugin/mysql/MySqlPlugin.java
+++ b/plugin/trino-mysql/src/main/java/io/trino/plugin/mysql/MySqlPlugin.java
@@ -13,13 +13,22 @@
*/
package io.trino.plugin.mysql;
-import io.trino.plugin.jdbc.JdbcPlugin;
+import com.google.common.collect.ImmutableList;
+import io.trino.plugin.jdbc.JdbcConnectorFactory;
+import io.trino.plugin.jdbc.credential.CredentialProviderModule;
+import io.trino.spi.Plugin;
+import io.trino.spi.connector.ConnectorFactory;
+
+import static io.airlift.configuration.ConfigurationAwareModule.combine;
public class MySqlPlugin
- extends JdbcPlugin
+ implements Plugin
{
- public MySqlPlugin()
+ @Override
+ public Iterable getConnectorFactories()
{
- super("mysql", new MySqlClientModule());
+ return ImmutableList.of(
+ new JdbcConnectorFactory("mysql", combine(new CredentialProviderModule(), new MySqlClientModule())),
+ new JdbcConnectorFactory("mariadb", combine(new CredentialProviderModule(), new MySqlClientModule())));
}
}
diff --git a/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/BaseMySqlConnectorTest.java b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/BaseMySqlConnectorTest.java
index 3629026fa8e7..4651a80ee4ad 100644
--- a/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/BaseMySqlConnectorTest.java
+++ b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/BaseMySqlConnectorTest.java
@@ -37,7 +37,6 @@
import static io.trino.SystemSessionProperties.USE_MARK_DISTINCT;
import static io.trino.spi.type.VarcharType.VARCHAR;
import static io.trino.testing.MaterializedResult.resultBuilder;
-import static io.trino.testing.TestingSession.testSessionBuilder;
import static io.trino.testing.assertions.Assert.assertEquals;
import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
@@ -169,7 +168,7 @@ public void testDescribeTable()
public void testShowCreateTable()
{
assertThat(computeActual("SHOW CREATE TABLE orders").getOnlyValue())
- .isEqualTo("CREATE TABLE mysql.tpch.orders (\n" +
+ .isEqualTo("CREATE TABLE " + getSession().getCatalog().orElseThrow() + ".tpch.orders (\n" +
" orderkey bigint,\n" +
" custkey bigint,\n" +
" orderstatus varchar(255),\n" +
@@ -213,43 +212,38 @@ public void testInsert()
@Test
public void testNameEscaping()
{
- Session session = testSessionBuilder()
- .setCatalog("mysql")
- .setSchema(getSession().getSchema().get())
- .build();
-
- assertFalse(getQueryRunner().tableExists(session, "test_table"));
+ assertFalse(getQueryRunner().tableExists(getSession(), "test_table"));
- assertUpdate(session, "CREATE TABLE test_table AS SELECT 123 x", 1);
- assertTrue(getQueryRunner().tableExists(session, "test_table"));
+ assertUpdate("CREATE TABLE test_table AS SELECT 123 x", 1);
+ assertTrue(getQueryRunner().tableExists(getSession(), "test_table"));
- assertQuery(session, "SELECT * FROM test_table", "SELECT 123");
+ assertQuery("SELECT * FROM test_table", "SELECT 123");
- assertUpdate(session, "DROP TABLE test_table");
- assertFalse(getQueryRunner().tableExists(session, "test_table"));
+ assertUpdate("DROP TABLE test_table");
+ assertFalse(getQueryRunner().tableExists(getSession(), "test_table"));
}
@Test
- public void testMySqlTinyint()
+ public void testTinyint()
{
- onRemoteDatabase().execute("CREATE TABLE tpch.mysql_test_tinyint1 (c_tinyint tinyint(1))");
+ onRemoteDatabase().execute("CREATE TABLE tpch.test_tinyint1 (c_tinyint tinyint(1))");
- MaterializedResult actual = computeActual("SHOW COLUMNS FROM mysql_test_tinyint1");
+ MaterializedResult actual = computeActual("SHOW COLUMNS FROM test_tinyint1");
MaterializedResult expected = MaterializedResult.resultBuilder(getSession(), VARCHAR, VARCHAR, VARCHAR, VARCHAR)
.row("c_tinyint", "tinyint", "", "")
.build();
assertEquals(actual, expected);
- onRemoteDatabase().execute("INSERT INTO tpch.mysql_test_tinyint1 VALUES (127), (-128)");
- MaterializedResult materializedRows = computeActual("SELECT * FROM tpch.mysql_test_tinyint1 WHERE c_tinyint = 127");
+ onRemoteDatabase().execute("INSERT INTO tpch.test_tinyint1 VALUES (127), (-128)");
+ MaterializedResult materializedRows = computeActual("SELECT * FROM tpch.test_tinyint1 WHERE c_tinyint = 127");
assertEquals(materializedRows.getRowCount(), 1);
MaterializedRow row = getOnlyElement(materializedRows);
assertEquals(row.getFields().size(), 1);
assertEquals(row.getField(0), (byte) 127);
- assertUpdate("DROP TABLE mysql_test_tinyint1");
+ assertUpdate("DROP TABLE test_tinyint1");
}
@Test
diff --git a/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/BaseMySqlTypeMappingTest.java b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/BaseMySqlTypeMappingTest.java
new file mode 100644
index 000000000000..e60b68c5c24b
--- /dev/null
+++ b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/BaseMySqlTypeMappingTest.java
@@ -0,0 +1,876 @@
+/*
+ * 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.plugin.mysql;
+
+import com.google.common.collect.ImmutableList;
+import io.trino.Session;
+import io.trino.plugin.jdbc.UnsupportedTypeHandling;
+import io.trino.spi.type.BigintType;
+import io.trino.spi.type.DoubleType;
+import io.trino.spi.type.IntegerType;
+import io.trino.spi.type.RealType;
+import io.trino.spi.type.SmallintType;
+import io.trino.spi.type.TimeZoneKey;
+import io.trino.spi.type.VarcharType;
+import io.trino.testing.AbstractTestQueryFramework;
+import io.trino.testing.TestingSession;
+import io.trino.testing.datatype.CreateAndInsertDataSetup;
+import io.trino.testing.datatype.CreateAsSelectDataSetup;
+import io.trino.testing.datatype.DataSetup;
+import io.trino.testing.datatype.DataType;
+import io.trino.testing.datatype.DataTypeTest;
+import io.trino.testing.datatype.SqlDataTypeTest;
+import io.trino.testing.sql.SqlExecutor;
+import io.trino.testing.sql.TestTable;
+import io.trino.testing.sql.TrinoSqlExecutor;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.Objects;
+import java.util.function.Function;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.io.BaseEncoding.base16;
+import static io.trino.plugin.jdbc.DecimalConfig.DecimalMapping.ALLOW_OVERFLOW;
+import static io.trino.plugin.jdbc.DecimalConfig.DecimalMapping.STRICT;
+import static io.trino.plugin.jdbc.DecimalSessionSessionProperties.DECIMAL_DEFAULT_SCALE;
+import static io.trino.plugin.jdbc.DecimalSessionSessionProperties.DECIMAL_MAPPING;
+import static io.trino.plugin.jdbc.DecimalSessionSessionProperties.DECIMAL_ROUNDING_MODE;
+import static io.trino.plugin.jdbc.TypeHandlingJdbcSessionProperties.UNSUPPORTED_TYPE_HANDLING;
+import static io.trino.plugin.jdbc.UnsupportedTypeHandling.CONVERT_TO_VARCHAR;
+import static io.trino.spi.type.DecimalType.createDecimalType;
+import static io.trino.spi.type.TimeZoneKey.UTC_KEY;
+import static io.trino.spi.type.TimestampType.TIMESTAMP_MILLIS;
+import static io.trino.spi.type.TimestampType.createTimestampType;
+import static io.trino.spi.type.VarbinaryType.VARBINARY;
+import static io.trino.spi.type.VarcharType.createUnboundedVarcharType;
+import static io.trino.spi.type.VarcharType.createVarcharType;
+import static io.trino.testing.datatype.DataType.bigintDataType;
+import static io.trino.testing.datatype.DataType.charDataType;
+import static io.trino.testing.datatype.DataType.dataType;
+import static io.trino.testing.datatype.DataType.dateDataType;
+import static io.trino.testing.datatype.DataType.decimalDataType;
+import static io.trino.testing.datatype.DataType.doubleDataType;
+import static io.trino.testing.datatype.DataType.formatStringLiteral;
+import static io.trino.testing.datatype.DataType.integerDataType;
+import static io.trino.testing.datatype.DataType.realDataType;
+import static io.trino.testing.datatype.DataType.smallintDataType;
+import static io.trino.testing.datatype.DataType.stringDataType;
+import static io.trino.testing.datatype.DataType.tinyintDataType;
+import static io.trino.testing.datatype.DataType.varcharDataType;
+import static io.trino.type.JsonType.JSON;
+import static java.lang.String.format;
+import static java.math.RoundingMode.HALF_UP;
+import static java.math.RoundingMode.UNNECESSARY;
+import static java.time.ZoneOffset.UTC;
+import static java.util.Arrays.asList;
+import static java.util.function.Function.identity;
+
+public abstract class BaseMySqlTypeMappingTest
+ extends AbstractTestQueryFramework
+{
+ private static final String CHARACTER_SET_UTF8 = "CHARACTER SET utf8";
+
+ @Test
+ public void testBasicTypes()
+ {
+ DataTypeTest.create()
+ .addRoundTrip(bigintDataType(), 123_456_789_012L)
+ .addRoundTrip(integerDataType(), 1_234_567_890)
+ .addRoundTrip(smallintDataType(), (short) 32_456)
+ .addRoundTrip(tinyintDataType(), (byte) 125)
+ .addRoundTrip(doubleDataType(), 123.45d)
+ .addRoundTrip(realDataType(), 123.45f)
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_basic_types"));
+ }
+
+ @Test
+ public void testTrinoCreatedParameterizedVarchar()
+ {
+ DataTypeTest.create()
+ .addRoundTrip(stringDataType("varchar(10)", createVarcharType(255)), "text_a")
+ .addRoundTrip(stringDataType("varchar(255)", createVarcharType(255)), "text_b")
+ .addRoundTrip(stringDataType("varchar(256)", createVarcharType(65535)), "text_c")
+ .addRoundTrip(stringDataType("varchar(65535)", createVarcharType(65535)), "text_d")
+ .addRoundTrip(stringDataType("varchar(65536)", createVarcharType(16777215)), "text_e")
+ .addRoundTrip(stringDataType("varchar(16777215)", createVarcharType(16777215)), "text_f")
+ .addRoundTrip(stringDataType("varchar(16777216)", createUnboundedVarcharType()), "text_g")
+ .addRoundTrip(stringDataType("varchar(" + VarcharType.MAX_LENGTH + ")", createUnboundedVarcharType()), "text_h")
+ .addRoundTrip(stringDataType("varchar", createUnboundedVarcharType()), "unbounded")
+ .execute(getQueryRunner(), trinoCreateAsSelect("trino__test_parameterized_varchar"));
+ }
+
+ @Test
+ public void testMySqlCreatedParameterizedVarchar()
+ {
+ DataTypeTest.create()
+ .addRoundTrip(stringDataType("tinytext", createVarcharType(255)), "a")
+ .addRoundTrip(stringDataType("text", createVarcharType(65535)), "b")
+ .addRoundTrip(stringDataType("mediumtext", createVarcharType(16777215)), "c")
+ .addRoundTrip(stringDataType("longtext", createUnboundedVarcharType()), "d")
+ .addRoundTrip(varcharDataType(32), "e")
+ .addRoundTrip(varcharDataType(15000), "f")
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_parameterized_varchar"));
+ }
+
+ @Test
+ public void testMySqlCreatedParameterizedVarcharUnicode()
+ {
+ String sampleUnicodeText = "\u653b\u6bbb\u6a5f\u52d5\u968a";
+ DataTypeTest.create()
+ .addRoundTrip(stringDataType("tinytext " + CHARACTER_SET_UTF8, createVarcharType(255)), sampleUnicodeText)
+ .addRoundTrip(stringDataType("text " + CHARACTER_SET_UTF8, createVarcharType(65535)), sampleUnicodeText)
+ .addRoundTrip(stringDataType("mediumtext " + CHARACTER_SET_UTF8, createVarcharType(16777215)), sampleUnicodeText)
+ .addRoundTrip(stringDataType("longtext " + CHARACTER_SET_UTF8, createUnboundedVarcharType()), sampleUnicodeText)
+ .addRoundTrip(varcharDataType(sampleUnicodeText.length(), CHARACTER_SET_UTF8), sampleUnicodeText)
+ .addRoundTrip(varcharDataType(32, CHARACTER_SET_UTF8), sampleUnicodeText)
+ .addRoundTrip(varcharDataType(20000, CHARACTER_SET_UTF8), sampleUnicodeText)
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_parameterized_varchar_unicode"));
+ }
+
+ @Test
+ public void testTrinoCreatedParameterizedChar()
+ {
+ mysqlCharTypeTest()
+ .execute(getQueryRunner(), trinoCreateAsSelect("mysql_test_parameterized_char"));
+ }
+
+ @Test
+ public void testMySqlCreatedParameterizedChar()
+ {
+ mysqlCharTypeTest()
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_parameterized_char"));
+ }
+
+ private DataTypeTest mysqlCharTypeTest()
+ {
+ return DataTypeTest.create()
+ .addRoundTrip(charDataType("char", 1), "")
+ .addRoundTrip(charDataType("char", 1), "a")
+ .addRoundTrip(charDataType(1), "")
+ .addRoundTrip(charDataType(1), "a")
+ .addRoundTrip(charDataType(8), "abc")
+ .addRoundTrip(charDataType(8), "12345678")
+ .addRoundTrip(charDataType(255), "a".repeat(255));
+ }
+
+ @Test
+ public void testMySqlCreatedParameterizedCharUnicode()
+ {
+ DataTypeTest.create()
+ .addRoundTrip(charDataType(1, CHARACTER_SET_UTF8), "\u653b")
+ .addRoundTrip(charDataType(5, CHARACTER_SET_UTF8), "\u653b\u6bbb")
+ .addRoundTrip(charDataType(5, CHARACTER_SET_UTF8), "\u653b\u6bbb\u6a5f\u52d5\u968a")
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_parameterized_varchar"));
+ }
+
+ @Test
+ public void testMysqlCreatedDecimal()
+ {
+ decimalTests()
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_decimal"));
+ }
+
+ @Test
+ public void testTrinoCreatedDecimal()
+ {
+ decimalTests()
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_decimal"));
+ }
+
+ private DataTypeTest decimalTests()
+ {
+ return DataTypeTest.create()
+ .addRoundTrip(decimalDataType(3, 0), new BigDecimal("193"))
+ .addRoundTrip(decimalDataType(3, 0), new BigDecimal("19"))
+ .addRoundTrip(decimalDataType(3, 0), new BigDecimal("-193"))
+ .addRoundTrip(decimalDataType(3, 1), new BigDecimal("10.0"))
+ .addRoundTrip(decimalDataType(3, 1), new BigDecimal("10.1"))
+ .addRoundTrip(decimalDataType(3, 1), new BigDecimal("-10.1"))
+ .addRoundTrip(decimalDataType(4, 2), new BigDecimal("2"))
+ .addRoundTrip(decimalDataType(4, 2), new BigDecimal("2.3"))
+ .addRoundTrip(decimalDataType(24, 2), new BigDecimal("2"))
+ .addRoundTrip(decimalDataType(24, 2), new BigDecimal("2.3"))
+ .addRoundTrip(decimalDataType(24, 2), new BigDecimal("123456789.3"))
+ .addRoundTrip(decimalDataType(24, 4), new BigDecimal("12345678901234567890.31"))
+ .addRoundTrip(decimalDataType(30, 5), new BigDecimal("3141592653589793238462643.38327"))
+ .addRoundTrip(decimalDataType(30, 5), new BigDecimal("-3141592653589793238462643.38327"))
+ .addRoundTrip(decimalDataType(38, 0), new BigDecimal("27182818284590452353602874713526624977"))
+ .addRoundTrip(decimalDataType(38, 0), new BigDecimal("-27182818284590452353602874713526624977"));
+ }
+
+ @Test
+ public void testDecimalExceedingPrecisionMax()
+ {
+ testUnsupportedDataType("decimal(50,0)");
+ }
+
+ @Test
+ public void testDecimalExceedingPrecisionMaxWithExceedingIntegerValues()
+ {
+ try (TestTable testTable = new TestTable(
+ onRemoteDatabase(),
+ "tpch.test_exceeding_max_decimal",
+ "(d_col decimal(65,25))",
+ asList("1234567890123456789012345678901234567890.123456789", "-1234567890123456789012345678901234567890.123456789"))) {
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 0),
+ format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
+ "VALUES ('d_col', 'decimal(38,0)')");
+ assertQueryFails(
+ sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 0),
+ "SELECT d_col FROM " + testTable.getName(),
+ "Rounding necessary");
+ assertQueryFails(
+ sessionWithDecimalMappingAllowOverflow(HALF_UP, 0),
+ "SELECT d_col FROM " + testTable.getName(),
+ "Decimal overflow");
+ assertQuery(
+ sessionWithDecimalMappingStrict(CONVERT_TO_VARCHAR),
+ format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
+ "VALUES ('d_col', 'varchar')");
+ assertQuery(
+ sessionWithDecimalMappingStrict(CONVERT_TO_VARCHAR),
+ "SELECT d_col FROM " + testTable.getName(),
+ "VALUES ('1234567890123456789012345678901234567890.1234567890000000000000000'), ('-1234567890123456789012345678901234567890.1234567890000000000000000')");
+ }
+ }
+
+ @Test
+ public void testDecimalExceedingPrecisionMaxWithNonExceedingIntegerValues()
+ {
+ try (TestTable testTable = new TestTable(
+ onRemoteDatabase(),
+ "tpch.test_exceeding_max_decimal",
+ "(d_col decimal(60,20))",
+ asList("123456789012345678901234567890.123456789012345", "-123456789012345678901234567890.123456789012345"))) {
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 0),
+ format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
+ "VALUES ('d_col', 'decimal(38,0)')");
+ assertQueryFails(
+ sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 0),
+ "SELECT d_col FROM " + testTable.getName(),
+ "Rounding necessary");
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(HALF_UP, 0),
+ "SELECT d_col FROM " + testTable.getName(),
+ "VALUES (123456789012345678901234567890), (-123456789012345678901234567890)");
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 8),
+ format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
+ "VALUES ('d_col', 'decimal(38,8)')");
+ assertQueryFails(
+ sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 8),
+ "SELECT d_col FROM " + testTable.getName(),
+ "Rounding necessary");
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(HALF_UP, 8),
+ "SELECT d_col FROM " + testTable.getName(),
+ "VALUES (123456789012345678901234567890.12345679), (-123456789012345678901234567890.12345679)");
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(HALF_UP, 22),
+ format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
+ "VALUES ('d_col', 'decimal(38,20)')");
+ assertQueryFails(
+ sessionWithDecimalMappingAllowOverflow(HALF_UP, 20),
+ "SELECT d_col FROM " + testTable.getName(),
+ "Decimal overflow");
+ assertQueryFails(
+ sessionWithDecimalMappingAllowOverflow(HALF_UP, 9),
+ "SELECT d_col FROM " + testTable.getName(),
+ "Decimal overflow");
+ assertQuery(
+ sessionWithDecimalMappingStrict(CONVERT_TO_VARCHAR),
+ format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
+ "VALUES ('d_col', 'varchar')");
+ assertQuery(
+ sessionWithDecimalMappingStrict(CONVERT_TO_VARCHAR),
+ "SELECT d_col FROM " + testTable.getName(),
+ "VALUES ('123456789012345678901234567890.12345678901234500000'), ('-123456789012345678901234567890.12345678901234500000')");
+ }
+ }
+
+ @Test(dataProvider = "testDecimalExceedingPrecisionMaxProvider")
+ public void testDecimalExceedingPrecisionMaxWithSupportedValues(int typePrecision, int typeScale)
+ {
+ try (TestTable testTable = new TestTable(
+ onRemoteDatabase(),
+ "tpch.test_exceeding_max_decimal",
+ format("(d_col decimal(%d,%d))", typePrecision, typeScale),
+ asList("12.01", "-12.01", "123", "-123", "1.12345678", "-1.12345678"))) {
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 0),
+ format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
+ "VALUES ('d_col', 'decimal(38,0)')");
+ assertQueryFails(
+ sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 0),
+ "SELECT d_col FROM " + testTable.getName(),
+ "Rounding necessary");
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(HALF_UP, 0),
+ "SELECT d_col FROM " + testTable.getName(),
+ "VALUES (12), (-12), (123), (-123), (1), (-1)");
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(HALF_UP, 3),
+ format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
+ "VALUES ('d_col', 'decimal(38,3)')");
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(HALF_UP, 3),
+ "SELECT d_col FROM " + testTable.getName(),
+ "VALUES (12.01), (-12.01), (123), (-123), (1.123), (-1.123)");
+ assertQueryFails(
+ sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 3),
+ "SELECT d_col FROM " + testTable.getName(),
+ "Rounding necessary");
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(HALF_UP, 8),
+ format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
+ "VALUES ('d_col', 'decimal(38,8)')");
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(HALF_UP, 8),
+ "SELECT d_col FROM " + testTable.getName(),
+ "VALUES (12.01), (-12.01), (123), (-123), (1.12345678), (-1.12345678)");
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(HALF_UP, 9),
+ "SELECT d_col FROM " + testTable.getName(),
+ "VALUES (12.01), (-12.01), (123), (-123), (1.12345678), (-1.12345678)");
+ assertQuery(
+ sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 8),
+ "SELECT d_col FROM " + testTable.getName(),
+ "VALUES (12.01), (-12.01), (123), (-123), (1.12345678), (-1.12345678)");
+ }
+ }
+
+ @DataProvider
+ public Object[][] testDecimalExceedingPrecisionMaxProvider()
+ {
+ return new Object[][] {
+ {40, 8},
+ {50, 10},
+ };
+ }
+
+ private Session sessionWithDecimalMappingAllowOverflow(RoundingMode roundingMode, int scale)
+ {
+ String catalog = getSession().getCatalog().orElseThrow();
+ return Session.builder(getSession())
+ .setCatalogSessionProperty(catalog, DECIMAL_MAPPING, ALLOW_OVERFLOW.name())
+ .setCatalogSessionProperty(catalog, DECIMAL_ROUNDING_MODE, roundingMode.name())
+ .setCatalogSessionProperty(catalog, DECIMAL_DEFAULT_SCALE, Integer.valueOf(scale).toString())
+ .build();
+ }
+
+ private Session sessionWithDecimalMappingStrict(UnsupportedTypeHandling unsupportedTypeHandling)
+ {
+ String catalog = getSession().getCatalog().orElseThrow();
+ return Session.builder(getSession())
+ .setCatalogSessionProperty(catalog, DECIMAL_MAPPING, STRICT.name())
+ .setCatalogSessionProperty(catalog, UNSUPPORTED_TYPE_HANDLING, unsupportedTypeHandling.name())
+ .build();
+ }
+
+ @Test
+ public void testVarbinary()
+ {
+ varbinaryTestCases("varbinary(50)")
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_varbinary"));
+
+ varbinaryTestCases("tinyblob")
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_varbinary"));
+
+ varbinaryTestCases("blob")
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_varbinary"));
+
+ varbinaryTestCases("mediumblob")
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_varbinary"));
+
+ varbinaryTestCases("longblob")
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_varbinary"));
+
+ varbinaryTestCases("varbinary")
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_varbinary"));
+ }
+
+ private SqlDataTypeTest varbinaryTestCases(String insertType)
+ {
+ return SqlDataTypeTest.create()
+ .addRoundTrip(insertType, "NULL", VARBINARY, "CAST(NULL AS varbinary)")
+ .addRoundTrip(insertType, "X''", VARBINARY, "X''")
+ .addRoundTrip(insertType, "X'68656C6C6F'", VARBINARY, "to_utf8('hello')")
+ .addRoundTrip(insertType, "X'5069C4996B6E6120C582C4856B61207720E69DB1E4BAACE983BD'", VARBINARY, "to_utf8('Piękna łąka w 東京都')")
+ .addRoundTrip(insertType, "X'4261672066756C6C206F6620F09F92B0'", VARBINARY, "to_utf8('Bag full of 💰')")
+ .addRoundTrip(insertType, "X'0001020304050607080DF9367AA7000000'", VARBINARY, "X'0001020304050607080DF9367AA7000000'") // non-text
+ .addRoundTrip(insertType, "X'000000000000'", VARBINARY, "X'000000000000'");
+ }
+
+ @Test
+ public void testBinary()
+ {
+ SqlDataTypeTest.create()
+ .addRoundTrip("binary(18)", "NULL", VARBINARY, "CAST(NULL AS varbinary)")
+ .addRoundTrip("binary(18)", "X''", VARBINARY, "X'000000000000000000000000000000000000'")
+ .addRoundTrip("binary(18)", "X'68656C6C6F'", VARBINARY, "to_utf8('hello') || X'00000000000000000000000000'")
+ .addRoundTrip("binary(18)", "X'C582C4856B61207720E69DB1E4BAACE983BD'", VARBINARY, "to_utf8('łąka w 東京都')") // no trailing zeros
+ .addRoundTrip("binary(18)", "X'4261672066756C6C206F6620F09F92B0'", VARBINARY, "to_utf8('Bag full of 💰') || X'0000'")
+ .addRoundTrip("binary(18)", "X'0001020304050607080DF9367AA7000000'", VARBINARY, "X'0001020304050607080DF9367AA700000000'") // non-text prefix
+ .addRoundTrip("binary(18)", "X'000000000000'", VARBINARY, "X'000000000000000000000000000000000000'")
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_binary"));
+ }
+
+ @Test
+ public void testDate()
+ {
+ // Note: there is identical test for PostgreSQL
+
+ ZoneId jvmZone = ZoneId.systemDefault();
+ checkState(jvmZone.getId().equals("America/Bahia_Banderas"), "This test assumes certain JVM time zone");
+ LocalDate dateOfLocalTimeChangeForwardAtMidnightInJvmZone = LocalDate.of(1970, 1, 1);
+ verify(jvmZone.getRules().getValidOffsets(dateOfLocalTimeChangeForwardAtMidnightInJvmZone.atStartOfDay()).isEmpty());
+
+ ZoneId someZone = ZoneId.of("Europe/Vilnius");
+ LocalDate dateOfLocalTimeChangeForwardAtMidnightInSomeZone = LocalDate.of(1983, 4, 1);
+ verify(someZone.getRules().getValidOffsets(dateOfLocalTimeChangeForwardAtMidnightInSomeZone.atStartOfDay()).isEmpty());
+ LocalDate dateOfLocalTimeChangeBackwardAtMidnightInSomeZone = LocalDate.of(1983, 10, 1);
+ verify(someZone.getRules().getValidOffsets(dateOfLocalTimeChangeBackwardAtMidnightInSomeZone.atStartOfDay().minusMinutes(1)).size() == 2);
+
+ DataTypeTest testCases = DataTypeTest.create()
+ .addRoundTrip(dateDataType(), LocalDate.of(1952, 4, 3)) // before epoch
+ .addRoundTrip(dateDataType(), LocalDate.of(1970, 1, 1))
+ .addRoundTrip(dateDataType(), LocalDate.of(1970, 2, 3))
+ .addRoundTrip(dateDataType(), LocalDate.of(2017, 7, 1)) // summer on northern hemisphere (possible DST)
+ .addRoundTrip(dateDataType(), LocalDate.of(2017, 1, 1)) // winter on northern hemisphere (possible DST on southern hemisphere)
+ .addRoundTrip(dateDataType(), dateOfLocalTimeChangeForwardAtMidnightInJvmZone)
+ .addRoundTrip(dateDataType(), dateOfLocalTimeChangeForwardAtMidnightInSomeZone)
+ .addRoundTrip(dateDataType(), dateOfLocalTimeChangeBackwardAtMidnightInSomeZone);
+
+ for (String timeZoneId : ImmutableList.of(UTC_KEY.getId(), jvmZone.getId(), someZone.getId())) {
+ Session session = Session.builder(getSession())
+ .setTimeZoneKey(TimeZoneKey.getTimeZoneKey(timeZoneId))
+ .build();
+ testCases.execute(getQueryRunner(), session, mysqlCreateAndInsert("tpch.test_date"));
+ testCases.execute(getQueryRunner(), session, trinoCreateAsSelect(session, "test_date"));
+ testCases.execute(getQueryRunner(), session, trinoCreateAsSelect(getSession(), "test_date"));
+ testCases.execute(getQueryRunner(), session, trinoCreateAndInsert(session, "test_date"));
+ }
+ }
+
+ /**
+ * Read {@code DATATIME}s inserted by MySQL as Trino {@code TIMESTAMP}s
+ */
+ @Test(dataProvider = "sessionZonesDataProvider")
+ public void testMySqlDatetimeType(ZoneId sessionZone)
+ {
+ Session session = Session.builder(getSession())
+ .setTimeZoneKey(TimeZoneKey.getTimeZoneKey(sessionZone.getId()))
+ .build();
+
+ SqlDataTypeTest.create()
+ // before epoch
+ .addRoundTrip("datetime(3)", "TIMESTAMP '1958-01-01 13:18:03.123'", TIMESTAMP_MILLIS, "TIMESTAMP '1958-01-01 13:18:03.123'")
+ // after epoch
+ .addRoundTrip("datetime(3)", "TIMESTAMP '2019-03-18 10:01:17.987'", TIMESTAMP_MILLIS, "TIMESTAMP '2019-03-18 10:01:17.987'")
+ // time doubled in JVM zone
+ .addRoundTrip("datetime(3)", "TIMESTAMP '2018-10-28 01:33:17.456'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 01:33:17.456'")
+ // time double in Vilnius
+ .addRoundTrip("datetime(3)", "TIMESTAMP '2018-10-28 03:33:33.333'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 03:33:33.333'")
+ // epoch
+ .addRoundTrip("datetime(3)", "TIMESTAMP '1970-01-01 00:00:00.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:00.000'")
+ // TODO time gaps do not round-trip (https://github.com/trinodb/trino/issues/6910)
+ .addRoundTrip("datetime(3)", "TIMESTAMP '1970-01-01 00:13:42.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:13:42.000'")
+ .addRoundTrip("datetime(3)", "TIMESTAMP '2018-04-01 02:13:55.123'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-04-01 03:13:55.123'")
+ // time gap in Vilnius
+ .addRoundTrip("datetime(3)", "TIMESTAMP '2018-03-25 03:17:17.000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-03-25 03:17:17.000'")
+ // time gap in Kathmandu
+ .addRoundTrip("datetime(3)", "TIMESTAMP '1986-01-01 00:13:07.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1986-01-01 00:13:07.000'")
+
+ // same as above but with higher precision
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1958-01-01 13:18:03.123000'", TIMESTAMP_MILLIS, "TIMESTAMP '1958-01-01 13:18:03.123'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '2019-03-18 10:01:17.987000'", TIMESTAMP_MILLIS, "TIMESTAMP '2019-03-18 10:01:17.987'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '2018-10-28 01:33:17.456000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 01:33:17.456'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '2018-10-28 03:33:33.333000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 03:33:33.333'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:00.000'")
+ // TODO time gaps do not round-trip (https://github.com/trinodb/trino/issues/6910)
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:13:42.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:13:42.000'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '2018-04-01 02:13:55.123000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-04-01 03:13:55.123'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '2018-03-25 03:17:17.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-03-25 03:17:17.000'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1986-01-01 00:13:07.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '1986-01-01 00:13:07.000'")
+
+ // test arbitrary time for all supported precisions
+ .addRoundTrip("datetime(0)", "TIMESTAMP '1970-01-01 00:00:01'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.000'")
+ .addRoundTrip("datetime(1)", "TIMESTAMP '1970-01-01 00:00:01.1'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.100'")
+ .addRoundTrip("datetime(2)", "TIMESTAMP '1970-01-01 00:00:01.12'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.120'")
+ .addRoundTrip("datetime(3)", "TIMESTAMP '1970-01-01 00:00:01.123'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("datetime(4)", "TIMESTAMP '1970-01-01 00:00:01.1234'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("datetime(5)", "TIMESTAMP '1970-01-01 00:00:01.12345'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:01.123456'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+
+ // test rounding for precisions too high for MySQL
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:01.1234560'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:01.12345649999'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:01.1234565'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:01.1234569'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+
+ .execute(getQueryRunner(), session, mysqlCreateAndInsert("tpch.test_datetime"));
+
+ SqlDataTypeTest.create()
+ // TODO support higher precision timestamps (https://github.com/trinodb/trino/issues/6910)
+ // before epoch with second fraction
+ //.addRoundTrip("timestamp(7)", "TIMESTAMP '1969-12-31 23:59:59.1230000'", createTimestampType(7), "TIMESTAMP '1969-12-31 23:59:59.1230000'")
+ //.addRoundTrip("timestamp(7)", "TIMESTAMP '1969-12-31 23:59:59.1234567'", createTimestampType(7), "TIMESTAMP '1969-12-31 23:59:59.1234567'")
+
+ // precision 0 ends up as precision 0
+ .addRoundTrip("datetime(0)", "TIMESTAMP '1970-01-01 00:00:01'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:01.000'")
+
+ .addRoundTrip("datetime(1)", "TIMESTAMP '1970-01-01 00:00:00.1'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.100'")
+ .addRoundTrip("datetime(1)", "TIMESTAMP '1970-01-01 00:00:00.9'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.900'")
+ .addRoundTrip("datetime(3)", "TIMESTAMP '1970-01-01 00:00:00.123'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123000'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
+ .addRoundTrip("datetime(3)", "TIMESTAMP '1970-01-01 00:00:00.999'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.999'")
+ // max supported precision
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123456'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
+
+ .addRoundTrip("datetime(1)", "TIMESTAMP '2020-09-27 12:34:56.1'", createTimestampType(3), "TIMESTAMP '2020-09-27 12:34:56.100'")
+ .addRoundTrip("datetime(1)", "TIMESTAMP '2020-09-27 12:34:56.9'", createTimestampType(3), "TIMESTAMP '2020-09-27 12:34:56.900'")
+ .addRoundTrip("datetime(3)", "TIMESTAMP '2020-09-27 12:34:56.123'", createTimestampType(3), "TIMESTAMP '2020-09-27 12:34:56.123'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '2020-09-27 12:34:56.123000'", createTimestampType(3), "TIMESTAMP '2020-09-27 12:34:56.123'")
+ .addRoundTrip("datetime(3)", "TIMESTAMP '2020-09-27 12:34:56.999'", createTimestampType(3), "TIMESTAMP '2020-09-27 12:34:56.999'")
+ // max supported precision
+ .addRoundTrip("datetime(6)", "TIMESTAMP '2020-09-27 12:34:56.123456'", createTimestampType(3), "TIMESTAMP '2020-09-27 12:34:56.123'")
+
+ // round down
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123451'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
+
+ // nanos round up, end result rounds down
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123499'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123399'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
+
+ // round up
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123999'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.124'")
+
+ // max precision
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123456'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
+
+ // round up to next second
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.999999'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:01.000'")
+
+ // round up to next day
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 23:59:59.999999'", createTimestampType(3), "TIMESTAMP '1970-01-02 00:00:00.000'")
+
+ // negative epoch - round up
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1969-12-31 23:59:59.999995'", createTimestampType(3), "TIMESTAMP '1970-01-01 00:00:00.000'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1969-12-31 23:59:59.999949'", createTimestampType(3), "TIMESTAMP '1970-01-01 00:00:00.000'")
+ .addRoundTrip("datetime(6)", "TIMESTAMP '1969-12-31 23:59:59.999994'", createTimestampType(3), "TIMESTAMP '1970-01-01 00:00:00.000'")
+
+ .execute(getQueryRunner(), session, mysqlCreateAndInsert("tpch.test_timestamp"));
+ }
+
+ /**
+ * Read {@code TIMESTAMP}s inserted by MySQL as Trino {@code TIMESTAMP}s
+ */
+ @Test(dataProvider = "sessionZonesDataProvider")
+ public void testTimestampFromMySql(ZoneId sessionZone)
+ {
+ Session session = Session.builder(getSession())
+ .setTimeZoneKey(TimeZoneKey.getTimeZoneKey(sessionZone.getId()))
+ .build();
+
+ // Same as above but with inserts from MySQL - i.e. read path
+ SqlDataTypeTest.create()
+ // before epoch (MySQL's timestamp type doesn't support values <= epoch)
+ //.addRoundTrip("timestamp(3)", "TIMESTAMP '1958-01-01 13:18:03.123'", createTimestampType(3), "TIMESTAMP '1958-01-01 13:18:03.000'")
+ // after epoch
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '2019-03-18 10:01:17.987'", TIMESTAMP_MILLIS, "TIMESTAMP '2019-03-18 10:01:17.987'")
+ // time doubled in JVM zone
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-10-28 01:33:17.456'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 01:33:17.456'")
+ // time double in Vilnius
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-10-28 03:33:33.333'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 03:33:33.333'")
+ // epoch (MySQL's timestamp type doesn't support values <= epoch)
+ //.addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:00:00.000'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.000'")
+ // TODO time gaps do not round-trip (https://github.com/trinodb/trino/issues/6910)
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:13:42.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:13:42.000'")
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-04-01 02:13:55.123'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-04-01 03:13:55.123'")
+ // time gap in Vilnius
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-03-25 03:17:17.000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-03-25 03:17:17.000'")
+ // time gap in Kathmandu
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '1986-01-01 00:13:07.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1986-01-01 00:13:07.000'")
+
+ // same as above but with higher precision - note that currently anything other than timestamp(3) can only be inserted directly via MySQL
+ // MySQL's timestamp type doesn't support values <= epoch
+ //.addRoundTrip("timestamp(6)", "TIMESTAMP '1958-01-01 13:18:03.123000'", createTimestampType(3), "TIMESTAMP '1958-01-01 13:18:03.123'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '2019-03-18 10:01:17.987000'", TIMESTAMP_MILLIS, "TIMESTAMP '2019-03-18 10:01:17.987'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '2018-10-28 01:33:17.456000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 01:33:17.456'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '2018-10-28 03:33:33.333000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 03:33:33.333'")
+ // MySQL's timestamp type doesn't support values <= epoch
+ //.addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:00.000000'", createTimestampType(3), "TIMESTAMP '1970-01-01 00:00:00.000'")
+ // TODO time gaps do not round-trip (https://github.com/trinodb/trino/issues/6910)
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:13:42.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:13:42.000'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '2018-04-01 02:13:55.123000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-04-01 03:13:55.123'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '2018-03-25 03:17:17.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-03-25 03:17:17.000'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1986-01-01 00:13:07.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '1986-01-01 00:13:07.000'")
+
+ // test arbitrary time for all supported precisions
+ .addRoundTrip("timestamp(0)", "TIMESTAMP '1970-01-01 00:00:01'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.000'")
+ .addRoundTrip("timestamp(1)", "TIMESTAMP '1970-01-01 00:00:01.1'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.100'")
+ .addRoundTrip("timestamp(2)", "TIMESTAMP '1970-01-01 00:00:01.12'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.120'")
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:00:01.123'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("timestamp(4)", "TIMESTAMP '1970-01-01 00:00:01.1234'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("timestamp(5)", "TIMESTAMP '1970-01-01 00:00:01.12345'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.123456'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.1234560'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.12345649999'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.1234565'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.1234569'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+
+ // TODO support higher precision timestamps (https://github.com/trinodb/trino/issues/6910)
+ // before epoch with second fraction
+ //.addRoundTrip("timestamp(7)", "TIMESTAMP '1969-12-31 23:59:59.1230000'", createTimestampType(7), "TIMESTAMP '1969-12-31 23:59:59.1230000'")
+ //.addRoundTrip("timestamp(7)", "TIMESTAMP '1969-12-31 23:59:59.1234567'", createTimestampType(7), "TIMESTAMP '1969-12-31 23:59:59.1234567'")
+
+ // precision 0 ends up as precision 0
+ .addRoundTrip("timestamp(0)", "TIMESTAMP '1970-01-01 00:00:01'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.000'")
+
+ .addRoundTrip("timestamp(1)", "TIMESTAMP '1970-01-01 00:00:01.1'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.100'")
+ .addRoundTrip("timestamp(1)", "TIMESTAMP '1970-01-01 00:00:01.9'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.900'")
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:00:01.123'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.123000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:00:01.999'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.999'")
+ // max supported precision
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.123456'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+
+ .addRoundTrip("timestamp(1)", "TIMESTAMP '2020-09-27 12:34:56.1'", TIMESTAMP_MILLIS, "TIMESTAMP '2020-09-27 12:34:56.100'")
+ .addRoundTrip("timestamp(1)", "TIMESTAMP '2020-09-27 12:34:56.9'", TIMESTAMP_MILLIS, "TIMESTAMP '2020-09-27 12:34:56.900'")
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '2020-09-27 12:34:56.123'", TIMESTAMP_MILLIS, "TIMESTAMP '2020-09-27 12:34:56.123'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '2020-09-27 12:34:56.123000'", TIMESTAMP_MILLIS, "TIMESTAMP '2020-09-27 12:34:56.123'")
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '2020-09-27 12:34:56.999'", TIMESTAMP_MILLIS, "TIMESTAMP '2020-09-27 12:34:56.999'")
+ // max supported precision
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '2020-09-27 12:34:56.123456'", TIMESTAMP_MILLIS, "TIMESTAMP '2020-09-27 12:34:56.123'")
+
+ // round down
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.12345671'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+
+ // nanos round up, end result rounds down
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.1234567499'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.123456749999'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+
+ // round up
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.12345675'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
+
+ // max precision
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.111222333444'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.111'")
+
+ // round up to next second
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.99999995'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:02.000'")
+
+ // round up to next day
+ .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 23:59:59.99999995'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-02 00:00:00.000'")
+
+ // negative epoch (MySQL's timestamp type doesn't support values <= epoch)
+ //.addRoundTrip("TIMESTAMP '1969-12-31 23:59:59.99999995'", "TIMESTAMP '1970-01-01 00:00:00.000'")
+ //.addRoundTrip("TIMESTAMP '1969-12-31 23:59:59.999999949999'", "TIMESTAMP '1969-12-31 23:59:59.999'")
+ //.addRoundTrip("TIMESTAMP '1969-12-31 23:59:59.99999994'", "TIMESTAMP '1969-12-31 23:59:59.999'")
+
+ .execute(getQueryRunner(), session, mysqlCreateAndInsert("tpch.test_timestamp"));
+ }
+
+ @Test(dataProvider = "sessionZonesDataProvider")
+ public void testTimestampFromTrino(ZoneId sessionZone)
+ {
+ Session session = Session.builder(getSession())
+ .setTimeZoneKey(TimeZoneKey.getTimeZoneKey(sessionZone.getId()))
+ .build();
+
+ SqlDataTypeTest.create()
+ // before epoch
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '1958-01-01 13:18:03.123'", TIMESTAMP_MILLIS, "TIMESTAMP '1958-01-01 13:18:03.123'")
+ // after epoch
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '2019-03-18 10:01:17.987'", TIMESTAMP_MILLIS, "TIMESTAMP '2019-03-18 10:01:17.987'")
+ // time doubled in JVM zone
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-10-28 01:33:17.456'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 01:33:17.456'")
+ // time double in Vilnius
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-10-28 03:33:33.333'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 03:33:33.333'")
+ // epoch
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:00:00.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:00.000'")
+ // TODO time gaps do not round-trip (https://github.com/trinodb/trino/issues/6910)
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:13:42.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:13:42.000'")
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-04-01 02:13:55.123'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-04-01 03:13:55.123'")
+ // time gap in Vilnius
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-03-25 03:17:17.000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-03-25 03:17:17.000'")
+ // time gap in Kathmandu
+ .addRoundTrip("timestamp(3)", "TIMESTAMP '1986-01-01 00:13:07.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1986-01-01 00:13:07.000'")
+
+ .execute(getQueryRunner(), session, trinoCreateAsSelect(session, "test_timestamp"))
+ .execute(getQueryRunner(), session, trinoCreateAsSelect(getSession(), "test_timestamp"))
+ .execute(getQueryRunner(), session, trinoCreateAndInsert(session, "test_timestamp"));
+ }
+
+ @DataProvider
+ public Object[][] sessionZonesDataProvider()
+ {
+ return new Object[][] {
+ {UTC},
+ {ZoneId.systemDefault()},
+ // no DST in 1970, but has DST in later years (e.g. 2018)
+ {ZoneId.of("Europe/Vilnius")},
+ // minutes offset change since 1970-01-01, no DST
+ {ZoneId.of("Asia/Kathmandu")},
+ {ZoneId.of(TestingSession.DEFAULT_TIME_ZONE_KEY.getId())},
+ };
+ }
+
+ @Test
+ public void testJson()
+ {
+ jsonTestCases(jsonDataType(value -> "JSON " + formatStringLiteral(value)))
+ .execute(getQueryRunner(), trinoCreateAsSelect("trino_test_json"));
+ jsonTestCases(jsonDataType(value -> format("CAST(%s AS JSON)", formatStringLiteral(value))))
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_json"));
+ }
+
+ private DataTypeTest jsonTestCases(DataType jsonDataType)
+ {
+ return DataTypeTest.create()
+ .addRoundTrip(jsonDataType, "{}")
+ .addRoundTrip(jsonDataType, null)
+ .addRoundTrip(jsonDataType, "null")
+ .addRoundTrip(jsonDataType, "123.4")
+ .addRoundTrip(jsonDataType, "\"abc\"")
+ .addRoundTrip(jsonDataType, "\"text with ' apostrophes\"")
+ .addRoundTrip(jsonDataType, "\"\"")
+ .addRoundTrip(jsonDataType, "{\"a\":1,\"b\":2}")
+ .addRoundTrip(jsonDataType, "{\"a\":[1,2,3],\"b\":{\"aa\":11,\"bb\":[{\"a\":1,\"b\":2},{\"a\":0}]}}")
+ .addRoundTrip(jsonDataType, "[]");
+ }
+
+ @Test
+ public void testFloat()
+ {
+ singlePrecisionFloatingPointTests(realDataType())
+ .execute(getQueryRunner(), trinoCreateAsSelect("trino_test_float"));
+ singlePrecisionFloatingPointTests(mysqlFloatDataType())
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_float"));
+ }
+
+ @Test
+ public void testDouble()
+ {
+ doublePrecisionFloatingPointTests(doubleDataType())
+ .execute(getQueryRunner(), trinoCreateAsSelect("trino_test_double"));
+ doublePrecisionFloatingPointTests(mysqlDoubleDataType())
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_double"));
+ }
+
+ @Test
+ public void testUnsignedTypes()
+ {
+ DataType mysqlUnsignedTinyInt = DataType.dataType("TINYINT UNSIGNED", SmallintType.SMALLINT, Objects::toString);
+ DataType mysqlUnsignedSmallInt = DataType.dataType("SMALLINT UNSIGNED", IntegerType.INTEGER, Objects::toString);
+ DataType mysqlUnsignedInt = DataType.dataType("INT UNSIGNED", BigintType.BIGINT, Objects::toString);
+ DataType mysqlUnsignedInteger = DataType.dataType("INTEGER UNSIGNED", BigintType.BIGINT, Objects::toString);
+ DataType mysqlUnsignedBigint = DataType.dataType("BIGINT UNSIGNED", createDecimalType(20), Objects::toString);
+
+ DataTypeTest.create()
+ .addRoundTrip(mysqlUnsignedTinyInt, (short) 255)
+ .addRoundTrip(mysqlUnsignedSmallInt, 65_535)
+ .addRoundTrip(mysqlUnsignedInt, 4_294_967_295L)
+ .addRoundTrip(mysqlUnsignedInteger, 4_294_967_295L)
+ .addRoundTrip(mysqlUnsignedBigint, new BigDecimal("18446744073709551615"))
+ .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_unsigned"));
+ }
+
+ private static DataTypeTest singlePrecisionFloatingPointTests(DataType floatType)
+ {
+ // we are not testing Nan/-Infinity/+Infinity as those are not supported by MySQL
+ return DataTypeTest.create()
+ .addRoundTrip(floatType, 3.14f)
+ // .addRoundTrip(floatType, 3.1415927f) // Overeagerly rounded by mysql to 3.14159
+ .addRoundTrip(floatType, null);
+ }
+
+ private static DataTypeTest doublePrecisionFloatingPointTests(DataType doubleType)
+ {
+ // we are not testing Nan/-Infinity/+Infinity as those are not supported by MySQL
+ return DataTypeTest.create()
+ .addRoundTrip(doubleType, 1.0e100d)
+ .addRoundTrip(doubleType, null);
+ }
+
+ private void testUnsupportedDataType(String databaseDataType)
+ {
+ onRemoteDatabase().execute(format("CREATE TABLE tpch.test_unsupported_data_type(supported_column varchar(5), unsupported_column %s)", databaseDataType));
+ try {
+ assertQuery(
+ "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'tpch' AND TABLE_NAME = 'test_unsupported_data_type'",
+ "VALUES 'supported_column'"); // no 'unsupported_column'
+ }
+ finally {
+ onRemoteDatabase().execute("DROP TABLE tpch.test_unsupported_data_type");
+ }
+ }
+
+ private DataSetup trinoCreateAsSelect(String tableNamePrefix)
+ {
+ return trinoCreateAsSelect(getSession(), tableNamePrefix);
+ }
+
+ private DataSetup trinoCreateAsSelect(Session session, String tableNamePrefix)
+ {
+ return new CreateAsSelectDataSetup(new TrinoSqlExecutor(getQueryRunner(), session), tableNamePrefix);
+ }
+
+ private DataSetup trinoCreateAndInsert(Session session, String tableNamePrefix)
+ {
+ return new CreateAndInsertDataSetup(new TrinoSqlExecutor(getQueryRunner(), session), tableNamePrefix);
+ }
+
+ private DataSetup mysqlCreateAndInsert(String tableNamePrefix)
+ {
+ return new CreateAndInsertDataSetup(onRemoteDatabase(), tableNamePrefix);
+ }
+
+ private static DataType jsonDataType(Function toLiteral)
+ {
+ return dataType(
+ "json",
+ JSON,
+ toLiteral);
+ }
+
+ private static DataType mysqlFloatDataType()
+ {
+ return dataType("float", RealType.REAL, Object::toString);
+ }
+
+ private static DataType mysqlDoubleDataType()
+ {
+ return dataType("double precision", DoubleType.DOUBLE, Object::toString);
+ }
+
+ private static DataType mysqlBinaryDataType(String insertType)
+ {
+ return dataType(
+ insertType,
+ VARBINARY,
+ bytes -> "X'" + base16().encode(bytes) + "'",
+ DataType::binaryLiteral,
+ identity());
+ }
+
+ protected abstract SqlExecutor onRemoteDatabase();
+}
diff --git a/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/MariaDbQueryRunner.java b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/MariaDbQueryRunner.java
new file mode 100644
index 000000000000..a0dc3b85c2cd
--- /dev/null
+++ b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/MariaDbQueryRunner.java
@@ -0,0 +1,102 @@
+/*
+ * 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.plugin.mysql;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import io.airlift.log.Logger;
+import io.airlift.log.Logging;
+import io.trino.Session;
+import io.trino.plugin.tpch.TpchPlugin;
+import io.trino.testing.DistributedQueryRunner;
+import io.trino.testing.QueryRunner;
+import io.trino.tpch.TpchTable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static io.airlift.testing.Closeables.closeAllSuppress;
+import static io.trino.plugin.tpch.TpchMetadata.TINY_SCHEMA_NAME;
+import static io.trino.testing.QueryAssertions.copyTpchTables;
+import static io.trino.testing.TestingSession.testSessionBuilder;
+
+public final class MariaDbQueryRunner
+{
+ private MariaDbQueryRunner() {}
+
+ private static final String TPCH_SCHEMA = "tpch";
+
+ public static QueryRunner createMariaDbQueryRunner(TestingMariaDbServer server, TpchTable>... tables)
+ throws Exception
+ {
+ return createMariaDbQueryRunner(server, ImmutableMap.of(), ImmutableMap.of(), ImmutableList.copyOf(tables));
+ }
+
+ public static DistributedQueryRunner createMariaDbQueryRunner(
+ TestingMariaDbServer server,
+ Map extraProperties,
+ Map connectorProperties,
+ Iterable> tables)
+ throws Exception
+ {
+ DistributedQueryRunner queryRunner = DistributedQueryRunner.builder(createSession())
+ .setExtraProperties(extraProperties)
+ .build();
+ try {
+ queryRunner.installPlugin(new TpchPlugin());
+ queryRunner.createCatalog("tpch", "tpch");
+
+ connectorProperties = new HashMap<>(ImmutableMap.copyOf(connectorProperties));
+ connectorProperties.putIfAbsent("connection-url", server.getJdbcUrl());
+ connectorProperties.putIfAbsent("connection-user", server.getUsername());
+ connectorProperties.putIfAbsent("connection-password", server.getPassword());
+ connectorProperties.putIfAbsent("allow-drop-table", "true");
+
+ queryRunner.installPlugin(new MySqlPlugin());
+ queryRunner.createCatalog("mariadb", "mariadb", connectorProperties);
+
+ copyTpchTables(queryRunner, "tpch", TINY_SCHEMA_NAME, createSession(), tables);
+
+ return queryRunner;
+ }
+ catch (Throwable e) {
+ closeAllSuppress(e, queryRunner);
+ throw e;
+ }
+ }
+
+ private static Session createSession()
+ {
+ return testSessionBuilder()
+ .setCatalog("mariadb")
+ .setSchema(TPCH_SCHEMA)
+ .build();
+ }
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ Logging.initialize();
+
+ DistributedQueryRunner queryRunner = createMariaDbQueryRunner(
+ new TestingMariaDbServer(),
+ ImmutableMap.of("http-server.http.port", "8080"),
+ ImmutableMap.of(),
+ TpchTable.getTables());
+
+ Logger log = Logger.get(MariaDbQueryRunner.class);
+ log.info("======== SERVER STARTED ========");
+ log.info("\n====\n%s\n====", queryRunner.getCoordinator().getBaseUrl());
+ }
+}
diff --git a/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMariaDbConnectorTest.java b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMariaDbConnectorTest.java
new file mode 100644
index 000000000000..4c1b4247b89e
--- /dev/null
+++ b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMariaDbConnectorTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.plugin.mysql;
+
+import com.google.common.collect.ImmutableMap;
+import io.trino.testing.QueryRunner;
+import io.trino.testing.sql.SqlExecutor;
+
+import static io.trino.plugin.mysql.MariaDbQueryRunner.createMariaDbQueryRunner;
+
+public class TestMariaDbConnectorTest
+ extends BaseMySqlConnectorTest
+{
+ private TestingMariaDbServer mariaDbServer;
+
+ @Override
+ protected QueryRunner createQueryRunner()
+ throws Exception
+ {
+ mariaDbServer = closeAfterClass(new TestingMariaDbServer());
+ return createMariaDbQueryRunner(mariaDbServer, ImmutableMap.of(), ImmutableMap.of(), REQUIRED_TPCH_TABLES);
+ }
+
+ @Override
+ protected SqlExecutor onRemoteDatabase()
+ {
+ return mariaDbServer::execute;
+ }
+}
diff --git a/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMariaDbTypeMapping.java b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMariaDbTypeMapping.java
new file mode 100644
index 000000000000..196519f6cb43
--- /dev/null
+++ b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMariaDbTypeMapping.java
@@ -0,0 +1,41 @@
+/*
+ * 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.plugin.mysql;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import io.trino.testing.QueryRunner;
+import io.trino.testing.sql.SqlExecutor;
+
+import static io.trino.plugin.mysql.MariaDbQueryRunner.createMariaDbQueryRunner;
+
+public class TestMariaDbTypeMapping
+ extends BaseMySqlTypeMappingTest
+{
+ private TestingMariaDbServer mariaDbServer;
+
+ @Override
+ protected QueryRunner createQueryRunner()
+ throws Exception
+ {
+ mariaDbServer = closeAfterClass(new TestingMariaDbServer());
+ return createMariaDbQueryRunner(mariaDbServer, ImmutableMap.of(), ImmutableMap.of(), ImmutableList.of());
+ }
+
+ @Override
+ protected SqlExecutor onRemoteDatabase()
+ {
+ return mariaDbServer::execute;
+ }
+}
diff --git a/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMySqlPlugin.java b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMySqlPlugin.java
index 7607ca52e961..a6c649b7a1c0 100644
--- a/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMySqlPlugin.java
+++ b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMySqlPlugin.java
@@ -19,7 +19,6 @@
import io.trino.testing.TestingConnectorContext;
import org.testng.annotations.Test;
-import static com.google.common.collect.Iterables.getOnlyElement;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class TestMySqlPlugin
@@ -28,14 +27,16 @@ public class TestMySqlPlugin
public void testCreateConnector()
{
Plugin plugin = new MySqlPlugin();
- ConnectorFactory factory = getOnlyElement(plugin.getConnectorFactories());
+ for (ConnectorFactory factory : plugin.getConnectorFactories()) {
+ factory.create("test", ImmutableMap.of("connection-url", "jdbc:mysql://test"), new TestingConnectorContext()).shutdown();
- factory.create("test", ImmutableMap.of("connection-url", "jdbc:mysql://test"), new TestingConnectorContext()).shutdown();
+ assertThatThrownBy(() -> factory.create("test", ImmutableMap.of("connection-url", "test"), new TestingConnectorContext()))
+ .hasMessageContaining("Invalid JDBC URL for MySQL connector");
+ assertThatThrownBy(() -> factory.create("test", ImmutableMap.of("connection-url", "jdbc:mariadb://test"), new TestingConnectorContext()))
+ .hasMessageContaining("Invalid JDBC URL for MySQL connector");
- assertThatThrownBy(() -> factory.create("test", ImmutableMap.of("connection-url", "test"), new TestingConnectorContext()))
- .hasMessageContaining("Invalid JDBC URL for MySQL connector");
-
- assertThatThrownBy(() -> factory.create("test", ImmutableMap.of("connection-url", "jdbc:mysql://test/abc"), new TestingConnectorContext()))
- .hasMessageContaining("Database (catalog) must not be specified in JDBC URL for MySQL connector");
+ assertThatThrownBy(() -> factory.create("test", ImmutableMap.of("connection-url", "jdbc:mysql://test/abc"), new TestingConnectorContext()))
+ .hasMessageContaining("Database (catalog) must not be specified in JDBC URL for MySQL connector");
+ }
}
}
diff --git a/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMySqlTypeMapping.java b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMySqlTypeMapping.java
index 0331903f5148..5b4f01c8960a 100644
--- a/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMySqlTypeMapping.java
+++ b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestMySqlTypeMapping.java
@@ -15,81 +15,14 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import io.trino.Session;
-import io.trino.plugin.jdbc.UnsupportedTypeHandling;
-import io.trino.spi.type.BigintType;
-import io.trino.spi.type.DoubleType;
-import io.trino.spi.type.IntegerType;
-import io.trino.spi.type.RealType;
-import io.trino.spi.type.SmallintType;
-import io.trino.spi.type.TimeZoneKey;
-import io.trino.spi.type.VarcharType;
-import io.trino.testing.AbstractTestQueryFramework;
import io.trino.testing.QueryRunner;
-import io.trino.testing.TestingSession;
-import io.trino.testing.datatype.CreateAndInsertDataSetup;
-import io.trino.testing.datatype.CreateAsSelectDataSetup;
-import io.trino.testing.datatype.DataSetup;
-import io.trino.testing.datatype.DataType;
-import io.trino.testing.datatype.DataTypeTest;
-import io.trino.testing.datatype.SqlDataTypeTest;
import io.trino.testing.sql.SqlExecutor;
-import io.trino.testing.sql.TestTable;
-import io.trino.testing.sql.TrinoSqlExecutor;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.time.LocalDate;
-import java.time.ZoneId;
-import java.util.Objects;
-import java.util.function.Function;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.io.BaseEncoding.base16;
-import static io.trino.plugin.jdbc.DecimalConfig.DecimalMapping.ALLOW_OVERFLOW;
-import static io.trino.plugin.jdbc.DecimalConfig.DecimalMapping.STRICT;
-import static io.trino.plugin.jdbc.DecimalSessionSessionProperties.DECIMAL_DEFAULT_SCALE;
-import static io.trino.plugin.jdbc.DecimalSessionSessionProperties.DECIMAL_MAPPING;
-import static io.trino.plugin.jdbc.DecimalSessionSessionProperties.DECIMAL_ROUNDING_MODE;
-import static io.trino.plugin.jdbc.TypeHandlingJdbcSessionProperties.UNSUPPORTED_TYPE_HANDLING;
-import static io.trino.plugin.jdbc.UnsupportedTypeHandling.CONVERT_TO_VARCHAR;
import static io.trino.plugin.mysql.MySqlQueryRunner.createMySqlQueryRunner;
-import static io.trino.spi.type.DecimalType.createDecimalType;
-import static io.trino.spi.type.TimeZoneKey.UTC_KEY;
-import static io.trino.spi.type.TimestampType.TIMESTAMP_MILLIS;
-import static io.trino.spi.type.TimestampType.createTimestampType;
-import static io.trino.spi.type.VarbinaryType.VARBINARY;
-import static io.trino.spi.type.VarcharType.createUnboundedVarcharType;
-import static io.trino.spi.type.VarcharType.createVarcharType;
-import static io.trino.testing.datatype.DataType.bigintDataType;
-import static io.trino.testing.datatype.DataType.charDataType;
-import static io.trino.testing.datatype.DataType.dataType;
-import static io.trino.testing.datatype.DataType.dateDataType;
-import static io.trino.testing.datatype.DataType.decimalDataType;
-import static io.trino.testing.datatype.DataType.doubleDataType;
-import static io.trino.testing.datatype.DataType.formatStringLiteral;
-import static io.trino.testing.datatype.DataType.integerDataType;
-import static io.trino.testing.datatype.DataType.realDataType;
-import static io.trino.testing.datatype.DataType.smallintDataType;
-import static io.trino.testing.datatype.DataType.stringDataType;
-import static io.trino.testing.datatype.DataType.tinyintDataType;
-import static io.trino.testing.datatype.DataType.varcharDataType;
-import static io.trino.type.JsonType.JSON;
-import static java.lang.String.format;
-import static java.math.RoundingMode.HALF_UP;
-import static java.math.RoundingMode.UNNECESSARY;
-import static java.time.ZoneOffset.UTC;
-import static java.util.Arrays.asList;
-import static java.util.function.Function.identity;
public class TestMySqlTypeMapping
- extends AbstractTestQueryFramework
+ extends BaseMySqlTypeMappingTest
{
- private static final String CHARACTER_SET_UTF8 = "CHARACTER SET utf8";
-
private TestingMySqlServer mysqlServer;
@Override
@@ -100,787 +33,9 @@ protected QueryRunner createQueryRunner()
return createMySqlQueryRunner(mysqlServer, ImmutableMap.of(), ImmutableMap.of(), ImmutableList.of());
}
- @Test
- public void testBasicTypes()
- {
- DataTypeTest.create()
- .addRoundTrip(bigintDataType(), 123_456_789_012L)
- .addRoundTrip(integerDataType(), 1_234_567_890)
- .addRoundTrip(smallintDataType(), (short) 32_456)
- .addRoundTrip(tinyintDataType(), (byte) 125)
- .addRoundTrip(doubleDataType(), 123.45d)
- .addRoundTrip(realDataType(), 123.45f)
- .execute(getQueryRunner(), trinoCreateAsSelect("test_basic_types"));
- }
-
- @Test
- public void testTrinoCreatedParameterizedVarchar()
- {
- DataTypeTest.create()
- .addRoundTrip(stringDataType("varchar(10)", createVarcharType(255)), "text_a")
- .addRoundTrip(stringDataType("varchar(255)", createVarcharType(255)), "text_b")
- .addRoundTrip(stringDataType("varchar(256)", createVarcharType(65535)), "text_c")
- .addRoundTrip(stringDataType("varchar(65535)", createVarcharType(65535)), "text_d")
- .addRoundTrip(stringDataType("varchar(65536)", createVarcharType(16777215)), "text_e")
- .addRoundTrip(stringDataType("varchar(16777215)", createVarcharType(16777215)), "text_f")
- .addRoundTrip(stringDataType("varchar(16777216)", createUnboundedVarcharType()), "text_g")
- .addRoundTrip(stringDataType("varchar(" + VarcharType.MAX_LENGTH + ")", createUnboundedVarcharType()), "text_h")
- .addRoundTrip(stringDataType("varchar", createUnboundedVarcharType()), "unbounded")
- .execute(getQueryRunner(), trinoCreateAsSelect("trino__test_parameterized_varchar"));
- }
-
- @Test
- public void testMySqlCreatedParameterizedVarchar()
- {
- DataTypeTest.create()
- .addRoundTrip(stringDataType("tinytext", createVarcharType(255)), "a")
- .addRoundTrip(stringDataType("text", createVarcharType(65535)), "b")
- .addRoundTrip(stringDataType("mediumtext", createVarcharType(16777215)), "c")
- .addRoundTrip(stringDataType("longtext", createUnboundedVarcharType()), "d")
- .addRoundTrip(varcharDataType(32), "e")
- .addRoundTrip(varcharDataType(15000), "f")
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_parameterized_varchar"));
- }
-
- @Test
- public void testMySqlCreatedParameterizedVarcharUnicode()
- {
- String sampleUnicodeText = "\u653b\u6bbb\u6a5f\u52d5\u968a";
- DataTypeTest.create()
- .addRoundTrip(stringDataType("tinytext " + CHARACTER_SET_UTF8, createVarcharType(255)), sampleUnicodeText)
- .addRoundTrip(stringDataType("text " + CHARACTER_SET_UTF8, createVarcharType(65535)), sampleUnicodeText)
- .addRoundTrip(stringDataType("mediumtext " + CHARACTER_SET_UTF8, createVarcharType(16777215)), sampleUnicodeText)
- .addRoundTrip(stringDataType("longtext " + CHARACTER_SET_UTF8, createUnboundedVarcharType()), sampleUnicodeText)
- .addRoundTrip(varcharDataType(sampleUnicodeText.length(), CHARACTER_SET_UTF8), sampleUnicodeText)
- .addRoundTrip(varcharDataType(32, CHARACTER_SET_UTF8), sampleUnicodeText)
- .addRoundTrip(varcharDataType(20000, CHARACTER_SET_UTF8), sampleUnicodeText)
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_parameterized_varchar_unicode"));
- }
-
- @Test
- public void testTrinoCreatedParameterizedChar()
- {
- mysqlCharTypeTest()
- .execute(getQueryRunner(), trinoCreateAsSelect("mysql_test_parameterized_char"));
- }
-
- @Test
- public void testMySqlCreatedParameterizedChar()
- {
- mysqlCharTypeTest()
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_parameterized_char"));
- }
-
- private DataTypeTest mysqlCharTypeTest()
- {
- return DataTypeTest.create()
- .addRoundTrip(charDataType("char", 1), "")
- .addRoundTrip(charDataType("char", 1), "a")
- .addRoundTrip(charDataType(1), "")
- .addRoundTrip(charDataType(1), "a")
- .addRoundTrip(charDataType(8), "abc")
- .addRoundTrip(charDataType(8), "12345678")
- .addRoundTrip(charDataType(255), "a".repeat(255));
- }
-
- @Test
- public void testMySqlCreatedParameterizedCharUnicode()
- {
- DataTypeTest.create()
- .addRoundTrip(charDataType(1, CHARACTER_SET_UTF8), "\u653b")
- .addRoundTrip(charDataType(5, CHARACTER_SET_UTF8), "\u653b\u6bbb")
- .addRoundTrip(charDataType(5, CHARACTER_SET_UTF8), "\u653b\u6bbb\u6a5f\u52d5\u968a")
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_parameterized_varchar"));
- }
-
- @Test
- public void testMysqlCreatedDecimal()
- {
- decimalTests()
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_decimal"));
- }
-
- @Test
- public void testTrinoCreatedDecimal()
- {
- decimalTests()
- .execute(getQueryRunner(), trinoCreateAsSelect("test_decimal"));
- }
-
- private DataTypeTest decimalTests()
- {
- return DataTypeTest.create()
- .addRoundTrip(decimalDataType(3, 0), new BigDecimal("193"))
- .addRoundTrip(decimalDataType(3, 0), new BigDecimal("19"))
- .addRoundTrip(decimalDataType(3, 0), new BigDecimal("-193"))
- .addRoundTrip(decimalDataType(3, 1), new BigDecimal("10.0"))
- .addRoundTrip(decimalDataType(3, 1), new BigDecimal("10.1"))
- .addRoundTrip(decimalDataType(3, 1), new BigDecimal("-10.1"))
- .addRoundTrip(decimalDataType(4, 2), new BigDecimal("2"))
- .addRoundTrip(decimalDataType(4, 2), new BigDecimal("2.3"))
- .addRoundTrip(decimalDataType(24, 2), new BigDecimal("2"))
- .addRoundTrip(decimalDataType(24, 2), new BigDecimal("2.3"))
- .addRoundTrip(decimalDataType(24, 2), new BigDecimal("123456789.3"))
- .addRoundTrip(decimalDataType(24, 4), new BigDecimal("12345678901234567890.31"))
- .addRoundTrip(decimalDataType(30, 5), new BigDecimal("3141592653589793238462643.38327"))
- .addRoundTrip(decimalDataType(30, 5), new BigDecimal("-3141592653589793238462643.38327"))
- .addRoundTrip(decimalDataType(38, 0), new BigDecimal("27182818284590452353602874713526624977"))
- .addRoundTrip(decimalDataType(38, 0), new BigDecimal("-27182818284590452353602874713526624977"));
- }
-
- @Test
- public void testDecimalExceedingPrecisionMax()
- {
- testUnsupportedDataType("decimal(50,0)");
- }
-
- @Test
- public void testDecimalExceedingPrecisionMaxWithExceedingIntegerValues()
- {
- try (TestTable testTable = new TestTable(
- mysqlServer::execute,
- "tpch.test_exceeding_max_decimal",
- "(d_col decimal(65,25))",
- asList("1234567890123456789012345678901234567890.123456789", "-1234567890123456789012345678901234567890.123456789"))) {
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 0),
- format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
- "VALUES ('d_col', 'decimal(38,0)')");
- assertQueryFails(
- sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 0),
- "SELECT d_col FROM " + testTable.getName(),
- "Rounding necessary");
- assertQueryFails(
- sessionWithDecimalMappingAllowOverflow(HALF_UP, 0),
- "SELECT d_col FROM " + testTable.getName(),
- "Decimal overflow");
- assertQuery(
- sessionWithDecimalMappingStrict(CONVERT_TO_VARCHAR),
- format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
- "VALUES ('d_col', 'varchar')");
- assertQuery(
- sessionWithDecimalMappingStrict(CONVERT_TO_VARCHAR),
- "SELECT d_col FROM " + testTable.getName(),
- "VALUES ('1234567890123456789012345678901234567890.1234567890000000000000000'), ('-1234567890123456789012345678901234567890.1234567890000000000000000')");
- }
- }
-
- @Test
- public void testDecimalExceedingPrecisionMaxWithNonExceedingIntegerValues()
- {
- try (TestTable testTable = new TestTable(
- mysqlServer::execute,
- "tpch.test_exceeding_max_decimal",
- "(d_col decimal(60,20))",
- asList("123456789012345678901234567890.123456789012345", "-123456789012345678901234567890.123456789012345"))) {
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 0),
- format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
- "VALUES ('d_col', 'decimal(38,0)')");
- assertQueryFails(
- sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 0),
- "SELECT d_col FROM " + testTable.getName(),
- "Rounding necessary");
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(HALF_UP, 0),
- "SELECT d_col FROM " + testTable.getName(),
- "VALUES (123456789012345678901234567890), (-123456789012345678901234567890)");
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 8),
- format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
- "VALUES ('d_col', 'decimal(38,8)')");
- assertQueryFails(
- sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 8),
- "SELECT d_col FROM " + testTable.getName(),
- "Rounding necessary");
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(HALF_UP, 8),
- "SELECT d_col FROM " + testTable.getName(),
- "VALUES (123456789012345678901234567890.12345679), (-123456789012345678901234567890.12345679)");
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(HALF_UP, 22),
- format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
- "VALUES ('d_col', 'decimal(38,20)')");
- assertQueryFails(
- sessionWithDecimalMappingAllowOverflow(HALF_UP, 20),
- "SELECT d_col FROM " + testTable.getName(),
- "Decimal overflow");
- assertQueryFails(
- sessionWithDecimalMappingAllowOverflow(HALF_UP, 9),
- "SELECT d_col FROM " + testTable.getName(),
- "Decimal overflow");
- assertQuery(
- sessionWithDecimalMappingStrict(CONVERT_TO_VARCHAR),
- format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
- "VALUES ('d_col', 'varchar')");
- assertQuery(
- sessionWithDecimalMappingStrict(CONVERT_TO_VARCHAR),
- "SELECT d_col FROM " + testTable.getName(),
- "VALUES ('123456789012345678901234567890.12345678901234500000'), ('-123456789012345678901234567890.12345678901234500000')");
- }
- }
-
- @Test(dataProvider = "testDecimalExceedingPrecisionMaxProvider")
- public void testDecimalExceedingPrecisionMaxWithSupportedValues(int typePrecision, int typeScale)
- {
- try (TestTable testTable = new TestTable(
- mysqlServer::execute,
- "tpch.test_exceeding_max_decimal",
- format("(d_col decimal(%d,%d))", typePrecision, typeScale),
- asList("12.01", "-12.01", "123", "-123", "1.12345678", "-1.12345678"))) {
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 0),
- format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
- "VALUES ('d_col', 'decimal(38,0)')");
- assertQueryFails(
- sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 0),
- "SELECT d_col FROM " + testTable.getName(),
- "Rounding necessary");
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(HALF_UP, 0),
- "SELECT d_col FROM " + testTable.getName(),
- "VALUES (12), (-12), (123), (-123), (1), (-1)");
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(HALF_UP, 3),
- format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
- "VALUES ('d_col', 'decimal(38,3)')");
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(HALF_UP, 3),
- "SELECT d_col FROM " + testTable.getName(),
- "VALUES (12.01), (-12.01), (123), (-123), (1.123), (-1.123)");
- assertQueryFails(
- sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 3),
- "SELECT d_col FROM " + testTable.getName(),
- "Rounding necessary");
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(HALF_UP, 8),
- format("SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'tpch' AND table_schema||'.'||table_name = '%s'", testTable.getName()),
- "VALUES ('d_col', 'decimal(38,8)')");
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(HALF_UP, 8),
- "SELECT d_col FROM " + testTable.getName(),
- "VALUES (12.01), (-12.01), (123), (-123), (1.12345678), (-1.12345678)");
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(HALF_UP, 9),
- "SELECT d_col FROM " + testTable.getName(),
- "VALUES (12.01), (-12.01), (123), (-123), (1.12345678), (-1.12345678)");
- assertQuery(
- sessionWithDecimalMappingAllowOverflow(UNNECESSARY, 8),
- "SELECT d_col FROM " + testTable.getName(),
- "VALUES (12.01), (-12.01), (123), (-123), (1.12345678), (-1.12345678)");
- }
- }
-
- @DataProvider
- public Object[][] testDecimalExceedingPrecisionMaxProvider()
- {
- return new Object[][] {
- {40, 8},
- {50, 10},
- };
- }
-
- private Session sessionWithDecimalMappingAllowOverflow(RoundingMode roundingMode, int scale)
- {
- return Session.builder(getSession())
- .setCatalogSessionProperty("mysql", DECIMAL_MAPPING, ALLOW_OVERFLOW.name())
- .setCatalogSessionProperty("mysql", DECIMAL_ROUNDING_MODE, roundingMode.name())
- .setCatalogSessionProperty("mysql", DECIMAL_DEFAULT_SCALE, Integer.valueOf(scale).toString())
- .build();
- }
-
- private Session sessionWithDecimalMappingStrict(UnsupportedTypeHandling unsupportedTypeHandling)
- {
- return Session.builder(getSession())
- .setCatalogSessionProperty("mysql", DECIMAL_MAPPING, STRICT.name())
- .setCatalogSessionProperty("mysql", UNSUPPORTED_TYPE_HANDLING, unsupportedTypeHandling.name())
- .build();
- }
-
- @Test
- public void testVarbinary()
- {
- varbinaryTestCases("varbinary(50)")
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_varbinary"));
-
- varbinaryTestCases("tinyblob")
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_varbinary"));
-
- varbinaryTestCases("blob")
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_varbinary"));
-
- varbinaryTestCases("mediumblob")
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_varbinary"));
-
- varbinaryTestCases("longblob")
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_varbinary"));
-
- varbinaryTestCases("varbinary")
- .execute(getQueryRunner(), trinoCreateAsSelect("test_varbinary"));
- }
-
- private SqlDataTypeTest varbinaryTestCases(String insertType)
- {
- return SqlDataTypeTest.create()
- .addRoundTrip(insertType, "NULL", VARBINARY, "CAST(NULL AS varbinary)")
- .addRoundTrip(insertType, "X''", VARBINARY, "X''")
- .addRoundTrip(insertType, "X'68656C6C6F'", VARBINARY, "to_utf8('hello')")
- .addRoundTrip(insertType, "X'5069C4996B6E6120C582C4856B61207720E69DB1E4BAACE983BD'", VARBINARY, "to_utf8('Piękna łąka w 東京都')")
- .addRoundTrip(insertType, "X'4261672066756C6C206F6620F09F92B0'", VARBINARY, "to_utf8('Bag full of 💰')")
- .addRoundTrip(insertType, "X'0001020304050607080DF9367AA7000000'", VARBINARY, "X'0001020304050607080DF9367AA7000000'") // non-text
- .addRoundTrip(insertType, "X'000000000000'", VARBINARY, "X'000000000000'");
- }
-
- @Test
- public void testBinary()
- {
- SqlDataTypeTest.create()
- .addRoundTrip("binary(18)", "NULL", VARBINARY, "CAST(NULL AS varbinary)")
- .addRoundTrip("binary(18)", "X''", VARBINARY, "X'000000000000000000000000000000000000'")
- .addRoundTrip("binary(18)", "X'68656C6C6F'", VARBINARY, "to_utf8('hello') || X'00000000000000000000000000'")
- .addRoundTrip("binary(18)", "X'C582C4856B61207720E69DB1E4BAACE983BD'", VARBINARY, "to_utf8('łąka w 東京都')") // no trailing zeros
- .addRoundTrip("binary(18)", "X'4261672066756C6C206F6620F09F92B0'", VARBINARY, "to_utf8('Bag full of 💰') || X'0000'")
- .addRoundTrip("binary(18)", "X'0001020304050607080DF9367AA7000000'", VARBINARY, "X'0001020304050607080DF9367AA700000000'") // non-text prefix
- .addRoundTrip("binary(18)", "X'000000000000'", VARBINARY, "X'000000000000000000000000000000000000'")
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.test_binary"));
- }
-
- @Test
- public void testDate()
- {
- // Note: there is identical test for PostgreSQL
-
- ZoneId jvmZone = ZoneId.systemDefault();
- checkState(jvmZone.getId().equals("America/Bahia_Banderas"), "This test assumes certain JVM time zone");
- LocalDate dateOfLocalTimeChangeForwardAtMidnightInJvmZone = LocalDate.of(1970, 1, 1);
- verify(jvmZone.getRules().getValidOffsets(dateOfLocalTimeChangeForwardAtMidnightInJvmZone.atStartOfDay()).isEmpty());
-
- ZoneId someZone = ZoneId.of("Europe/Vilnius");
- LocalDate dateOfLocalTimeChangeForwardAtMidnightInSomeZone = LocalDate.of(1983, 4, 1);
- verify(someZone.getRules().getValidOffsets(dateOfLocalTimeChangeForwardAtMidnightInSomeZone.atStartOfDay()).isEmpty());
- LocalDate dateOfLocalTimeChangeBackwardAtMidnightInSomeZone = LocalDate.of(1983, 10, 1);
- verify(someZone.getRules().getValidOffsets(dateOfLocalTimeChangeBackwardAtMidnightInSomeZone.atStartOfDay().minusMinutes(1)).size() == 2);
-
- DataTypeTest testCases = DataTypeTest.create()
- .addRoundTrip(dateDataType(), LocalDate.of(1952, 4, 3)) // before epoch
- .addRoundTrip(dateDataType(), LocalDate.of(1970, 1, 1))
- .addRoundTrip(dateDataType(), LocalDate.of(1970, 2, 3))
- .addRoundTrip(dateDataType(), LocalDate.of(2017, 7, 1)) // summer on northern hemisphere (possible DST)
- .addRoundTrip(dateDataType(), LocalDate.of(2017, 1, 1)) // winter on northern hemisphere (possible DST on southern hemisphere)
- .addRoundTrip(dateDataType(), dateOfLocalTimeChangeForwardAtMidnightInJvmZone)
- .addRoundTrip(dateDataType(), dateOfLocalTimeChangeForwardAtMidnightInSomeZone)
- .addRoundTrip(dateDataType(), dateOfLocalTimeChangeBackwardAtMidnightInSomeZone);
-
- for (String timeZoneId : ImmutableList.of(UTC_KEY.getId(), jvmZone.getId(), someZone.getId())) {
- Session session = Session.builder(getSession())
- .setTimeZoneKey(TimeZoneKey.getTimeZoneKey(timeZoneId))
- .build();
- testCases.execute(getQueryRunner(), session, mysqlCreateAndInsert("tpch.test_date"));
- testCases.execute(getQueryRunner(), session, trinoCreateAsSelect(session, "test_date"));
- testCases.execute(getQueryRunner(), session, trinoCreateAsSelect(getSession(), "test_date"));
- testCases.execute(getQueryRunner(), session, trinoCreateAndInsert(session, "test_date"));
- }
- }
-
- /**
- * Read {@code DATATIME}s inserted by MySQL as Trino {@code TIMESTAMP}s
- */
- @Test(dataProvider = "sessionZonesDataProvider")
- public void testMySqlDatetimeType(ZoneId sessionZone)
- {
- Session session = Session.builder(getSession())
- .setTimeZoneKey(TimeZoneKey.getTimeZoneKey(sessionZone.getId()))
- .build();
-
- SqlDataTypeTest.create()
- // before epoch
- .addRoundTrip("datetime(3)", "TIMESTAMP '1958-01-01 13:18:03.123'", TIMESTAMP_MILLIS, "TIMESTAMP '1958-01-01 13:18:03.123'")
- // after epoch
- .addRoundTrip("datetime(3)", "TIMESTAMP '2019-03-18 10:01:17.987'", TIMESTAMP_MILLIS, "TIMESTAMP '2019-03-18 10:01:17.987'")
- // time doubled in JVM zone
- .addRoundTrip("datetime(3)", "TIMESTAMP '2018-10-28 01:33:17.456'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 01:33:17.456'")
- // time double in Vilnius
- .addRoundTrip("datetime(3)", "TIMESTAMP '2018-10-28 03:33:33.333'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 03:33:33.333'")
- // epoch
- .addRoundTrip("datetime(3)", "TIMESTAMP '1970-01-01 00:00:00.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:00.000'")
- // TODO time gaps do not round-trip (https://github.com/trinodb/trino/issues/6910)
- .addRoundTrip("datetime(3)", "TIMESTAMP '1970-01-01 00:13:42.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:13:42.000'")
- .addRoundTrip("datetime(3)", "TIMESTAMP '2018-04-01 02:13:55.123'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-04-01 03:13:55.123'")
- // time gap in Vilnius
- .addRoundTrip("datetime(3)", "TIMESTAMP '2018-03-25 03:17:17.000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-03-25 03:17:17.000'")
- // time gap in Kathmandu
- .addRoundTrip("datetime(3)", "TIMESTAMP '1986-01-01 00:13:07.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1986-01-01 00:13:07.000'")
-
- // same as above but with higher precision
- .addRoundTrip("datetime(6)", "TIMESTAMP '1958-01-01 13:18:03.123000'", TIMESTAMP_MILLIS, "TIMESTAMP '1958-01-01 13:18:03.123'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '2019-03-18 10:01:17.987000'", TIMESTAMP_MILLIS, "TIMESTAMP '2019-03-18 10:01:17.987'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '2018-10-28 01:33:17.456000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 01:33:17.456'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '2018-10-28 03:33:33.333000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 03:33:33.333'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:00.000'")
- // TODO time gaps do not round-trip (https://github.com/trinodb/trino/issues/6910)
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:13:42.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:13:42.000'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '2018-04-01 02:13:55.123000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-04-01 03:13:55.123'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '2018-03-25 03:17:17.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-03-25 03:17:17.000'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '1986-01-01 00:13:07.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '1986-01-01 00:13:07.000'")
-
- // test arbitrary time for all supported precisions
- .addRoundTrip("datetime(0)", "TIMESTAMP '1970-01-01 00:00:01'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.000'")
- .addRoundTrip("datetime(1)", "TIMESTAMP '1970-01-01 00:00:01.1'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.100'")
- .addRoundTrip("datetime(2)", "TIMESTAMP '1970-01-01 00:00:01.12'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.120'")
- .addRoundTrip("datetime(3)", "TIMESTAMP '1970-01-01 00:00:01.123'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("datetime(4)", "TIMESTAMP '1970-01-01 00:00:01.1234'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("datetime(5)", "TIMESTAMP '1970-01-01 00:00:01.12345'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:01.123456'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
-
- // test rounding for precisions too high for MySQL
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:01.1234560'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:01.12345649999'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:01.1234565'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:01.1234569'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
-
- .execute(getQueryRunner(), session, mysqlCreateAndInsert("tpch.test_datetime"));
-
- SqlDataTypeTest.create()
- // TODO support higher precision timestamps (https://github.com/trinodb/trino/issues/6910)
- // before epoch with second fraction
- //.addRoundTrip("timestamp(7)", "TIMESTAMP '1969-12-31 23:59:59.1230000'", createTimestampType(7), "TIMESTAMP '1969-12-31 23:59:59.1230000'")
- //.addRoundTrip("timestamp(7)", "TIMESTAMP '1969-12-31 23:59:59.1234567'", createTimestampType(7), "TIMESTAMP '1969-12-31 23:59:59.1234567'")
-
- // precision 0 ends up as precision 0
- .addRoundTrip("datetime(0)", "TIMESTAMP '1970-01-01 00:00:01'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:01.000'")
-
- .addRoundTrip("datetime(1)", "TIMESTAMP '1970-01-01 00:00:00.1'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.100'")
- .addRoundTrip("datetime(1)", "TIMESTAMP '1970-01-01 00:00:00.9'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.900'")
- .addRoundTrip("datetime(3)", "TIMESTAMP '1970-01-01 00:00:00.123'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123000'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
- .addRoundTrip("datetime(3)", "TIMESTAMP '1970-01-01 00:00:00.999'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.999'")
- // max supported precision
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123456'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
-
- .addRoundTrip("datetime(1)", "TIMESTAMP '2020-09-27 12:34:56.1'", createTimestampType(3), "TIMESTAMP '2020-09-27 12:34:56.100'")
- .addRoundTrip("datetime(1)", "TIMESTAMP '2020-09-27 12:34:56.9'", createTimestampType(3), "TIMESTAMP '2020-09-27 12:34:56.900'")
- .addRoundTrip("datetime(3)", "TIMESTAMP '2020-09-27 12:34:56.123'", createTimestampType(3), "TIMESTAMP '2020-09-27 12:34:56.123'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '2020-09-27 12:34:56.123000'", createTimestampType(3), "TIMESTAMP '2020-09-27 12:34:56.123'")
- .addRoundTrip("datetime(3)", "TIMESTAMP '2020-09-27 12:34:56.999'", createTimestampType(3), "TIMESTAMP '2020-09-27 12:34:56.999'")
- // max supported precision
- .addRoundTrip("datetime(6)", "TIMESTAMP '2020-09-27 12:34:56.123456'", createTimestampType(3), "TIMESTAMP '2020-09-27 12:34:56.123'")
-
- // round down
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123451'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
-
- // nanos round up, end result rounds down
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123499'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123399'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
-
- // round up
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123999'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.124'")
-
- // max precision
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.123456'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.123'")
-
- // round up to next second
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 00:00:00.999999'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:01.000'")
-
- // round up to next day
- .addRoundTrip("datetime(6)", "TIMESTAMP '1970-01-01 23:59:59.999999'", createTimestampType(3), "TIMESTAMP '1970-01-02 00:00:00.000'")
-
- // negative epoch - round up
- .addRoundTrip("datetime(6)", "TIMESTAMP '1969-12-31 23:59:59.999995'", createTimestampType(3), "TIMESTAMP '1970-01-01 00:00:00.000'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '1969-12-31 23:59:59.999949'", createTimestampType(3), "TIMESTAMP '1970-01-01 00:00:00.000'")
- .addRoundTrip("datetime(6)", "TIMESTAMP '1969-12-31 23:59:59.999994'", createTimestampType(3), "TIMESTAMP '1970-01-01 00:00:00.000'")
-
- .execute(getQueryRunner(), session, mysqlCreateAndInsert("tpch.test_timestamp"));
- }
-
- /**
- * Read {@code TIMESTAMP}s inserted by MySQL as Trino {@code TIMESTAMP}s
- */
- @Test(dataProvider = "sessionZonesDataProvider")
- public void testTimestampFromMySql(ZoneId sessionZone)
- {
- Session session = Session.builder(getSession())
- .setTimeZoneKey(TimeZoneKey.getTimeZoneKey(sessionZone.getId()))
- .build();
-
- // Same as above but with inserts from MySQL - i.e. read path
- SqlDataTypeTest.create()
- // before epoch (MySQL's timestamp type doesn't support values <= epoch)
- //.addRoundTrip("timestamp(3)", "TIMESTAMP '1958-01-01 13:18:03.123'", createTimestampType(3), "TIMESTAMP '1958-01-01 13:18:03.000'")
- // after epoch
- .addRoundTrip("timestamp(3)", "TIMESTAMP '2019-03-18 10:01:17.987'", TIMESTAMP_MILLIS, "TIMESTAMP '2019-03-18 10:01:17.987'")
- // time doubled in JVM zone
- .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-10-28 01:33:17.456'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 01:33:17.456'")
- // time double in Vilnius
- .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-10-28 03:33:33.333'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 03:33:33.333'")
- // epoch (MySQL's timestamp type doesn't support values <= epoch)
- //.addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:00:00.000'", createTimestampType(3), "TIMESTAMP '1970-01-01 01:00:00.000'")
- // TODO time gaps do not round-trip (https://github.com/trinodb/trino/issues/6910)
- .addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:13:42.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:13:42.000'")
- .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-04-01 02:13:55.123'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-04-01 03:13:55.123'")
- // time gap in Vilnius
- .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-03-25 03:17:17.000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-03-25 03:17:17.000'")
- // time gap in Kathmandu
- .addRoundTrip("timestamp(3)", "TIMESTAMP '1986-01-01 00:13:07.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1986-01-01 00:13:07.000'")
-
- // same as above but with higher precision - note that currently anything other than timestamp(3) can only be inserted directly via MySQL
- // MySQL's timestamp type doesn't support values <= epoch
- //.addRoundTrip("timestamp(6)", "TIMESTAMP '1958-01-01 13:18:03.123000'", createTimestampType(3), "TIMESTAMP '1958-01-01 13:18:03.123'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '2019-03-18 10:01:17.987000'", TIMESTAMP_MILLIS, "TIMESTAMP '2019-03-18 10:01:17.987'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '2018-10-28 01:33:17.456000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 01:33:17.456'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '2018-10-28 03:33:33.333000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 03:33:33.333'")
- // MySQL's timestamp type doesn't support values <= epoch
- //.addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:00.000000'", createTimestampType(3), "TIMESTAMP '1970-01-01 00:00:00.000'")
- // TODO time gaps do not round-trip (https://github.com/trinodb/trino/issues/6910)
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:13:42.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:13:42.000'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '2018-04-01 02:13:55.123000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-04-01 03:13:55.123'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '2018-03-25 03:17:17.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-03-25 03:17:17.000'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1986-01-01 00:13:07.000000'", TIMESTAMP_MILLIS, "TIMESTAMP '1986-01-01 00:13:07.000'")
-
- // test arbitrary time for all supported precisions
- .addRoundTrip("timestamp(0)", "TIMESTAMP '1970-01-01 00:00:01'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.000'")
- .addRoundTrip("timestamp(1)", "TIMESTAMP '1970-01-01 00:00:01.1'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.100'")
- .addRoundTrip("timestamp(2)", "TIMESTAMP '1970-01-01 00:00:01.12'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.120'")
- .addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:00:01.123'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("timestamp(4)", "TIMESTAMP '1970-01-01 00:00:01.1234'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("timestamp(5)", "TIMESTAMP '1970-01-01 00:00:01.12345'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.123456'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.1234560'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.12345649999'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.1234565'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.1234569'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
-
- // TODO support higher precision timestamps (https://github.com/trinodb/trino/issues/6910)
- // before epoch with second fraction
- //.addRoundTrip("timestamp(7)", "TIMESTAMP '1969-12-31 23:59:59.1230000'", createTimestampType(7), "TIMESTAMP '1969-12-31 23:59:59.1230000'")
- //.addRoundTrip("timestamp(7)", "TIMESTAMP '1969-12-31 23:59:59.1234567'", createTimestampType(7), "TIMESTAMP '1969-12-31 23:59:59.1234567'")
-
- // precision 0 ends up as precision 0
- .addRoundTrip("timestamp(0)", "TIMESTAMP '1970-01-01 00:00:01'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.000'")
-
- .addRoundTrip("timestamp(1)", "TIMESTAMP '1970-01-01 00:00:01.1'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.100'")
- .addRoundTrip("timestamp(1)", "TIMESTAMP '1970-01-01 00:00:01.9'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.900'")
- .addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:00:01.123'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.123000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:00:01.999'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.999'")
- // max supported precision
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.123456'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
-
- .addRoundTrip("timestamp(1)", "TIMESTAMP '2020-09-27 12:34:56.1'", TIMESTAMP_MILLIS, "TIMESTAMP '2020-09-27 12:34:56.100'")
- .addRoundTrip("timestamp(1)", "TIMESTAMP '2020-09-27 12:34:56.9'", TIMESTAMP_MILLIS, "TIMESTAMP '2020-09-27 12:34:56.900'")
- .addRoundTrip("timestamp(3)", "TIMESTAMP '2020-09-27 12:34:56.123'", TIMESTAMP_MILLIS, "TIMESTAMP '2020-09-27 12:34:56.123'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '2020-09-27 12:34:56.123000'", TIMESTAMP_MILLIS, "TIMESTAMP '2020-09-27 12:34:56.123'")
- .addRoundTrip("timestamp(3)", "TIMESTAMP '2020-09-27 12:34:56.999'", TIMESTAMP_MILLIS, "TIMESTAMP '2020-09-27 12:34:56.999'")
- // max supported precision
- .addRoundTrip("timestamp(6)", "TIMESTAMP '2020-09-27 12:34:56.123456'", TIMESTAMP_MILLIS, "TIMESTAMP '2020-09-27 12:34:56.123'")
-
- // round down
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.12345671'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
-
- // nanos round up, end result rounds down
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.1234567499'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.123456749999'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
-
- // round up
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.12345675'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.123'")
-
- // max precision
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.111222333444'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:01.111'")
-
- // round up to next second
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 00:00:01.99999995'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:02.000'")
-
- // round up to next day
- .addRoundTrip("timestamp(6)", "TIMESTAMP '1970-01-01 23:59:59.99999995'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-02 00:00:00.000'")
-
- // negative epoch (MySQL's timestamp type doesn't support values <= epoch)
- //.addRoundTrip("TIMESTAMP '1969-12-31 23:59:59.99999995'", "TIMESTAMP '1970-01-01 00:00:00.000'")
- //.addRoundTrip("TIMESTAMP '1969-12-31 23:59:59.999999949999'", "TIMESTAMP '1969-12-31 23:59:59.999'")
- //.addRoundTrip("TIMESTAMP '1969-12-31 23:59:59.99999994'", "TIMESTAMP '1969-12-31 23:59:59.999'")
-
- .execute(getQueryRunner(), session, mysqlCreateAndInsert("tpch.test_timestamp"));
- }
-
- @Test(dataProvider = "sessionZonesDataProvider")
- public void testTimestampFromTrino(ZoneId sessionZone)
- {
- Session session = Session.builder(getSession())
- .setTimeZoneKey(TimeZoneKey.getTimeZoneKey(sessionZone.getId()))
- .build();
-
- SqlDataTypeTest.create()
- // before epoch
- .addRoundTrip("timestamp(3)", "TIMESTAMP '1958-01-01 13:18:03.123'", TIMESTAMP_MILLIS, "TIMESTAMP '1958-01-01 13:18:03.123'")
- // after epoch
- .addRoundTrip("timestamp(3)", "TIMESTAMP '2019-03-18 10:01:17.987'", TIMESTAMP_MILLIS, "TIMESTAMP '2019-03-18 10:01:17.987'")
- // time doubled in JVM zone
- .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-10-28 01:33:17.456'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 01:33:17.456'")
- // time double in Vilnius
- .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-10-28 03:33:33.333'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-10-28 03:33:33.333'")
- // epoch
- .addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:00:00.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:00:00.000'")
- // TODO time gaps do not round-trip (https://github.com/trinodb/trino/issues/6910)
- .addRoundTrip("timestamp(3)", "TIMESTAMP '1970-01-01 00:13:42.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1970-01-01 01:13:42.000'")
- .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-04-01 02:13:55.123'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-04-01 03:13:55.123'")
- // time gap in Vilnius
- .addRoundTrip("timestamp(3)", "TIMESTAMP '2018-03-25 03:17:17.000'", TIMESTAMP_MILLIS, "TIMESTAMP '2018-03-25 03:17:17.000'")
- // time gap in Kathmandu
- .addRoundTrip("timestamp(3)", "TIMESTAMP '1986-01-01 00:13:07.000'", TIMESTAMP_MILLIS, "TIMESTAMP '1986-01-01 00:13:07.000'")
-
- .execute(getQueryRunner(), session, trinoCreateAsSelect(session, "test_timestamp"))
- .execute(getQueryRunner(), session, trinoCreateAsSelect(getSession(), "test_timestamp"))
- .execute(getQueryRunner(), session, trinoCreateAndInsert(session, "test_timestamp"));
- }
-
- @DataProvider
- public Object[][] sessionZonesDataProvider()
- {
- return new Object[][] {
- {UTC},
- {ZoneId.systemDefault()},
- // no DST in 1970, but has DST in later years (e.g. 2018)
- {ZoneId.of("Europe/Vilnius")},
- // minutes offset change since 1970-01-01, no DST
- {ZoneId.of("Asia/Kathmandu")},
- {ZoneId.of(TestingSession.DEFAULT_TIME_ZONE_KEY.getId())},
- };
- }
-
- @Test
- public void testJson()
- {
- jsonTestCases(jsonDataType(value -> "JSON " + formatStringLiteral(value)))
- .execute(getQueryRunner(), trinoCreateAsSelect("trino_test_json"));
- jsonTestCases(jsonDataType(value -> format("CAST(%s AS JSON)", formatStringLiteral(value))))
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_json"));
- }
-
- private DataTypeTest jsonTestCases(DataType jsonDataType)
- {
- return DataTypeTest.create()
- .addRoundTrip(jsonDataType, "{}")
- .addRoundTrip(jsonDataType, null)
- .addRoundTrip(jsonDataType, "null")
- .addRoundTrip(jsonDataType, "123.4")
- .addRoundTrip(jsonDataType, "\"abc\"")
- .addRoundTrip(jsonDataType, "\"text with ' apostrophes\"")
- .addRoundTrip(jsonDataType, "\"\"")
- .addRoundTrip(jsonDataType, "{\"a\":1,\"b\":2}")
- .addRoundTrip(jsonDataType, "{\"a\":[1,2,3],\"b\":{\"aa\":11,\"bb\":[{\"a\":1,\"b\":2},{\"a\":0}]}}")
- .addRoundTrip(jsonDataType, "[]");
- }
-
- @Test
- public void testFloat()
- {
- singlePrecisionFloatingPointTests(realDataType())
- .execute(getQueryRunner(), trinoCreateAsSelect("trino_test_float"));
- singlePrecisionFloatingPointTests(mysqlFloatDataType())
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_float"));
- }
-
- @Test
- public void testDouble()
- {
- doublePrecisionFloatingPointTests(doubleDataType())
- .execute(getQueryRunner(), trinoCreateAsSelect("trino_test_double"));
- doublePrecisionFloatingPointTests(mysqlDoubleDataType())
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_double"));
- }
-
- @Test
- public void testUnsignedTypes()
- {
- DataType mysqlUnsignedTinyInt = DataType.dataType("TINYINT UNSIGNED", SmallintType.SMALLINT, Objects::toString);
- DataType mysqlUnsignedSmallInt = DataType.dataType("SMALLINT UNSIGNED", IntegerType.INTEGER, Objects::toString);
- DataType mysqlUnsignedInt = DataType.dataType("INT UNSIGNED", BigintType.BIGINT, Objects::toString);
- DataType mysqlUnsignedInteger = DataType.dataType("INTEGER UNSIGNED", BigintType.BIGINT, Objects::toString);
- DataType mysqlUnsignedBigint = DataType.dataType("BIGINT UNSIGNED", createDecimalType(20), Objects::toString);
-
- DataTypeTest.create()
- .addRoundTrip(mysqlUnsignedTinyInt, (short) 255)
- .addRoundTrip(mysqlUnsignedSmallInt, 65_535)
- .addRoundTrip(mysqlUnsignedInt, 4_294_967_295L)
- .addRoundTrip(mysqlUnsignedInteger, 4_294_967_295L)
- .addRoundTrip(mysqlUnsignedBigint, new BigDecimal("18446744073709551615"))
- .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_unsigned"));
- }
-
- private static DataTypeTest singlePrecisionFloatingPointTests(DataType floatType)
- {
- // we are not testing Nan/-Infinity/+Infinity as those are not supported by MySQL
- return DataTypeTest.create()
- .addRoundTrip(floatType, 3.14f)
- // .addRoundTrip(floatType, 3.1415927f) // Overeagerly rounded by mysql to 3.14159
- .addRoundTrip(floatType, null);
- }
-
- private static DataTypeTest doublePrecisionFloatingPointTests(DataType doubleType)
- {
- // we are not testing Nan/-Infinity/+Infinity as those are not supported by MySQL
- return DataTypeTest.create()
- .addRoundTrip(doubleType, 1.0e100d)
- .addRoundTrip(doubleType, null);
- }
-
- private void testUnsupportedDataType(String databaseDataType)
- {
- SqlExecutor jdbcSqlExecutor = mysqlServer::execute;
- jdbcSqlExecutor.execute(format("CREATE TABLE tpch.test_unsupported_data_type(supported_column varchar(5), unsupported_column %s)", databaseDataType));
- try {
- assertQuery(
- "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'tpch' AND TABLE_NAME = 'test_unsupported_data_type'",
- "VALUES 'supported_column'"); // no 'unsupported_column'
- }
- finally {
- jdbcSqlExecutor.execute("DROP TABLE tpch.test_unsupported_data_type");
- }
- }
-
- private DataSetup trinoCreateAsSelect(String tableNamePrefix)
- {
- return trinoCreateAsSelect(getSession(), tableNamePrefix);
- }
-
- private DataSetup trinoCreateAsSelect(Session session, String tableNamePrefix)
- {
- return new CreateAsSelectDataSetup(new TrinoSqlExecutor(getQueryRunner(), session), tableNamePrefix);
- }
-
- private DataSetup trinoCreateAndInsert(Session session, String tableNamePrefix)
- {
- return new CreateAndInsertDataSetup(new TrinoSqlExecutor(getQueryRunner(), session), tableNamePrefix);
- }
-
- private DataSetup mysqlCreateAndInsert(String tableNamePrefix)
- {
- return new CreateAndInsertDataSetup(mysqlServer::execute, tableNamePrefix);
- }
-
- private static DataType jsonDataType(Function toLiteral)
- {
- return dataType(
- "json",
- JSON,
- toLiteral);
- }
-
- private static DataType mysqlFloatDataType()
- {
- return dataType("float", RealType.REAL, Object::toString);
- }
-
- private static DataType mysqlDoubleDataType()
- {
- return dataType("double precision", DoubleType.DOUBLE, Object::toString);
- }
-
- private static DataType mysqlBinaryDataType(String insertType)
+ @Override
+ protected SqlExecutor onRemoteDatabase()
{
- return dataType(
- insertType,
- VARBINARY,
- bytes -> "X'" + base16().encode(bytes) + "'",
- DataType::binaryLiteral,
- identity());
+ return mysqlServer::execute;
}
}
diff --git a/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestingMariaDbServer.java b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestingMariaDbServer.java
new file mode 100644
index 000000000000..07f2ab9fedd5
--- /dev/null
+++ b/plugin/trino-mysql/src/test/java/io/trino/plugin/mysql/TestingMariaDbServer.java
@@ -0,0 +1,104 @@
+/*
+ * 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.plugin.mysql;
+
+import org.testcontainers.containers.MariaDBContainer;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import static io.trino.testing.containers.TestContainers.startOrReuse;
+import static java.lang.String.format;
+
+public class TestingMariaDbServer
+ implements AutoCloseable
+{
+ private final MariaDBContainer> container;
+ private final Closeable cleanup;
+
+ public TestingMariaDbServer()
+ {
+ this("mariadb:10.5.4");
+ }
+
+ public TestingMariaDbServer(String dockerImageName)
+ {
+ MariaDBContainer> container = new MariaDBContainer<>(dockerImageName);
+ container = container.withDatabaseName("tpch");
+ this.container = container;
+ configureContainer(container);
+ cleanup = startOrReuse(container);
+ execute(format("GRANT ALL PRIVILEGES ON *.* TO '%s'", container.getUsername()), "root", container.getPassword());
+ }
+
+ protected void configureContainer(MariaDBContainer> container) {}
+
+ public Connection createConnection()
+ throws SQLException
+ {
+ return container.createConnection("");
+ }
+
+ public void execute(String sql)
+ {
+ execute(sql, getUsername(), getPassword());
+ }
+
+ public void execute(String sql, String user, String password)
+ {
+ try (Connection connection = DriverManager.getConnection(getJdbcUrl(), user, password);
+ Statement statement = connection.createStatement()) {
+ statement.execute(sql);
+ }
+ catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String getUsername()
+ {
+ return container.getUsername();
+ }
+
+ public String getPassword()
+ {
+ return container.getPassword();
+ }
+
+ public String getDatabaseName()
+ {
+ return container.getDatabaseName();
+ }
+
+ public String getJdbcUrl()
+ {
+ return format("jdbc:mysql://%s:%s?useSSL=false&allowPublicKeyRetrieval=true", container.getContainerIpAddress(), container.getMappedPort(3306));
+ }
+
+ @Override
+ public void close()
+ {
+ try {
+ cleanup.close();
+ }
+ catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index 3abc18c1d3a8..50da85d23f6b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1420,6 +1420,12 @@
+
+ org.mariadb.jdbc
+ mariadb-java-client
+ 2.7.2
+
+
org.openjdk.jol
jol-core
diff --git a/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/env/environment/SinglenodeMariadb.java b/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/env/environment/SinglenodeMariadb.java
new file mode 100644
index 000000000000..2ea8edbe6984
--- /dev/null
+++ b/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/env/environment/SinglenodeMariadb.java
@@ -0,0 +1,83 @@
+/*
+ * 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.launcher.env.environment;
+
+import com.google.common.collect.ImmutableList;
+import io.trino.tests.product.launcher.docker.DockerFiles;
+import io.trino.tests.product.launcher.env.DockerContainer;
+import io.trino.tests.product.launcher.env.Environment;
+import io.trino.tests.product.launcher.env.EnvironmentProvider;
+import io.trino.tests.product.launcher.env.common.Standard;
+import io.trino.tests.product.launcher.env.common.TestsEnvironment;
+import io.trino.tests.product.launcher.testcontainers.PortBinder;
+import org.testcontainers.containers.startupcheck.IsRunningStartupCheckStrategy;
+
+import javax.inject.Inject;
+
+import static io.trino.tests.product.launcher.docker.ContainerUtil.forSelectedPorts;
+import static io.trino.tests.product.launcher.env.EnvironmentContainers.COORDINATOR;
+import static io.trino.tests.product.launcher.env.common.Standard.CONTAINER_PRESTO_ETC;
+import static java.util.Objects.requireNonNull;
+import static org.testcontainers.utility.MountableFile.forHostPath;
+
+@TestsEnvironment
+public final class SinglenodeMariadb
+ extends EnvironmentProvider
+{
+ // Use non-default MySQL port to avoid conflicts with locally installed MySQL if any.
+ public static final int MARIADB_PORT = 23306;
+
+ private final DockerFiles dockerFiles;
+ private final PortBinder portBinder;
+
+ @Inject
+ public SinglenodeMariadb(Standard standard, DockerFiles dockerFiles, PortBinder portBinder)
+ {
+ super(ImmutableList.of(standard));
+ this.dockerFiles = requireNonNull(dockerFiles, "dockerFiles is null");
+ this.portBinder = requireNonNull(portBinder, "portBinder is null");
+ }
+
+ @Override
+ public void extendEnvironment(Environment.Builder builder)
+ {
+ builder.configureContainer(COORDINATOR, container -> container
+ .withCopyFileToContainer(
+ forHostPath(dockerFiles.getDockerFilesHostPath("conf/environment/singlenode-mariadb/mariadb.properties")),
+ CONTAINER_PRESTO_ETC + "/catalog/mariadb.properties"));
+
+ builder.addContainer(createMariaDb());
+ builder.configureContainer("mariadb", container -> container
+ .withCopyFileToContainer(
+ // This custom my.cnf overrides the MariaDB port from 3306 to 23306
+ forHostPath(dockerFiles.getDockerFilesHostPath("conf/environment/singlenode-mariadb/my.cnf")),
+ "/etc/mysql/my.cnf"));
+ }
+
+ @SuppressWarnings("resource")
+ private DockerContainer createMariaDb()
+ {
+ DockerContainer container = new DockerContainer("mariadb:10.5.4", "mariadb")
+ .withEnv("MYSQL_USER", "test")
+ .withEnv("MYSQL_PASSWORD", "test")
+ .withEnv("MYSQL_ROOT_PASSWORD", "test")
+ .withEnv("MYSQL_DATABASE", "test")
+ .withStartupCheckStrategy(new IsRunningStartupCheckStrategy())
+ .waitingFor(forSelectedPorts(MARIADB_PORT));
+
+ portBinder.exposePort(container, MARIADB_PORT);
+
+ return container;
+ }
+}
diff --git a/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/suite/suites/Suite7NonGeneric.java b/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/suite/suites/Suite7NonGeneric.java
index ee68e85e8d7b..7ce2696ee0b9 100644
--- a/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/suite/suites/Suite7NonGeneric.java
+++ b/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/suite/suites/Suite7NonGeneric.java
@@ -18,6 +18,7 @@
import io.trino.tests.product.launcher.env.EnvironmentDefaults;
import io.trino.tests.product.launcher.env.environment.SinglenodeKerberosHdfsImpersonationCrossRealm;
import io.trino.tests.product.launcher.env.environment.SinglenodeLdapBindDn;
+import io.trino.tests.product.launcher.env.environment.SinglenodeMariadb;
import io.trino.tests.product.launcher.env.environment.SinglenodeMysql;
import io.trino.tests.product.launcher.env.environment.SinglenodePostgresql;
import io.trino.tests.product.launcher.env.environment.SinglenodeSparkHive;
@@ -43,6 +44,7 @@ public List getTestRuns(EnvironmentConfig config)
return ImmutableList.of(
testOnEnvironment(SinglenodeMysql.class).withGroups("mysql").build(),
+ testOnEnvironment(SinglenodeMariadb.class).withGroups("mariadb").build(),
testOnEnvironment(SinglenodePostgresql.class).withGroups("postgresql").build(),
testOnEnvironment(SinglenodeSqlserver.class).withGroups("sqlserver").build(),
testOnEnvironment(SinglenodeSparkHive.class).withGroups("hive_spark_bucketing").build(),
diff --git a/testing/trino-product-tests-launcher/src/main/resources/docker/presto-product-tests/conf/environment/singlenode-mariadb/mariadb.properties b/testing/trino-product-tests-launcher/src/main/resources/docker/presto-product-tests/conf/environment/singlenode-mariadb/mariadb.properties
new file mode 100644
index 000000000000..3fb677dc5640
--- /dev/null
+++ b/testing/trino-product-tests-launcher/src/main/resources/docker/presto-product-tests/conf/environment/singlenode-mariadb/mariadb.properties
@@ -0,0 +1,4 @@
+connector.name=mariadb
+connection-url=jdbc:mysql://mariadb:23306
+connection-user=test
+connection-password=test
diff --git a/testing/trino-product-tests-launcher/src/main/resources/docker/presto-product-tests/conf/environment/singlenode-mariadb/my.cnf b/testing/trino-product-tests-launcher/src/main/resources/docker/presto-product-tests/conf/environment/singlenode-mariadb/my.cnf
new file mode 100644
index 000000000000..05db1f1d560c
--- /dev/null
+++ b/testing/trino-product-tests-launcher/src/main/resources/docker/presto-product-tests/conf/environment/singlenode-mariadb/my.cnf
@@ -0,0 +1,26 @@
+# The MariaDB configuration file
+#
+# The MariaDB/MySQL tools read configuration files in the following order:
+# 1. "/etc/mysql/mariadb.cnf" (this file) to set global defaults,
+# 2. "/etc/mysql/conf.d/*.cnf" to set global options.
+# 3. "/etc/mysql/mariadb.conf.d/*.cnf" to set MariaDB-only options.
+# 4. "~/.my.cnf" to set user-specific options.
+#
+# If the same option is defined multiple times, the last one will apply.
+#
+# One can use all long options that the program supports.
+# Run program with --help to get a list of available options and with
+# --print-defaults to see which it would actually understand and use.
+
+#
+# This group is read both by the client and the server
+# use it for options that affect everything
+#
+[client-server]
+
+socket = /run/mysqld/mysqld.sock
+port = 23306
+
+# Import all .cnf files from configuration directory
+!includedir /etc/mysql/conf.d/
+!includedir /etc/mysql/mariadb.conf.d/
diff --git a/testing/trino-product-tests/src/main/java/io/trino/tests/TestGroups.java b/testing/trino-product-tests/src/main/java/io/trino/tests/TestGroups.java
index eef485f1064c..f115f10eba75 100644
--- a/testing/trino-product-tests/src/main/java/io/trino/tests/TestGroups.java
+++ b/testing/trino-product-tests/src/main/java/io/trino/tests/TestGroups.java
@@ -29,6 +29,7 @@ public final class TestGroups
public static final String JDBC = "jdbc";
public static final String OAUTH2 = "oauth2";
public static final String MYSQL = "mysql";
+ public static final String MARIADB = "mariadb";
public static final String TRINO_JDBC = "trino_jdbc";
public static final String QUERY_ENGINE = "qe";
public static final String COMPARISON = "comparison";
diff --git a/testing/trino-product-tests/src/main/java/io/trino/tests/mariadb/TestCreateTableAsSelect.java b/testing/trino-product-tests/src/main/java/io/trino/tests/mariadb/TestCreateTableAsSelect.java
new file mode 100644
index 000000000000..6c4d692d3aa8
--- /dev/null
+++ b/testing/trino-product-tests/src/main/java/io/trino/tests/mariadb/TestCreateTableAsSelect.java
@@ -0,0 +1,48 @@
+/*
+ * 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.mariadb;
+
+import io.trino.tempto.AfterTestWithContext;
+import io.trino.tempto.BeforeTestWithContext;
+import io.trino.tempto.ProductTest;
+import io.trino.tempto.query.QueryResult;
+import org.testng.annotations.Test;
+
+import static io.trino.tempto.assertions.QueryAssert.Row.row;
+import static io.trino.tempto.assertions.QueryAssert.assertThat;
+import static io.trino.tempto.query.QueryExecutor.query;
+import static io.trino.tests.TestGroups.MARIADB;
+import static io.trino.tests.TestGroups.PROFILE_SPECIFIC_TESTS;
+import static io.trino.tests.utils.QueryExecutors.onMariaDb;
+import static java.lang.String.format;
+
+public class TestCreateTableAsSelect
+ extends ProductTest
+{
+ private static final String TABLE_NAME = "test.nation_tmp";
+
+ @BeforeTestWithContext
+ @AfterTestWithContext
+ public void dropTestTable()
+ {
+ onMariaDb().executeQuery(format("DROP TABLE IF EXISTS %s", TABLE_NAME));
+ }
+
+ @Test(groups = {MARIADB, PROFILE_SPECIFIC_TESTS})
+ public void testCreateTableAsSelect()
+ {
+ QueryResult queryResult = query(format("CREATE TABLE mariadb.%s AS SELECT * FROM tpch.tiny.nation", TABLE_NAME));
+ assertThat(queryResult).containsOnly(row(25));
+ }
+}
diff --git a/testing/trino-product-tests/src/main/java/io/trino/tests/utils/QueryExecutors.java b/testing/trino-product-tests/src/main/java/io/trino/tests/utils/QueryExecutors.java
index 1b9aaf060185..044c161db353 100644
--- a/testing/trino-product-tests/src/main/java/io/trino/tests/utils/QueryExecutors.java
+++ b/testing/trino-product-tests/src/main/java/io/trino/tests/utils/QueryExecutors.java
@@ -79,6 +79,11 @@ public static QueryExecutor onMySql()
return testContext().getDependency(QueryExecutor.class, "mysql");
}
+ public static QueryExecutor onMariaDb()
+ {
+ return testContext().getDependency(QueryExecutor.class, "mariadb");
+ }
+
public static QueryExecutor onSpark()
{
return testContext().getDependency(QueryExecutor.class, "spark");
diff --git a/testing/trino-product-tests/src/main/resources/tempto-configuration.yaml b/testing/trino-product-tests/src/main/resources/tempto-configuration.yaml
index eed741db488a..8871155daa0d 100644
--- a/testing/trino-product-tests/src/main/resources/tempto-configuration.yaml
+++ b/testing/trino-product-tests/src/main/resources/tempto-configuration.yaml
@@ -120,6 +120,14 @@ databases:
jdbc_pooling: true
table_manager_type: jdbc
+ mariadb:
+ jdbc_driver_class: com.mysql.jdbc.Driver
+ jdbc_url: jdbc:mysql://mariadb:23306/test
+ jdbc_user: root
+ jdbc_password: test
+ jdbc_pooling: true
+ table_manager_type: jdbc
+
postgres:
jdbc_driver_class: org.postgresql.Driver
jdbc_url: jdbc:postgresql://postgresql:15432/test