-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add extra_properties to hive table properties #17172
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,8 @@ | |
import io.trino.spi.TrinoException; | ||
import io.trino.spi.session.PropertyMetadata; | ||
import io.trino.spi.type.ArrayType; | ||
import io.trino.spi.type.MapType; | ||
import io.trino.spi.type.TypeManager; | ||
|
||
import javax.inject.Inject; | ||
|
||
|
@@ -69,13 +71,15 @@ public class HiveTableProperties | |
public static final String REGEX_CASE_INSENSITIVE = "regex_case_insensitive"; | ||
public static final String TRANSACTIONAL = "transactional"; | ||
public static final String AUTO_PURGE = "auto_purge"; | ||
public static final String EXTRA_PROPERTIES = "extra_properties"; | ||
|
||
private final List<PropertyMetadata<?>> tableProperties; | ||
|
||
@Inject | ||
public HiveTableProperties( | ||
HiveConfig config, | ||
OrcWriterConfig orcWriterConfig) | ||
OrcWriterConfig orcWriterConfig, | ||
TypeManager typeManager) | ||
{ | ||
tableProperties = ImmutableList.of( | ||
stringProperty( | ||
|
@@ -173,7 +177,25 @@ public HiveTableProperties( | |
PARTITION_PROJECTION_LOCATION_TEMPLATE, | ||
"Partition projection location template", | ||
null, | ||
false)); | ||
false), | ||
new PropertyMetadata<>( | ||
EXTRA_PROPERTIES, | ||
"Extra table properties", | ||
new MapType(VARCHAR, VARCHAR, typeManager.getTypeOperators()), | ||
Map.class, | ||
null, | ||
true, // currently not shown in SHOW CREATE TABLE | ||
value -> { | ||
Map<String, String> extraProperties = (Map<String, String>) value; | ||
if (extraProperties.containsValue(null)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added additional validation - but I'm not sure on how to create map with null keys. For |
||
throw new TrinoException(INVALID_TABLE_PROPERTY, format("Extra table property value cannot be null '%s'", extraProperties)); | ||
} | ||
if (extraProperties.containsKey(null)) { | ||
throw new TrinoException(INVALID_TABLE_PROPERTY, format("Extra table property key cannot be null '%s'", extraProperties)); | ||
} | ||
return extraProperties; | ||
}, | ||
value -> value)); | ||
Praveen2112 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
public List<PropertyMetadata<?>> getTableProperties() | ||
|
@@ -311,4 +333,9 @@ public static Optional<Boolean> isAutoPurge(Map<String, Object> tableProperties) | |
{ | ||
return Optional.ofNullable((Boolean) tableProperties.get(AUTO_PURGE)); | ||
} | ||
|
||
public static Optional<Map<String, String>> getExtraProperties(Map<String, Object> tableProperties) | ||
{ | ||
return Optional.ofNullable((Map<String, String>) tableProperties.get(EXTRA_PROPERTIES)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,7 @@ | |
import io.trino.testing.MaterializedResult; | ||
import io.trino.testing.MaterializedResultWithQueryId; | ||
import io.trino.testing.MaterializedRow; | ||
import io.trino.testing.QueryFailedException; | ||
import io.trino.testing.QueryRunner; | ||
import io.trino.testing.TestingConnectorBehavior; | ||
import io.trino.testing.sql.TestTable; | ||
|
@@ -8487,6 +8488,110 @@ public void testCreateAcidTableUnsupported() | |
assertQueryFails("CREATE TABLE acid_unsupported WITH (transactional = true) AS SELECT 123 x", "FileHiveMetastore does not support ACID tables"); | ||
} | ||
|
||
@Test | ||
Praveen2112 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public void testExtraProperties() | ||
{ | ||
String tableName = "create_table_with_multiple_extra_properties_" + randomNameSuffix(); | ||
assertUpdate("CREATE TABLE %s (c1 integer) WITH (extra_properties = MAP(ARRAY['extra.property.one', 'extra.property.two'], ARRAY['one', 'two']))".formatted(tableName)); | ||
|
||
assertQuery( | ||
"SELECT \"extra.property.one\", \"extra.property.two\" FROM \"%s$properties\"".formatted(tableName), | ||
"SELECT 'one', 'two'"); | ||
assertThat(computeActual("SHOW CREATE TABLE %s".formatted(tableName)).getOnlyValue()) | ||
.isEqualTo("CREATE TABLE hive.tpch.%s (\n".formatted(tableName) + | ||
" c1 integer\n" + | ||
")\n" + | ||
"WITH (\n" + | ||
" format = 'ORC'\n" + | ||
")"); | ||
assertUpdate("DROP TABLE %s".formatted(tableName)); | ||
} | ||
|
||
@Test | ||
public void testExtraPropertiesWithCtas() | ||
{ | ||
String tableName = "create_table_ctas_with_multiple_extra_properties_" + randomNameSuffix(); | ||
assertUpdate("CREATE TABLE %s (c1 integer) WITH (extra_properties = MAP(ARRAY['extra.property.one', 'extra.property.two'], ARRAY['one', 'two']))".formatted(tableName)); | ||
|
||
assertQuery( | ||
"SELECT \"extra.property.one\", \"extra.property.two\" FROM \"%s$properties\"".formatted(tableName), | ||
"SELECT 'one', 'two'"); | ||
assertThat(computeActual("SHOW CREATE TABLE %s".formatted(tableName)).getOnlyValue()) | ||
.isEqualTo("CREATE TABLE hive.tpch.%s (\n".formatted(tableName) + | ||
" c1 integer\n" + | ||
")\n" + | ||
"WITH (\n" + | ||
" format = 'ORC'\n" + | ||
")"); | ||
|
||
assertUpdate("DROP TABLE %s".formatted(tableName)); | ||
} | ||
|
||
@Test | ||
public void testShowCreateWithExtraProperties() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. merge this with |
||
{ | ||
String tableName = format("%s.%s.show_create_table_with_extra_properties_%s", getSession().getCatalog().get(), getSession().getSchema().get(), randomNameSuffix()); | ||
assertUpdate("CREATE TABLE %s (c1 integer) WITH (extra_properties = MAP(ARRAY['extra.property.one', 'extra.property.two'], ARRAY['one', 'two']))".formatted(tableName)); | ||
|
||
assertThat(computeActual("SHOW CREATE TABLE " + tableName).getOnlyValue()) | ||
.isEqualTo("CREATE TABLE %s (\n".formatted(tableName) + | ||
" c1 integer\n" + | ||
")\n" + | ||
"WITH (\n" + | ||
" format = 'ORC'\n" + | ||
")"); | ||
|
||
assertUpdate("DROP TABLE %s".formatted(tableName)); | ||
} | ||
|
||
@Test | ||
public void testDuplicateExtraProperties() | ||
{ | ||
assertQueryFails( | ||
"CREATE TABLE create_table_with_duplicate_extra_properties (c1 integer) WITH (extra_properties = MAP(ARRAY['extra.property', 'extra.property'], ARRAY['true', 'false']))", | ||
"Invalid value for catalog 'hive' table property 'extra_properties': Cannot convert.*"); | ||
assertQueryFails( | ||
"CREATE TABLE create_table_select_as_with_duplicate_extra_properties (c1 integer) WITH (extra_properties = MAP(ARRAY['extra.property', 'extra.property'], ARRAY['true', 'false']))", | ||
"Invalid value for catalog 'hive' table property 'extra_properties': Cannot convert.*"); | ||
} | ||
|
||
@Test | ||
public void testOverwriteExistingPropertyWithExtraProperties() | ||
{ | ||
assertThatThrownBy(() -> assertUpdate("CREATE TABLE create_table_with_overwrite_extra_properties (c1 integer) WITH (extra_properties = MAP(ARRAY['transactional'], ARRAY['true']))")) | ||
.isInstanceOf(QueryFailedException.class) | ||
.hasMessage("Illegal keys in extra_properties: [transactional]"); | ||
|
||
assertThatThrownBy(() -> assertUpdate("CREATE TABLE create_table_as_select_with_extra_properties WITH (extra_properties = MAP(ARRAY['rawDataSize'], ARRAY['1'])) AS SELECT 1 as c1")) | ||
.isInstanceOf(QueryFailedException.class) | ||
.hasMessage("Illegal keys in extra_properties: [rawDataSize]"); | ||
} | ||
|
||
@Test | ||
public void testNullExtraProperty() | ||
{ | ||
assertQueryFails( | ||
"CREATE TABLE create_table_with_duplicate_extra_properties (c1 integer) WITH (extra_properties = MAP(ARRAY['null.property'], ARRAY[null]))", | ||
".*Extra table property value cannot be null '\\{null.property=null}'.*"); | ||
assertQueryFails( | ||
"CREATE TABLE create_table_as_select_with_extra_properties WITH (extra_properties = MAP(ARRAY['null.property'], ARRAY[null])) AS SELECT 1 as c1", | ||
".*Extra table property value cannot be null '\\{null.property=null}'.*"); | ||
} | ||
|
||
@Test | ||
public void testCollidingMixedCaseProperty() | ||
{ | ||
String tableName = "create_table_with_mixed_case_extra_properties" + randomNameSuffix(); | ||
|
||
assertUpdate("CREATE TABLE %s (c1 integer) WITH (extra_properties = MAP(ARRAY['one', 'ONE'], ARRAY['one', 'ONE']))".formatted(tableName)); | ||
// TODO: (https://github.com/trinodb/trino/issues/17) This should run successfully | ||
assertThatThrownBy(() -> query("SELECT * FROM \"%s$properties\"".formatted(tableName))) | ||
.isInstanceOf(QueryFailedException.class) | ||
.hasMessageContaining("Multiple entries with same key: one=one and one=one"); | ||
|
||
assertUpdate("DROP TABLE %s".formatted(tableName)); | ||
} | ||
|
||
private static final Set<HiveStorageFormat> NAMED_COLUMN_ONLY_FORMATS = ImmutableSet.of(HiveStorageFormat.AVRO, HiveStorageFormat.JSON); | ||
|
||
@DataProvider | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline, please add a paragraph in the connector docs about how the user can specify extra properties for a hive table and also how they can be retrieved via "$properties" metadata table.
Follow-up PR though
cc @mosabua