/*
* Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. Crate 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.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial agreement.
*/
package io.crate.operation.fetch;
import io.crate.analyze.symbol.FetchReference;
import io.crate.analyze.symbol.Field;
import io.crate.analyze.symbol.InputColumn;
import io.crate.data.Input;
import io.crate.data.Row;
import io.crate.metadata.Functions;
import io.crate.metadata.Reference;
import io.crate.metadata.RowGranularity;
import io.crate.metadata.TableIdent;
import io.crate.operation.BaseImplementationSymbolVisitor;
import io.crate.operation.projectors.fetch.ArrayBackedRow;
import io.crate.planner.node.fetch.FetchSource;
import java.util.Map;
public class FetchRowInputSymbolVisitor extends BaseImplementationSymbolVisitor<FetchRowInputSymbolVisitor.Context> {
public static class Context {
private final ArrayBackedRow[] fetchRows;
private final Map<TableIdent, FetchSource> fetchSources;
private final ArrayBackedRow inputRow = new ArrayBackedRow();
private final int[] fetchIdPositions;
private final Object[][] nullCells;
private ArrayBackedRow[] partitionRows;
public Context(Map<TableIdent, FetchSource> fetchSources) {
assert !fetchSources.isEmpty() : "fetchSources must not be empty";
this.fetchSources = fetchSources;
int numFetchIds = 0;
for (FetchSource fetchSource : fetchSources.values()) {
numFetchIds += fetchSource.fetchIdCols().size();
}
this.fetchRows = new ArrayBackedRow[numFetchIds];
this.fetchIdPositions = new int[numFetchIds];
this.partitionRows = new ArrayBackedRow[numFetchIds];
nullCells = new Object[numFetchIds][];
int idx = 0;
for (FetchSource fetchSource : fetchSources.values()) {
for (InputColumn col : fetchSource.fetchIdCols()) {
fetchRows[idx] = new ArrayBackedRow();
fetchIdPositions[idx] = col.index();
if (!fetchSource.partitionedByColumns().isEmpty()) {
partitionRows[idx] = new ArrayBackedRow();
}
nullCells[idx] = new Object[fetchSource.references().size()];
idx++;
}
}
}
/**
* @return an array with the positions of the fetchIds in the input
*/
public int[] fetchIdPositions() {
return fetchIdPositions;
}
public ArrayBackedRow[] fetchRows() {
return fetchRows;
}
public ArrayBackedRow[] partitionRows() {
return partitionRows;
}
public ArrayBackedRow inputRow() {
return inputRow;
}
public Object[][] nullCells() {
return nullCells;
}
public Input<?> allocateInput(int index) {
return new RowInput(inputRow, index);
}
public Input<?> allocatePartitionedInput(FetchReference fetchReference) {
int idx = -1;
int fetchIdx = 0;
FetchSource fs = null;
for (FetchSource fetchSource : fetchSources.values()) {
idx = fetchSource.partitionedByColumns().indexOf(fetchReference.ref());
if (idx >= 0) {
for (InputColumn col : fetchSource.fetchIdCols()) {
if (col.equals(fetchReference.fetchId())) {
fs = fetchSource;
break;
}
}
if (fs != null) {
break;
}
}
fetchIdx++;
}
assert fs != null : "fs must not be null";
if (partitionRows == null) {
partitionRows = new ArrayBackedRow[fetchSources.size()];
}
ArrayBackedRow row = partitionRows[fetchIdx];
if (row == null) {
row = new ArrayBackedRow();
partitionRows[fetchIdx] = row;
}
return new RowInput(row, idx);
}
public Input<?> allocateInput(FetchReference fetchReference) {
FetchSource fs = null;
int fetchIdx = 0;
for (Map.Entry<TableIdent, FetchSource> entry : fetchSources.entrySet()) {
if (entry.getKey().equals(fetchReference.ref().ident().tableIdent())) {
fs = entry.getValue();
for (InputColumn col : fs.fetchIdCols()) {
if (col.equals(fetchReference.fetchId())) {
break;
}
fetchIdx++;
}
break;
} else {
fetchIdx += entry.getValue().fetchIdCols().size();
}
}
assert fs != null : "fs must not be null";
Row row = fetchRows[fetchIdx];
int idx = 0;
RowInput input = null;
for (Reference reference : fs.references()) {
if (reference.equals(fetchReference.ref())) {
input = new RowInput(row, idx);
break;
}
idx++;
}
assert input != null : "input must not be null";
return input;
}
}
static class RowInput implements Input<Object> {
private final Row row;
private final int index;
public RowInput(Row row, int index) {
this.row = row;
this.index = index;
}
@Override
public Object value() {
return row.get(index);
}
}
public FetchRowInputSymbolVisitor(Functions functions) {
super(functions);
}
@Override
public Input<?> visitInputColumn(InputColumn inputColumn, Context context) {
return context.allocateInput(inputColumn.index());
}
@Override
public Input<?> visitField(Field field, Context context) {
return context.allocateInput(field.index());
}
@Override
public Input<?> visitFetchReference(FetchReference fetchReference, Context context) {
if (fetchReference.ref().granularity() == RowGranularity.DOC) {
return context.allocateInput(fetchReference);
}
assert fetchReference.ref().granularity() == RowGranularity.PARTITION :
"fetchReference.ref().granularity() must be " + RowGranularity.PARTITION;
return context.allocatePartitionedInput(fetchReference);
}
}