blob: 075ecc986ca56c2bf8b7d5fdb42204ef2b14cc90 [file] [log] [blame]
/*
* 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.beam.sdk.io.gcp.spanner;
import com.google.auto.value.AutoValue;
import com.google.cloud.spanner.Type;
import java.io.Serializable;
import java.util.List;
import org.apache.beam.vendor.guava.v20_0.com.google.common.annotations.VisibleForTesting;
import org.apache.beam.vendor.guava.v20_0.com.google.common.collect.ImmutableList;
import org.apache.beam.vendor.guava.v20_0.com.google.common.collect.ImmutableListMultimap;
import org.apache.beam.vendor.guava.v20_0.com.google.common.collect.ImmutableMap;
import org.apache.beam.vendor.guava.v20_0.com.google.common.collect.ImmutableTable;
import org.apache.beam.vendor.guava.v20_0.com.google.common.collect.Maps;
/** Encapsulates Cloud Spanner Schema. */
@AutoValue
abstract class SpannerSchema implements Serializable {
abstract ImmutableList<String> tables();
abstract ImmutableListMultimap<String, Column> columns();
abstract ImmutableListMultimap<String, KeyPart> keyParts();
abstract ImmutableTable<String, String, Long> cellsMutatedPerColumn();
abstract ImmutableMap<String, Long> cellsMutatedPerRow();
public static Builder builder() {
return new AutoValue_SpannerSchema.Builder();
}
/** Builder for {@link SpannerSchema}. */
@AutoValue.Builder
abstract static class Builder {
abstract Builder setTables(ImmutableList<String> tablesBuilder);
abstract ImmutableListMultimap.Builder<String, Column> columnsBuilder();
abstract ImmutableListMultimap.Builder<String, KeyPart> keyPartsBuilder();
abstract ImmutableTable.Builder<String, String, Long> cellsMutatedPerColumnBuilder();
abstract ImmutableMap.Builder<String, Long> cellsMutatedPerRowBuilder();
abstract ImmutableListMultimap<String, Column> columns();
abstract ImmutableTable<String, String, Long> cellsMutatedPerColumn();
@VisibleForTesting
public Builder addColumn(String table, String name, String type) {
return addColumn(table, name, type, 1L);
}
public Builder addColumn(String table, String name, String type, long cellsMutated) {
String tableLower = table.toLowerCase();
String nameLower = name.toLowerCase();
columnsBuilder().put(tableLower, Column.create(nameLower, type));
cellsMutatedPerColumnBuilder().put(tableLower, nameLower, cellsMutated);
return this;
}
public Builder addKeyPart(String table, String column, boolean desc) {
keyPartsBuilder().put(table.toLowerCase(), KeyPart.create(column.toLowerCase(), desc));
return this;
}
abstract SpannerSchema autoBuild();
public final SpannerSchema build() {
// precompute the number of cells that are mutated for operations affecting
// an entire row such as a single key delete.
cellsMutatedPerRowBuilder()
.putAll(
Maps.transformValues(
cellsMutatedPerColumn().rowMap(),
entry -> entry.values().stream().mapToLong(Long::longValue).sum()));
setTables(ImmutableList.copyOf(columns().keySet()));
return autoBuild();
}
}
public List<String> getTables() {
return tables();
}
public List<Column> getColumns(String table) {
return columns().get(table.toLowerCase());
}
public List<KeyPart> getKeyParts(String table) {
return keyParts().get(table.toLowerCase());
}
/** Return the total number of cells affected when the specified column is mutated. */
public long getCellsMutatedPerColumn(String table, String column) {
return cellsMutatedPerColumn().row(table.toLowerCase()).getOrDefault(column.toLowerCase(), 1L);
}
/** Return the total number of cells affected with the given row is deleted. */
public long getCellsMutatedPerRow(String table) {
return cellsMutatedPerRow().getOrDefault(table.toLowerCase(), 1L);
}
@AutoValue
abstract static class KeyPart implements Serializable {
static KeyPart create(String field, boolean desc) {
return new AutoValue_SpannerSchema_KeyPart(field, desc);
}
abstract String getField();
abstract boolean isDesc();
}
@AutoValue
abstract static class Column implements Serializable {
static Column create(String name, Type type) {
return new AutoValue_SpannerSchema_Column(name, type);
}
static Column create(String name, String spannerType) {
return create(name, parseSpannerType(spannerType));
}
public abstract String getName();
public abstract Type getType();
private static Type parseSpannerType(String spannerType) {
spannerType = spannerType.toUpperCase();
if ("BOOL".equals(spannerType)) {
return Type.bool();
}
if ("INT64".equals(spannerType)) {
return Type.int64();
}
if ("FLOAT64".equals(spannerType)) {
return Type.float64();
}
if (spannerType.startsWith("STRING")) {
return Type.string();
}
if (spannerType.startsWith("BYTES")) {
return Type.bytes();
}
if ("TIMESTAMP".equals(spannerType)) {
return Type.timestamp();
}
if ("DATE".equals(spannerType)) {
return Type.date();
}
if (spannerType.startsWith("ARRAY")) {
// Substring "ARRAY<xxx>"
String spannerArrayType = spannerType.substring(6, spannerType.length() - 1);
Type itemType = parseSpannerType(spannerArrayType);
return Type.array(itemType);
}
throw new IllegalArgumentException("Unknown spanner type " + spannerType);
}
}
}