/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.qp.row;
import com.foundationdb.qp.expression.ExpressionRow;
import com.foundationdb.qp.operator.API;
import com.foundationdb.qp.operator.ExpressionGenerator;
import com.foundationdb.qp.operator.QueryBindings;
import com.foundationdb.qp.operator.QueryContext;
import com.foundationdb.qp.rowtype.RowType;
import com.foundationdb.server.explain.*;
import com.foundationdb.server.types.TPreptimeValue;
import com.foundationdb.server.types.value.ValueSource;
import com.foundationdb.server.types.texpressions.TPreparedExpression;
import com.foundationdb.util.AkibanAppender;
import com.foundationdb.util.ArgumentValidation;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public abstract class BindableRow {
// BindableRow class interface
public static BindableRow of(RowType rowType, List<? extends ExpressionGenerator> expressions) {
return of(rowType, API.generateNew(expressions), null);
}
public static BindableRow of(RowType rowType,
List<? extends TPreparedExpression> pExpressions,
QueryContext queryContext) {
Iterator<? extends ValueSource> newVals;
ArgumentValidation.isEQ("rowType fields", rowType.nFields(), "expressions.size", pExpressions.size());
for (TPreparedExpression expression : pExpressions) {
TPreptimeValue tpv = expression.evaluateConstant(queryContext);
if (tpv == null || tpv.value() == null)
return new BindingExpressions(rowType, pExpressions);
}
newVals = new PExpressionEvaluator(pExpressions, queryContext);
ImmutableRow holderRow = new ImmutableRow(rowType, newVals);
return new Delegating(holderRow);
}
public static BindableRow of(Row row) {
return new Delegating(strictCopy(row));
}
// BindableRow instance interface
public abstract Row bind(QueryContext context, QueryBindings bindings);
public abstract CompoundExplainer getExplainer(ExplainContext context);
private static ImmutableRow strictCopy(Row input) {
RowPCopier newCopier;
newCopier = new RowPCopier(input);
return new ImmutableRow(input.rowType(), newCopier);
}
// nested classes
private static class BindingExpressions extends BindableRow {
@Override
public Row bind(QueryContext context, QueryBindings bindings) {
return new ExpressionRow(rowType, context, bindings, pExprs);
}
@Override
public CompoundExplainer getExplainer(ExplainContext context) {
Attributes atts = new Attributes();
for (TPreparedExpression pexpr : pExprs) {
atts.put(Label.EXPRESSIONS, pexpr.getExplainer(context));
}
return new CompoundExplainer(Type.ROW, atts);
}
private BindingExpressions(RowType rowType, List<? extends TPreparedExpression> pExprs)
{
this.rowType = rowType;
this.pExprs = pExprs;
/*
if (expressions != null) {
// TODO do we need an equivalent for pexprs?
for (Expression expression : expressions) {
if (expression.needsRow()) {
throw new IllegalArgumentException("expression " + expression + " needs a row");
}
}
}
*/
}
// object interface
@Override
public String toString() {
return "Bindable " + pExprs;
}
private final List<? extends TPreparedExpression> pExprs;
private final RowType rowType;
}
private static class RowPCopier implements Iterator<ValueSource> {
@Override
public boolean hasNext() {
return i < sourceRow.rowType().nFields();
}
@Override
public ValueSource next() {
return sourceRow.value(i++);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
public RowPCopier(Row sourceRow) {
this.sourceRow = sourceRow;
}
private final Row sourceRow;
private int i = 0;
}
private static class PExpressionEvaluator implements Iterator<ValueSource> {
@Override
public boolean hasNext() {
return expressions.hasNext();
}
@Override
public ValueSource next() {
TPreparedExpression expression = expressions.next();
TPreptimeValue ptv = expression.evaluateConstant(context);
assert ptv != null && ptv.value() != null
: "not constant: " + expression + " with prepare-time value of " + ptv;
return ptv.value();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
private PExpressionEvaluator(Collection<? extends TPreparedExpression> expressions, QueryContext context)
{
this.expressions = expressions.iterator();
this.context = context;
}
private final Iterator<? extends TPreparedExpression> expressions;
private final QueryContext context;
}
private static class Delegating extends BindableRow {
@Override
public Row bind(QueryContext context, QueryBindings bindings) {
return row;
}
@Override
public CompoundExplainer getExplainer(ExplainContext context) {
Attributes atts = new Attributes();
for (int i = 0; i < row.rowType().nFields(); i++) {
atts.put(Label.EXPRESSIONS, PrimitiveExplainer.getInstance(formatAsLiteral(i)));
}
return new CompoundExplainer(Type.ROW, atts);
}
private String formatAsLiteral(int i) {
StringBuilder str = new StringBuilder();
row.rowType().typeAt(i).formatAsLiteral(row.value(i), AkibanAppender.of(str));
return str.toString();
}
@Override
public String toString() {
return String.valueOf(row);
}
private Delegating(ImmutableRow row) {
this.row = row;
}
private final ImmutableRow row;
}
}