| /* |
| * 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.orc; |
| |
| import java.util.Deque; |
| import java.util.List; |
| import java.util.Map; |
| import org.apache.iceberg.Schema; |
| import org.apache.iceberg.relocated.com.google.common.base.Joiner; |
| import org.apache.iceberg.relocated.com.google.common.collect.Iterables; |
| import org.apache.iceberg.relocated.com.google.common.collect.Lists; |
| import org.apache.iceberg.relocated.com.google.common.collect.Maps; |
| import org.apache.iceberg.types.Type; |
| import org.apache.iceberg.types.TypeUtil; |
| import org.apache.iceberg.types.Types; |
| |
| /** |
| * Generates mapping from field IDs to ORC qualified names. |
| * <p> |
| * This visitor also enclose column names in backticks i.e. ` so that ORC can correctly parse column names with |
| * special characters. A comparison of ORC convention with Iceberg convention is provided below |
| * <pre><code> |
| * Iceberg ORC |
| * field field field |
| * struct -> field struct.field struct.field |
| * list -> element list.element list._elem |
| * list -> struct element -> field list.field list._elem.field |
| * map -> key map.key map._key |
| * map -> value map.value map._value |
| * map -> struct key -> field map.key.field map._key.field |
| * map -> struct value -> field map.field map._value.field |
| * </code></pre> |
| */ |
| class IdToOrcName extends TypeUtil.SchemaVisitor<Map<Integer, String>> { |
| private static final Joiner DOT = Joiner.on("."); |
| |
| private final Deque<String> fieldNames = Lists.newLinkedList(); |
| private final Map<Integer, String> idToName = Maps.newHashMap(); |
| |
| @Override |
| public void beforeField(Types.NestedField field) { |
| fieldNames.push(field.name()); |
| } |
| |
| @Override |
| public void afterField(Types.NestedField field) { |
| fieldNames.pop(); |
| } |
| |
| @Override |
| public void beforeListElement(Types.NestedField elementField) { |
| fieldNames.push("_elem"); |
| } |
| |
| @Override |
| public void afterListElement(Types.NestedField elementField) { |
| fieldNames.pop(); |
| } |
| |
| @Override |
| public void beforeMapKey(Types.NestedField keyField) { |
| fieldNames.push("_key"); |
| } |
| |
| @Override |
| public void afterMapKey(Types.NestedField keyField) { |
| fieldNames.pop(); |
| } |
| |
| @Override |
| public void beforeMapValue(Types.NestedField valueField) { |
| fieldNames.push("_value"); |
| } |
| |
| @Override |
| public void afterMapValue(Types.NestedField valueField) { |
| fieldNames.pop(); |
| } |
| |
| @Override |
| public Map<Integer, String> schema(Schema schema, Map<Integer, String> structResult) { |
| return structResult; |
| } |
| |
| @Override |
| public Map<Integer, String> struct(Types.StructType struct, List<Map<Integer, String>> fieldResults) { |
| return idToName; |
| } |
| |
| @Override |
| public Map<Integer, String> field(Types.NestedField field, Map<Integer, String> fieldResult) { |
| addField(field.name(), field.fieldId()); |
| return idToName; |
| } |
| |
| @Override |
| public Map<Integer, String> list(Types.ListType list, Map<Integer, String> elementResult) { |
| addField("_elem", list.elementId()); |
| return idToName; |
| } |
| |
| @Override |
| public Map<Integer, String> map(Types.MapType map, Map<Integer, String> keyResult, Map<Integer, String> valueResult) { |
| addField("_key", map.keyId()); |
| addField("_value", map.valueId()); |
| return idToName; |
| } |
| |
| @Override |
| public Map<Integer, String> primitive(Type.PrimitiveType primitive) { |
| return idToName; |
| } |
| |
| private void addField(String name, int fieldId) { |
| List<String> fullName = Lists.newArrayList(fieldNames.descendingIterator()); |
| fullName.add(name); |
| idToName.put(fieldId, DOT.join(Iterables.transform(fullName, this::quoteName))); |
| } |
| |
| private String quoteName(String name) { |
| String escapedName = name.replace("`", "``"); // if the column name contains ` then escape it with another ` |
| return "`" + escapedName + "`"; |
| } |
| } |