Skip to content

Commit

Permalink
Allow using legacy Hive view translation logic
Browse files Browse the repository at this point in the history
  • Loading branch information
losipiuk committed Dec 3, 2020
1 parent 630a0ae commit 16c38d0
Show file tree
Hide file tree
Showing 8 changed files with 743 additions and 2 deletions.
15 changes: 15 additions & 0 deletions presto-hive/src/main/java/io/prestosql/plugin/hive/HiveConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ public class HiveConfig

private boolean optimizeSymlinkListing = true;

private boolean legacyHiveViewTranslation;

public int getMaxInitialSplits()
{
return maxInitialSplits;
Expand Down Expand Up @@ -1024,4 +1026,17 @@ public HiveConfig setOptimizeSymlinkListing(boolean optimizeSymlinkListing)
this.optimizeSymlinkListing = optimizeSymlinkListing;
return this;
}

@Config("hive.legacy-hive-view-translation")
@ConfigDescription("Use legacy Hive view translation mechanism")
public HiveConfig setLegacyHiveViewTranslation(boolean legacyHiveViewTranslation)
{
this.legacyHiveViewTranslation = legacyHiveViewTranslation;
return this;
}

public Boolean isLegacyHiveViewTranslation()
{
return this.legacyHiveViewTranslation;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* 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.prestosql.plugin.hive;

import com.google.common.collect.Lists;
import com.google.common.collect.PeekingIterator;
import io.prestosql.spi.PrestoException;

import static com.google.common.collect.Iterators.peekingIterator;
import static io.prestosql.plugin.hive.HiveErrorCode.HIVE_VIEW_TRANSLATION_ERROR;
import static org.apache.hadoop.hive.ql.parse.BaseSemanticAnalyzer.unescapeSQLString;

/**
* Translate statements in Hive QL to Presto SQL.
*
* Only translation of quoted literals is currently included.
*/
public final class HiveQlToPrestoTranslator
{
// Translation methods consume data from the iterator
private final PeekingIterator<Character> input;
private final StringBuilder output = new StringBuilder();

/**
* Translate a HiveQL statement to Presto SQL by fixing quoted identifiers
* and string literals. No other translation is performed.
*
* <p>Backquotes are replaced with double quotes, including SQL-style
* escapes (`` becomes ` and " becomes "").
*
* <p>Single and double quotes are replaced with single quotes, with
* minimal processing of escape sequences to ensure that the strings end in
* the right place.
*/
public static String translateHiveViewToPresto(String hiveStatement)
{
HiveQlToPrestoTranslator translator = new HiveQlToPrestoTranslator(hiveStatement);
return translator.translateQuotedLiterals();
}

private HiveQlToPrestoTranslator(String hiveQl)
{
input = peekingIterator(Lists.charactersOf(hiveQl).iterator());
}

private String translateQuotedLiterals()
{
// Consume input, passing control to other translation methods when
// their delimiters are encountered.
while (input.hasNext()) {
char c = input.next();
switch (c) {
case '"':
case '\'':
translateString(c);
break;
case '`':
translateQuotedIdentifier();
break;
default:
output.append(c);
break;
}
}
return output.toString();
}

private void translateString(char delimiter)
{
// Build a copy of the string to pass to Hive's string unescaper
StringBuilder string = new StringBuilder(String.valueOf(delimiter));
while (input.hasNext()) {
char c = input.next();

if (c == delimiter) {
string.append(delimiter);
String unescaped = unescapeSQLString(string.toString());
output.append("'");
output.append(unescaped.replace("'", "''"));
output.append("'");
return;
}

string.append(c);

if (c == '\\') {
if (!input.hasNext()) {
break; // skip to end-of-input error
}
string.append(input.next());
}
}
throw hiveViewParseError("unexpected end of input in string");
}

private void translateQuotedIdentifier()
{
output.append('"');
while (input.hasNext()) {
char c = input.next();

if (c == '"') {
// escape " as ""
output.append("\"\"");
}
else if (c == '`' && input.hasNext() && input.peek() == '`') {
// un-escape `` as `
output.append('`');
input.next();
}
else if (c == '`') {
// end of identifier
output.append('"');
return;
}
else {
// don't change characters besides ` and "
output.append(c);
}
}
throw hiveViewParseError("unexpected end of input in identifier");
}

private static PrestoException hiveViewParseError(String message)
{
return new PrestoException(HIVE_VIEW_TRANSLATION_ERROR, "Error translating Hive view to Presto: " + message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public final class HiveSessionProperties
private static final String PARQUET_OPTIMIZED_WRITER_ENABLED = "experimental_parquet_optimized_writer_enabled";
private static final String DYNAMIC_FILTERING_PROBE_BLOCKING_TIMEOUT = "dynamic_filtering_probe_blocking_timeout";
private static final String OPTIMIZE_SYMLINK_LISTING = "optimize_symlink_listing";
private static final String LEGACY_HIVE_VIEW_TRANSLATION = "legacy_hive_view_translation";

private final List<PropertyMetadata<?>> sessionProperties;

Expand Down Expand Up @@ -396,6 +397,11 @@ public HiveSessionProperties(
OPTIMIZE_SYMLINK_LISTING,
"Optimize listing for SymlinkTextFormat tables with files in a single directory",
hiveConfig.isOptimizeSymlinkListing(),
false),
booleanProperty(
LEGACY_HIVE_VIEW_TRANSLATION,
"Use legacy Hive view translation mechanism",
hiveConfig.isLegacyHiveViewTranslation(),
false));
}

Expand Down Expand Up @@ -677,4 +683,9 @@ public static boolean isOptimizeSymlinkListing(ConnectorSession session)
{
return session.getProperty(OPTIMIZE_SYMLINK_LISTING, Boolean.class);
}

public static boolean isLegacyHiveViewTranslation(ConnectorSession session)
{
return session.getProperty(LEGACY_HIVE_VIEW_TRANSLATION, Boolean.class);
}
}
Original file line number Diff line number Diff line change
@@ -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.prestosql.plugin.hive;

import io.prestosql.plugin.base.CatalogName;
import io.prestosql.plugin.hive.metastore.Table;
import io.prestosql.spi.PrestoException;
import io.prestosql.spi.connector.ConnectorViewDefinition;
import io.prestosql.spi.type.TypeId;

import java.util.Optional;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static io.prestosql.plugin.hive.HiveErrorCode.HIVE_INVALID_METADATA;
import static io.prestosql.plugin.hive.HiveMetadata.TABLE_COMMENT;
import static io.prestosql.plugin.hive.HiveQlToPrestoTranslator.translateHiveViewToPresto;

public class LegacyHiveViewReader
implements ViewReaderUtil.ViewReader
{
@Override
public ConnectorViewDefinition decodeViewData(String viewData, Table table, CatalogName catalogName)
{
String viewText = table.getViewExpandedText()
.orElseThrow(() -> new PrestoException(HIVE_INVALID_METADATA, "No view expanded text: " + table.getSchemaTableName()));
return new ConnectorViewDefinition(
translateHiveViewToPresto(viewText),
Optional.of(catalogName.toString()),
Optional.ofNullable(table.getDatabaseName()),
table.getDataColumns().stream()
.map(column -> new ConnectorViewDefinition.ViewColumn(column.getName(), TypeId.of(column.getType().getTypeSignature().toString())))
.collect(toImmutableList()),
Optional.ofNullable(table.getParameters().get(TABLE_COMMENT)),
Optional.of(table.getOwner()),
false); // don't run as invoker
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import static io.prestosql.plugin.hive.HiveErrorCode.HIVE_INVALID_VIEW_DATA;
import static io.prestosql.plugin.hive.HiveErrorCode.HIVE_VIEW_TRANSLATION_ERROR;
import static io.prestosql.plugin.hive.HiveMetadata.TABLE_COMMENT;
import static io.prestosql.plugin.hive.HiveSessionProperties.isLegacyHiveViewTranslation;
import static io.prestosql.plugin.hive.util.HiveUtil.checkCondition;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
Expand All @@ -65,6 +66,10 @@ public static ViewReader createViewReader(SemiTransactionalHiveMetastore metasto
if (isPrestoView(table)) {
return new PrestoViewReader();
}
if (isLegacyHiveViewTranslation(session)) {
return new LegacyHiveViewReader();
}

return new HiveViewReader(new CoralSemiTransactionalHiveMSCAdapter(metastore, new HiveIdentity(session)), typemanager);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ public void testDefaults()
.setProjectionPushdownEnabled(true)
.setDynamicFilteringProbeBlockingTimeout(new Duration(0, TimeUnit.MINUTES))
.setTimestampPrecision(HiveTimestampPrecision.MILLISECONDS)
.setOptimizeSymlinkListing(true));
.setOptimizeSymlinkListing(true)
.setLegacyHiveViewTranslation(false));
}

@Test
Expand Down Expand Up @@ -172,6 +173,7 @@ public void testExplicitPropertyMappings()
.put("hive.dynamic-filtering-probe-blocking-timeout", "10s")
.put("hive.timestamp-precision", "NANOSECONDS")
.put("hive.optimize-symlink-listing", "false")
.put("hive.legacy-hive-view-translation", "true")
.build();

HiveConfig expected = new HiveConfig()
Expand Down Expand Up @@ -239,7 +241,8 @@ public void testExplicitPropertyMappings()
.setProjectionPushdownEnabled(false)
.setDynamicFilteringProbeBlockingTimeout(new Duration(10, TimeUnit.SECONDS))
.setTimestampPrecision(HiveTimestampPrecision.NANOSECONDS)
.setOptimizeSymlinkListing(false);
.setOptimizeSymlinkListing(false)
.setLegacyHiveViewTranslation(true);

assertFullMapping(properties, expected);
}
Expand Down
Loading

0 comments on commit 16c38d0

Please sign in to comment.