diff --git a/plugin/trino-sqlserver/pom.xml b/plugin/trino-sqlserver/pom.xml
index 6cdd6b42231f..e1bd9785445e 100644
--- a/plugin/trino-sqlserver/pom.xml
+++ b/plugin/trino-sqlserver/pom.xml
@@ -164,6 +164,12 @@
test
+
+ org.jetbrains
+ annotations
+ test
+
+
org.testcontainers
mssqlserver
diff --git a/plugin/trino-sqlserver/src/test/java/io/trino/plugin/sqlserver/BaseSqlServerTypeMapping.java b/plugin/trino-sqlserver/src/test/java/io/trino/plugin/sqlserver/BaseSqlServerTypeMapping.java
index 218894e10c57..e9e5d4e55c30 100644
--- a/plugin/trino-sqlserver/src/test/java/io/trino/plugin/sqlserver/BaseSqlServerTypeMapping.java
+++ b/plugin/trino-sqlserver/src/test/java/io/trino/plugin/sqlserver/BaseSqlServerTypeMapping.java
@@ -25,6 +25,7 @@
import io.trino.testing.sql.SqlExecutor;
import io.trino.testing.sql.TestTable;
import io.trino.testing.sql.TrinoSqlExecutor;
+import org.intellij.lang.annotations.Language;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -55,6 +56,7 @@
import static io.trino.testing.sql.TestTable.randomTableSuffix;
import static java.lang.String.format;
import static java.time.ZoneOffset.UTC;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
public abstract class BaseSqlServerTypeMapping
extends AbstractTestQueryFramework
@@ -89,21 +91,114 @@ public void setUp()
}
@Test
- public void testBasicTypes()
+ public void testTrinoBoolean()
{
SqlDataTypeTest.create()
.addRoundTrip("boolean", "null", BOOLEAN, "CAST(NULL AS BOOLEAN)")
.addRoundTrip("boolean", "true", BOOLEAN)
.addRoundTrip("boolean", "false", BOOLEAN)
- .addRoundTrip("bigint", "null", BIGINT, "CAST(NULL AS BIGINT)")
- .addRoundTrip("bigint", "123456789012", BIGINT)
- .addRoundTrip("integer", "null", INTEGER, "CAST(NULL AS INTEGER)")
- .addRoundTrip("integer", "123456789", INTEGER)
- .addRoundTrip("smallint", "null", SMALLINT, "CAST(NULL AS SMALLINT)")
- .addRoundTrip("smallint", "32456", SMALLINT, "SMALLINT '32456'")
- .addRoundTrip("tinyint", "null", TINYINT, "CAST(NULL AS TINYINT)")
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_boolean"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_boolean"));
+ }
+
+ @Test
+ public void testSqlServerBit()
+ {
+ SqlDataTypeTest.create()
+ .addRoundTrip("bit", "null", BOOLEAN, "CAST(NULL AS BOOLEAN)")
+ .addRoundTrip("bit", "1", BOOLEAN, "true")
+ .addRoundTrip("bit", "0", BOOLEAN, "false")
+ .execute(getQueryRunner(), sqlServerCreateAndInsert("test_bit"));
+ }
+
+ @Test
+ public void testTinyint()
+ {
+ // TODO: Add min/max/min-1/max+1 tests (https://github.com/trinodb/trino/issues/11209)
+ SqlDataTypeTest.create()
+ .addRoundTrip("tinyint", "NULL", TINYINT, "CAST(NULL AS TINYINT)")
.addRoundTrip("tinyint", "5", TINYINT, "TINYINT '5'")
- .execute(getQueryRunner(), trinoCreateAsSelect("test_basic_types"));
+ .execute(getQueryRunner(), sqlServerCreateAndInsert("test_tinyint"))
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_tinyint"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_tinyint"));
+ }
+
+ @Test
+ public void testSmallint()
+ {
+ SqlDataTypeTest.create()
+ .addRoundTrip("smallint", "NULL", SMALLINT, "CAST(NULL AS SMALLINT)")
+ .addRoundTrip("smallint", "-32768", SMALLINT, "SMALLINT '-32768'") // min value in SQL Server and Trino
+ .addRoundTrip("smallint", "32456", SMALLINT, "SMALLINT '32456'")
+ .addRoundTrip("smallint", "32767", SMALLINT, "SMALLINT '32767'") // max value in SQL Server and Trino
+ .execute(getQueryRunner(), sqlServerCreateAndInsert("test_smallint"))
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_smallint"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_smallint"));
+ }
+
+ @Test
+ public void testUnsupportedSmallint()
+ {
+ try (TestTable table = new TestTable(onRemoteDatabase(), "test_unsupported_smallint", "(data smallint)")) {
+ assertSqlServerQueryFails(
+ format("INSERT INTO %s VALUES (-32769)", table.getName()), // min - 1
+ "Arithmetic overflow error for data type smallint, value = -32769.");
+ assertSqlServerQueryFails(
+ format("INSERT INTO %s VALUES (32768)", table.getName()), // max + 1
+ "Arithmetic overflow error for data type smallint, value = 32768.");
+ }
+ }
+
+ @Test
+ public void testInteger()
+ {
+ SqlDataTypeTest.create()
+ .addRoundTrip("integer", "NULL", INTEGER, "CAST(NULL AS INTEGER)")
+ .addRoundTrip("integer", "-2147483648", INTEGER, "-2147483648") // min value in SQL Server and Trino
+ .addRoundTrip("integer", "1234567890", INTEGER, "1234567890")
+ .addRoundTrip("integer", "2147483647", INTEGER, "2147483647") // max value in SQL Server and Trino
+ .execute(getQueryRunner(), sqlServerCreateAndInsert("test_int"))
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_int"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_int"));
+ }
+
+ @Test
+ public void testUnsupportedInteger()
+ {
+ try (TestTable table = new TestTable(onRemoteDatabase(), "test_unsupported_integer", "(data integer)")) {
+ assertSqlServerQueryFails(
+ format("INSERT INTO %s VALUES (-2147483649)", table.getName()), // min - 1
+ "Arithmetic overflow error converting expression to data type int.");
+ assertSqlServerQueryFails(
+ format("INSERT INTO %s VALUES (2147483648)", table.getName()), // max + 1
+ "Arithmetic overflow error converting expression to data type int.");
+ }
+ }
+
+ @Test
+ public void testBigint()
+ {
+ SqlDataTypeTest.create()
+ .addRoundTrip("bigint", "NULL", BIGINT, "CAST(NULL AS BIGINT)")
+ .addRoundTrip("bigint", "-9223372036854775808", BIGINT, "-9223372036854775808") // min value in SQL Server and Trino
+ .addRoundTrip("bigint", "123456789012", BIGINT, "123456789012")
+ .addRoundTrip("bigint", "9223372036854775807", BIGINT, "9223372036854775807") // max value in SQL Server and Trino
+ .execute(getQueryRunner(), sqlServerCreateAndInsert("test_bigint"))
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_bigint"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_bigint"));
+ }
+
+ @Test
+ public void testUnsupportedBigint()
+ {
+ try (TestTable table = new TestTable(onRemoteDatabase(), "test_unsupported_bigint", "(data bigint)")) {
+ assertSqlServerQueryFails(
+ format("INSERT INTO %s VALUES (-9223372036854775809)", table.getName()), // min - 1
+ "Arithmetic overflow error converting expression to data type bigint.");
+ assertSqlServerQueryFails(
+ format("INSERT INTO %s VALUES (9223372036854775808)", table.getName()), // max + 1
+ "Arithmetic overflow error converting expression to data type bigint.");
+ }
}
@Test
@@ -120,7 +215,8 @@ public void testReal()
.addRoundTrip("real", "NULL", REAL, "CAST(NULL AS real)")
.addRoundTrip("real", "3.14", REAL, "REAL '3.14'")
.addRoundTrip("real", "3.1415927", REAL, "REAL '3.1415927'")
- .execute(getQueryRunner(), trinoCreateAsSelect("test_real"));
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_real"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_real"));
}
@Test
@@ -178,7 +274,8 @@ public void testDecimal()
.addRoundTrip("decimal(30, 5)", "3141592653589793238462643.38327", createDecimalType(30, 5), "CAST('3141592653589793238462643.38327' AS decimal(30, 5))")
.addRoundTrip("decimal(30, 5)", "-3141592653589793238462643.38327", createDecimalType(30, 5), "CAST('-3141592653589793238462643.38327' AS decimal(30, 5))")
.execute(getQueryRunner(), sqlServerCreateAndInsert("test_decimal"))
- .execute(getQueryRunner(), trinoCreateAsSelect("test_decimal"));
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_decimal"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_decimal"));
}
@Test
@@ -199,7 +296,7 @@ public void testChar()
.addRoundTrip("char(32)", "CAST('攻殻機動隊' AS char(32))", createCharType(32), "CAST('攻殻機動隊' AS char(32))")
.addRoundTrip("char(20)", "CAST('😂' AS char(20))", createCharType(20), "CAST('😂' AS char(20))")
.addRoundTrip("char(77)", "CAST('Ну, погоди!' AS char(77))", createCharType(77), "CAST('Ну, погоди!' AS char(77))")
- .execute(getQueryRunner(), trinoCreateAndInsert(getSession(), "test_char"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_char"))
.execute(getQueryRunner(), trinoCreateAsSelect("test_char"));
}
@@ -219,7 +316,7 @@ public void testTrinoLongChar()
// testing mapping char > 4000 -> varchar(max)
SqlDataTypeTest.create()
.addRoundTrip("char(4001)", "'text_c'", createUnboundedVarcharType(), "VARCHAR 'text_c'")
- .execute(getQueryRunner(), trinoCreateAndInsert(getSession(), "test_long_char"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_long_char"))
.execute(getQueryRunner(), trinoCreateAsSelect("test_long_char"));
}
@@ -239,7 +336,7 @@ public void testVarchar()
.addRoundTrip("varchar(32)", "CAST('攻殻機動隊' AS varchar(32))", createVarcharType(32), "CAST('攻殻機動隊' AS varchar(32))")
.addRoundTrip("varchar(20)", "CAST('😂' AS varchar(20))", createVarcharType(20), "CAST('😂' AS varchar(20))")
.addRoundTrip("varchar(77)", "CAST('Ну, погоди!' AS varchar(77))", createVarcharType(77), "CAST('Ну, погоди!' AS varchar(77))")
- .execute(getQueryRunner(), trinoCreateAndInsert(getSession(), "test_varchar"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_varchar"))
.execute(getQueryRunner(), trinoCreateAsSelect("test_varchar"));
}
@@ -262,7 +359,7 @@ public void testTrinoLongVarchar()
// testing mapping varchar > 4000 -> varchar(max)
SqlDataTypeTest.create()
.addRoundTrip("varchar(4001)", "'text_c'", createUnboundedVarcharType(), "VARCHAR 'text_c'")
- .execute(getQueryRunner(), trinoCreateAndInsert(getSession(), "test_long_varchar"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_long_varchar"))
.execute(getQueryRunner(), trinoCreateAsSelect("test_long_varchar"));
}
@@ -288,7 +385,7 @@ public void testTrinoUnboundedVarchar()
.addRoundTrip("varchar", "VARCHAR '😂'", createUnboundedVarcharType(), "VARCHAR '😂'")
.addRoundTrip("varchar", "VARCHAR 'Ну, погоди!'", createUnboundedVarcharType(), "VARCHAR 'Ну, погоди!'")
.addRoundTrip("varchar", "'text_f'", createUnboundedVarcharType(), "VARCHAR 'text_f'")
- .execute(getQueryRunner(), trinoCreateAndInsert(getSession(), "test_unbounded_varchar"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_unbounded_varchar"))
.execute(getQueryRunner(), trinoCreateAsSelect("test_unbounded_varchar"));
}
@@ -303,7 +400,8 @@ public void testVarbinary()
.addRoundTrip("varbinary", "X'4261672066756C6C206F6620F09F92B0'", VARBINARY, "to_utf8('Bag full of 💰')")
.addRoundTrip("varbinary", "X'0001020304050607080DF9367AA7000000'", VARBINARY, "X'0001020304050607080DF9367AA7000000'") // non-text
.addRoundTrip("varbinary", "X'000000000000'", VARBINARY, "X'000000000000'")
- .execute(getQueryRunner(), trinoCreateAsSelect("test_varbinary"));
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_varbinary"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_varbinary"));
// Binary literals must be prefixed with 0x
// https://docs.microsoft.com/en-us/sql/analytics-platform-system/load-with-insert?view=aps-pdw-2016-au7#InsertingLiteralsBinary
@@ -447,8 +545,8 @@ public void testTime()
.addRoundTrip("TIME '23:59:59.999999'", "TIME '23:59:59.999999'")
.addRoundTrip("TIME '23:59:59.9999999'", "TIME '23:59:59.9999999'")
- .execute(getQueryRunner(), trinoCreateAsSelect(getSession(), "test_time"))
- .execute(getQueryRunner(), trinoCreateAndInsert(getSession(), "test_time"));
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_time"))
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_time"));
SqlDataTypeTest.create()
// round down
@@ -479,8 +577,8 @@ public void testTime()
// round down
.addRoundTrip("TIME '23:59:59.999999949999'", "TIME '23:59:59.9999999'")
- .execute(getQueryRunner(), trinoCreateAndInsert(getSession(), "test_time"))
- .execute(getQueryRunner(), trinoCreateAsSelect(getSession(), "test_time"));
+ .execute(getQueryRunner(), trinoCreateAndInsert("test_time"))
+ .execute(getQueryRunner(), trinoCreateAsSelect("test_time"));
}
@Test(dataProvider = "sessionZonesDataProvider")
@@ -583,7 +681,7 @@ public void testTimestamp(ZoneId sessionZone)
.build();
tests.execute(getQueryRunner(), session, trinoCreateAsSelect(session, "test_timestamp"));
- tests.execute(getQueryRunner(), session, trinoCreateAsSelect(getSession(), "test_timestamp"));
+ tests.execute(getQueryRunner(), session, trinoCreateAsSelect("test_timestamp"));
tests.execute(getQueryRunner(), session, trinoCreateAndInsert(session, "test_timestamp"));
}
@@ -733,6 +831,11 @@ protected DataSetup trinoCreateAsSelect(Session session, String tableNamePrefix)
return new CreateAsSelectDataSetup(new TrinoSqlExecutor(getQueryRunner(), session), tableNamePrefix);
}
+ protected DataSetup trinoCreateAndInsert(String tableNamePrefix)
+ {
+ return new CreateAndInsertDataSetup(new TrinoSqlExecutor(getQueryRunner(), getSession()), tableNamePrefix);
+ }
+
protected DataSetup trinoCreateAndInsert(Session session, String tableNamePrefix)
{
return new CreateAndInsertDataSetup(new TrinoSqlExecutor(getQueryRunner(), session), tableNamePrefix);
@@ -758,6 +861,13 @@ private static void checkIsGap(ZoneId zone, LocalDateTime dateTime)
verify(isGap(zone, dateTime), "Expected %s to be a gap in %s", dateTime, zone);
}
+ private void assertSqlServerQueryFails(@Language("SQL") String sql, String expectedMessage)
+ {
+ assertThatThrownBy(() -> onRemoteDatabase().execute(sql))
+ .getCause()
+ .hasMessageContaining(expectedMessage);
+ }
+
protected SqlExecutor onRemoteDatabase()
{
return sqlServer::execute;