Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

import static com.google.common.collect.ImmutableSet.toImmutableSet;
Expand Down Expand Up @@ -82,7 +83,7 @@ public void testNonLowerCaseTableName()
quoted("lower_case_name") + " varchar(1), " +
quoted("Mixed_Case_Name") + " varchar(1), " +
quoted("UPPER_CASE_NAME") + " varchar(1))")) {
onRemoteDatabase().execute("INSERT INTO " + (quoted("SomeSchema") + "." + quoted("NonLowerCaseTable")) + " SELECT 'a', 'b', 'c'");
onRemoteDatabase().execute("INSERT INTO " + (quoted("SomeSchema") + "." + quoted("NonLowerCaseTable")) + " SELECT 'a', 'b', 'c'" + optionalFromDual().orElse(""));
assertQuery(
"SELECT column_name FROM information_schema.columns WHERE table_schema = 'someschema' AND table_name = 'nonlowercasetable'",
"VALUES 'lower_case_name', 'mixed_case_name', 'upper_case_name'");
Expand Down Expand Up @@ -115,6 +116,15 @@ public void testNonLowerCaseTableName()
}
}

/**
* Must return a non-empty optional with the FROM clause to select from a dual table for databases that don't allow a SELECT without a FROM clause like Oracle.
* Must return an empty optional for databases which don't require a FROM clause with a SELECT.
*/
protected Optional<String> optionalFromDual()
{
return Optional.empty();
}

@Test
public void testSchemaNameClash()
throws Exception
Expand Down Expand Up @@ -235,25 +245,27 @@ public void testSchemaNameClashWithRuleMapping()
public void testTableNameRuleMapping()
throws Exception
{
String schema = "remote_schema";
updateRuleBasedIdentifierMappingFile(
getMappingFile(),
ImmutableList.of(),
ImmutableList.of(new TableMappingRule(getSession().getSchema().orElseThrow(), "remote_table", "trino_table")));
ImmutableList.of(new TableMappingRule(schema, "remote_table", "trino_table")));

try (AutoCloseable ignore1 = withTable("remote_table", "(c varchar(5))")) {
assertThat(computeActual("SHOW TABLES").getOnlyColumn())
try (AutoCloseable ignore = withSchema(schema);
AutoCloseable ignore1 = withTable(schema, "remote_table", "(c varchar(5))")) {
assertThat(computeActual("SHOW TABLES FROM " + schema).getOnlyColumn())
.contains("trino_table");
assertQuery("SHOW COLUMNS FROM trino_table", "SELECT 'c', 'varchar(5)', '', ''");
assertUpdate("INSERT INTO trino_table VALUES 'dane'", 1);
assertQuery("SELECT * FROM trino_table", "VALUES 'dane'");
assertQuery("SHOW COLUMNS FROM " + schema + ".trino_table", "SELECT 'c', 'varchar(5)', '', ''");
assertUpdate("INSERT INTO " + schema + ".trino_table VALUES 'dane'", 1);
assertQuery("SELECT * FROM " + schema + ".trino_table", "VALUES 'dane'");
}
}

@Test
public void testTableNameClashWithRuleMapping()
throws Exception
{
String schema = getSession().getSchema().orElseThrow();
String schema = "remote_schema";
List<TableMappingRule> tableMappingRules = ImmutableList.of(
new TableMappingRule(schema, "casesensitivename", "casesensitivename_a"),
new TableMappingRule(schema, "CaseSensitiveName", "casesensitivename_b"),
Expand All @@ -270,21 +282,22 @@ public void testTableNameClashWithRuleMapping()
for (int j = i + 1; j < nameVariants.length; j++) {
String remoteTable = nameVariants[i];
String otherRemoteTable = nameVariants[j];
try (AutoCloseable ignore1 = withTable(remoteTable, "(c varchar(5))");
AutoCloseable ignore2 = withTable(otherRemoteTable, "(d varchar(5))")) {
try (AutoCloseable ignore = withSchema(schema);
AutoCloseable ignore1 = withTable(schema, remoteTable, "(c varchar(5))");
AutoCloseable ignore2 = withTable(schema, otherRemoteTable, "(d varchar(5))")) {
String table = tableMappingRules.stream()
.filter(rule -> rule.getRemoteTable().equals(remoteTable))
.map(TableMappingRule::getMapping)
.collect(onlyElement());

assertThat(computeActual("SHOW TABLES")
assertThat(computeActual("SHOW TABLES FROM " + schema)
.getOnlyColumn()
.map(String.class::cast)
.filter(anObject -> anObject.startsWith("casesensitivename")))
.hasSize(2);
assertQuery("SHOW COLUMNS FROM " + table, "SELECT 'c', 'varchar(5)', '', ''");
assertUpdate("INSERT INTO " + table + " VALUES 'dane'", 1);
assertQuery("SELECT * FROM " + table, "VALUES 'dane'");
assertQuery("SHOW COLUMNS FROM " + schema + "." + table, "SELECT 'c', 'varchar(5)', '', ''");
assertUpdate("INSERT INTO " + schema + "." + table + " VALUES 'dane'", 1);
assertQuery("SELECT * FROM " + schema + "." + table, "VALUES 'dane'");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public void testDescribeTable()
protected TestTable createTableWithDefaultColumns()
{
return new TestTable(
clickhouseServer::execute,
onRemoteDatabase(),
"tpch.tbl",
"(col_required Int64," +
"col_nullable Nullable(Int64)," +
Expand Down Expand Up @@ -456,7 +456,7 @@ public void testAlterInvalidTableProperties()
protected TestTable createTableWithUnsupportedColumn()
{
return new TestTable(
clickhouseServer::execute,
onRemoteDatabase(),
"tpch.test_unsupported_column_present",
"(one bigint, two Array(UInt8), three String) ENGINE=Log");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,170 +15,68 @@

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.trino.testing.AbstractTestQueryFramework;
import io.trino.plugin.jdbc.BaseCaseInsensitiveMappingTest;
import io.trino.testing.QueryRunner;
import io.trino.testing.sql.TestTable;
import org.testng.annotations.AfterClass;
import io.trino.testing.sql.SqlExecutor;
import org.testng.annotations.Test;

import java.util.stream.Stream;
import java.nio.file.Path;

import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static io.trino.plugin.jdbc.mapping.RuleBasedIdentifierMappingUtils.createRuleBasedIdentifierMappingFile;
import static io.trino.plugin.memsql.MemSqlQueryRunner.createMemSqlQueryRunner;
import static io.trino.testing.assertions.Assert.assertEquals;
import static java.lang.String.format;
import static java.util.Locale.ENGLISH;
import static org.assertj.core.api.Assertions.assertThat;
import static java.util.Objects.requireNonNull;

// With case-insensitive-name-matching enabled colliding schema/table names are considered as errors.
// Some tests here create colliding names which can cause any other concurrent test to fail.
@Test(singleThreaded = true)
public class TestMemSqlCaseInsensitiveMapping
// TODO extends BaseCaseInsensitiveMappingTest - https://github.com/trinodb/trino/issues/7864
extends AbstractTestQueryFramework
extends BaseCaseInsensitiveMappingTest
{
protected Path mappingFile;
protected TestingMemSqlServer memSqlServer;

@Override
protected QueryRunner createQueryRunner()
throws Exception
{
memSqlServer = new TestingMemSqlServer();
return createMemSqlQueryRunner(memSqlServer, ImmutableMap.of(), ImmutableMap.of("case-insensitive-name-matching", "true"), ImmutableList.of());
mappingFile = createRuleBasedIdentifierMappingFile();
memSqlServer = closeAfterClass(new TestingMemSqlServer());
return createMemSqlQueryRunner(
memSqlServer,
ImmutableMap.of(),
ImmutableMap.<String, String>builder()
.put("case-insensitive-name-matching", "true")
.put("case-insensitive-name-matching.config-file", mappingFile.toFile().getAbsolutePath())
.put("case-insensitive-name-matching.config-file.refresh-period", "1ms") // ~always refresh
.buildOrThrow(),
ImmutableList.of());
}

@AfterClass(alwaysRun = true)
public final void destroy()
{
memSqlServer.close();
}

@Test
public void testNonLowerCaseSchemaName()
throws Exception
@Override
protected Path getMappingFile()
{
try (AutoCloseable ignore1 = withSchema("NonLowerCaseSchema");
AutoCloseable ignore2 = withTable("NonLowerCaseSchema.lower_case_name", "(c varchar(5))");
AutoCloseable ignore3 = withTable("NonLowerCaseSchema.Mixed_Case_Name", "(c varchar(5))");
AutoCloseable ignore4 = withTable("NonLowerCaseSchema.UPPER_CASE_NAME", "(c varchar(5))")) {
assertThat(computeActual("SHOW SCHEMAS").getOnlyColumn()).contains("nonlowercaseschema");
assertQuery("SHOW SCHEMAS LIKE 'nonlowerc%'", "VALUES 'nonlowercaseschema'");
assertQuery("SELECT schema_name FROM information_schema.schemata WHERE schema_name LIKE '%nonlowercaseschema'", "VALUES 'nonlowercaseschema'");
assertQuery("SHOW TABLES FROM nonlowercaseschema", "VALUES 'lower_case_name', 'mixed_case_name', 'upper_case_name'");
assertQuery("SELECT table_name FROM information_schema.tables WHERE table_schema = 'nonlowercaseschema'", "VALUES 'lower_case_name', 'mixed_case_name', 'upper_case_name'");
assertQueryReturnsEmptyResult("SELECT * FROM nonlowercaseschema.lower_case_name");
}
return requireNonNull(mappingFile, "mappingFile is null");
}

@Test
public void testNonLowerCaseTableName()
throws Exception
@Override
protected SqlExecutor onRemoteDatabase()
{
try (AutoCloseable ignore1 = withSchema("SomeSchema");
AutoCloseable ignore2 = withTable(
"SomeSchema.NonLowerCaseTable", "(lower_case_name varchar(10), Mixed_Case_Name varchar(10), UPPER_CASE_NAME varchar(10))")) {
execute("INSERT INTO SomeSchema.NonLowerCaseTable VALUES ('a', 'b', 'c')");

assertQuery(
"SELECT column_name FROM information_schema.columns WHERE table_schema = 'someschema' AND table_name = 'nonlowercasetable'",
"VALUES 'lower_case_name', 'mixed_case_name', 'upper_case_name'");
assertQuery(
"SELECT column_name FROM information_schema.columns WHERE table_name = 'nonlowercasetable'",
"VALUES 'lower_case_name', 'mixed_case_name', 'upper_case_name'");
assertEquals(
computeActual("SHOW COLUMNS FROM someschema.nonlowercasetable").getMaterializedRows().stream()
.map(row -> row.getField(0))
.collect(toImmutableSet()),
ImmutableSet.of("lower_case_name", "mixed_case_name", "upper_case_name"));

// Note: until https://github.com/prestodb/presto/issues/2863 is resolved, this is *the* way to access the tables.

assertQuery("SELECT lower_case_name FROM someschema.nonlowercasetable", "VALUES 'a'");
assertQuery("SELECT mixed_case_name FROM someschema.nonlowercasetable", "VALUES 'b'");
assertQuery("SELECT upper_case_name FROM someschema.nonlowercasetable", "VALUES 'c'");
assertQuery("SELECT upper_case_name FROM SomeSchema.NonLowerCaseTable", "VALUES 'c'");
assertQuery("SELECT upper_case_name FROM \"SomeSchema\".\"NonLowerCaseTable\"", "VALUES 'c'");

assertUpdate("INSERT INTO someschema.nonlowercasetable (lower_case_name) VALUES ('lower')", 1);
assertUpdate("INSERT INTO someschema.nonlowercasetable (mixed_case_name) VALUES ('mixed')", 1);
assertUpdate("INSERT INTO someschema.nonlowercasetable (upper_case_name) VALUES ('upper')", 1);
assertQuery(
"SELECT * FROM someschema.nonlowercasetable",
"VALUES ('a', 'b', 'c')," +
"('lower', NULL, NULL)," +
"(NULL, 'mixed', NULL)," +
"(NULL, NULL, 'upper')");
}
return requireNonNull(memSqlServer, "memSqlServer is null")::execute;
}

@Test
public void testSchemaNameClash()
throws Exception
@Override
protected String quoted(String name)
{
String[] nameVariants = {"casesensitivename", "CaseSensitiveName", "CASESENSITIVENAME"};
assertThat(Stream.of(nameVariants)
.map(name -> name.toLowerCase(ENGLISH))
.collect(toImmutableSet()))
.hasSize(1);

for (int i = 0; i < nameVariants.length; i++) {
for (int j = i + 1; j < nameVariants.length; j++) {
String schemaName = nameVariants[i];
String otherSchemaName = nameVariants[j];
try (AutoCloseable ignore1 = withSchema(schemaName);
AutoCloseable ignore2 = withSchema(otherSchemaName);
AutoCloseable ignore3 = withTable(schemaName + ".some_table_name", "(c varchar(5))")) {
assertThat(computeActual("SHOW SCHEMAS").getOnlyColumn()).contains("casesensitivename");
assertThat(computeActual("SHOW SCHEMAS").getOnlyColumn().filter("casesensitivename"::equals)).hasSize(1); // TODO change io.trino.plugin.jdbc.JdbcClient.getSchemaNames to return a List
assertQueryFails("SHOW TABLES FROM casesensitivename", "Failed to find remote schema name: Ambiguous name: casesensitivename");
assertQueryFails("SELECT * FROM casesensitivename.some_table_name", "Failed to find remote schema name: Ambiguous name: casesensitivename");
}
}
}
String identifierQuote = "`";
name = name.replace(identifierQuote, identifierQuote + identifierQuote);
return identifierQuote + name + identifierQuote;
}

@Test
public void testTableNameClash()
throws Exception
{
String[] nameVariants = {"casesensitivename", "CaseSensitiveName", "CASESENSITIVENAME"};
assertThat(Stream.of(nameVariants)
.map(name -> name.toLowerCase(ENGLISH))
.collect(toImmutableSet()))
.hasSize(1);

for (int i = 0; i < nameVariants.length; i++) {
for (int j = i + 1; j < nameVariants.length; j++) {
try (AutoCloseable ignore1 = withTable("tpch." + nameVariants[i], "(c varchar(5))");
AutoCloseable ignore2 = withTable("tpch." + nameVariants[j], "(d varchar(5))")) {
assertThat(computeActual("SHOW TABLES").getOnlyColumn()).contains("casesensitivename");
assertThat(computeActual("SHOW TABLES").getOnlyColumn().filter("casesensitivename"::equals)).hasSize(1); // TODO, should be 2
assertQueryFails("SHOW COLUMNS FROM casesensitivename", "Failed to find remote table name: Ambiguous name: casesensitivename");
assertQueryFails("SELECT * FROM casesensitivename", "Failed to find remote table name: Ambiguous name: casesensitivename");
}
}
}
}

private AutoCloseable withSchema(String schemaName)
{
execute(format("CREATE SCHEMA `%s`", schemaName));
return () -> execute(format("DROP SCHEMA `%s`", schemaName));
}

/**
* @deprecated Use {@link TestTable} instead.
*/
@Deprecated
private AutoCloseable withTable(String tableName, String tableDefinition)
{
execute(format("CREATE TABLE %s %s", tableName, tableDefinition));
return () -> execute(format("DROP TABLE %s", tableName));
}

private void execute(String sql)
public void forceTestNgToRespectSingleThreaded()
{
memSqlServer.execute(sql);
// TODO: Remove after updating TestNG to 7.4.0+ (https://github.com/trinodb/trino/issues/8571)
// TestNG doesn't enforce @Test(singleThreaded = true) when tests are defined in base class. According to
// https://github.com/cbeust/testng/issues/2361#issuecomment-688393166 a workaround it to add a dummy test to the leaf test class.
}
}
Loading