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 @@ -19,13 +19,15 @@
package org.apache.iceberg.view;

import java.util.List;
import org.apache.iceberg.Schema;
import javax.annotation.Nullable;
import org.apache.iceberg.catalog.Namespace;
import org.immutables.value.Value;

@Value.Immutable
public interface SQLViewRepresentation extends ViewRepresentation {

@Override
default Type type() {
default String type() {
return Type.SQL;
}

Expand All @@ -36,13 +38,19 @@ default Type type() {
String dialect();

/** The default catalog when the view is created. */
@Nullable
String defaultCatalog();

/** The default namespace when the view is created. */
@Nullable
Namespace defaultNamespace();

/** The query output schema at version create time, without aliases. */
Schema schema();
/**
* The query output schema ID at version create time, without aliases or null if no schema is
* defined
*/
@Nullable
Integer schemaId();

/** The view field comments. */
List<String> fieldComments();
Expand Down
17 changes: 6 additions & 11 deletions api/src/main/java/org/apache/iceberg/view/ViewRepresentation.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,16 @@
*/
package org.apache.iceberg.view;

import java.util.Locale;
import org.immutables.value.Value;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here: javax.annotation.Nullable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ViewRepresentations currently has a single required "type" field so it wasn't marked as Nullable to begin with. Let me know if I'm missing something!


@Value.Immutable
public interface ViewRepresentation {

enum Type {
SQL;
class Type {
private Type() {}

public static Type fromString(String typeName) {
return valueOf(typeName.toUpperCase(Locale.ENGLISH));
}

public String typeName() {
return name().toLowerCase(Locale.ENGLISH);
}
public static final String SQL = "sql";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change this from an enum to a String?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the suggestion since I see some other enum like classes are also implemented directly as strings, such as DataOperations, and it seems to simplify the code a bit. But if there is a specific reason for using enum we can stick to that.

Copy link
Contributor Author

@amogh-jahagirdar amogh-jahagirdar Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it seems like an established pattern elsewhere just to use a constant string and it simplifies the parsing logic a bit but I'm happy to revert back to enum if there's other advantages. Let me know your thoughts @rdblue @jzhuge

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it depends on whether we want to use this enum in switch statements in our code or if we want to extend it. For example, we could have a reference to the parser in the enum so we look up the symbol and then call something like ViewRepresentation.SQL.parse(jsonNode).

Since we only have the parser selection right now, it doesn't seem like it matters much.

}

Type type();
String type();
}
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ project(':iceberg-api') {
dependencies {
implementation project(path: ':iceberg-bundled-guava', configuration: 'shadow')
compileOnly "com.google.errorprone:error_prone_annotations"
compileOnly 'com.google.code.findbugs:jsr305'
annotationProcessor "org.immutables:value"
compileOnly "org.immutables:value"
testImplementation "org.apache.avro:avro"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.iceberg.view;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import org.apache.iceberg.catalog.Namespace;
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
import org.apache.iceberg.relocated.com.google.common.collect.Iterables;
import org.apache.iceberg.util.JsonUtil;

class SQLViewRepresentationParser {
private static final String SQL = "sql";
private static final String DIALECT = "dialect";
private static final String SCHEMA_ID = "schema-id";
private static final String DEFAULT_CATALOG = "default-catalog";
private static final String DEFAULT_NAMESPACE = "default-namespace";
private static final String FIELD_ALIASES = "field-aliases";
private static final String FIELD_COMMENTS = "field-comments";

private SQLViewRepresentationParser() {}

static String toJson(SQLViewRepresentation sqlViewRepresentation) {
return JsonUtil.generate(gen -> toJson(sqlViewRepresentation, gen), false);
}

static void toJson(SQLViewRepresentation view, JsonGenerator generator) throws IOException {
Preconditions.checkArgument(view != null, "Invalid SQL view representation: null");
generator.writeStartObject();
generator.writeStringField(ViewRepresentationParser.TYPE, view.type());
generator.writeStringField(SQL, view.sql());
generator.writeStringField(DIALECT, view.dialect());

if (view.schemaId() != null) {
generator.writeNumberField(SCHEMA_ID, view.schemaId());
}

if (view.defaultCatalog() != null) {
generator.writeStringField(DEFAULT_CATALOG, view.defaultCatalog());
}

if (view.defaultNamespace() != null) {
JsonUtil.writeStringArray(
DEFAULT_NAMESPACE, Arrays.asList(view.defaultNamespace().levels()), generator);
}

if (!view.fieldAliases().isEmpty()) {
JsonUtil.writeStringArray(
SQLViewRepresentationParser.FIELD_ALIASES, view.fieldAliases(), generator);
}

if (!view.fieldComments().isEmpty()) {
JsonUtil.writeStringArray(
SQLViewRepresentationParser.FIELD_COMMENTS, view.fieldComments(), generator);
}

generator.writeEndObject();
}

static SQLViewRepresentation fromJson(String json) {
return JsonUtil.parse(json, SQLViewRepresentationParser::fromJson);
}

static SQLViewRepresentation fromJson(JsonNode node) {
Preconditions.checkArgument(
node != null, "Cannot parse SQL view representation from null object");
Preconditions.checkArgument(
node.isObject(), "Cannot parse SQL view representation from non-object: %s", node);
ImmutableSQLViewRepresentation.Builder builder =
ImmutableSQLViewRepresentation.builder()
.sql(JsonUtil.getString(SQL, node))
.dialect(JsonUtil.getString(DIALECT, node));
String defaultCatalog = JsonUtil.getStringOrNull(DEFAULT_CATALOG, node);
if (defaultCatalog != null) {
builder.defaultCatalog(defaultCatalog);
}

Integer schemaId = JsonUtil.getIntOrNull(SCHEMA_ID, node);
if (schemaId != null) {
builder.schemaId(schemaId);
}

List<String> namespace = JsonUtil.getStringListOrNull(DEFAULT_NAMESPACE, node);
if (namespace != null && !namespace.isEmpty()) {
builder.defaultNamespace(Namespace.of(Iterables.toArray(namespace, String.class)));
}

List<String> fieldAliases = JsonUtil.getStringListOrNull(FIELD_ALIASES, node);
if (fieldAliases != null) {
builder.fieldAliases(fieldAliases);
}

List<String> fieldComments = JsonUtil.getStringListOrNull(FIELD_COMMENTS, node);
if (fieldComments != null) {
builder.fieldComments(fieldComments);
}

return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.iceberg.view;

import org.immutables.value.Value;

@Value.Immutable
public interface UnknownViewRepresentation extends ViewRepresentation {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.iceberg.view;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
import java.io.IOException;
import java.util.Locale;
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
import org.apache.iceberg.util.JsonUtil;

class ViewRepresentationParser {
static final String TYPE = "type";

private ViewRepresentationParser() {}

static void toJson(ViewRepresentation representation, JsonGenerator generator)
throws IOException {
Preconditions.checkArgument(representation != null, "Invalid view representation: null");
switch (representation.type()) {
case ViewRepresentation.Type.SQL:
SQLViewRepresentationParser.toJson((SQLViewRepresentation) representation, generator);
break;

default:
throw new IllegalArgumentException(
String.format("Cannot serialize view representation type: %s", representation.type()));
}
}

static String toJson(ViewRepresentation entry) {
return JsonUtil.generate(gen -> toJson(entry, gen), false);
}

static ViewRepresentation fromJson(String json) {
return JsonUtil.parse(json, ViewRepresentationParser::fromJson);
}

static ViewRepresentation fromJson(JsonNode node) {
Preconditions.checkArgument(node != null, "Cannot parse view representation from null object");
Preconditions.checkArgument(
node.isObject(), "Cannot parse view representation from non-object: %s", node);
String type = JsonUtil.getString(TYPE, node).toLowerCase(Locale.ENGLISH);
switch (type) {
case ViewRepresentation.Type.SQL:
return SQLViewRepresentationParser.fromJson(node);

default:
return ImmutableUnknownViewRepresentation.builder().type(type).build();
}
}
}
Loading