package net.sourceforge.mayfly.evaluation.command;
import net.sourceforge.mayfly.MayflyException;
import net.sourceforge.mayfly.MayflyInternalException;
import net.sourceforge.mayfly.MayflyResultSet;
import net.sourceforge.mayfly.datastore.ColumnNames;
import net.sourceforge.mayfly.datastore.DataStore;
import net.sourceforge.mayfly.datastore.TableReference;
import net.sourceforge.mayfly.evaluation.Checker;
import net.sourceforge.mayfly.evaluation.Expression;
import net.sourceforge.mayfly.evaluation.RealChecker;
import net.sourceforge.mayfly.evaluation.ValueList;
import net.sourceforge.mayfly.evaluation.select.Evaluator;
import net.sourceforge.mayfly.evaluation.select.OptimizedSelect;
import net.sourceforge.mayfly.evaluation.select.Select;
import net.sourceforge.mayfly.evaluation.what.Selected;
import net.sourceforge.mayfly.parser.Location;
import net.sourceforge.mayfly.util.ImmutableList;
import java.util.Iterator;
public class SubselectedInsert extends Command {
private final UnresolvedTableReference unresolvedTable;
private final ImmutableList<String> columnNames;
private final Location location;
private final Select subselect;
public SubselectedInsert(
UnresolvedTableReference table, ImmutableList columnNames,
Select subselect) {
this.unresolvedTable = table;
this.columnNames = columnNames;
this.subselect = subselect;
this.location = table.location;
}
@Override
public UpdateStore update(Evaluator evaluator) {
TableReference resolved = unresolvedTable.resolve(evaluator);
Checker checker = new RealChecker(evaluator.store(), resolved,
location, unresolvedTable.options);
return new UpdateStore(
insertRows(evaluator, resolved, checker),
1,
checker.newIdentityValue()
);
}
@Override
public UpdateStore update(DataStore store, String currentSchema) {
throw new MayflyInternalException("should call the other update");
}
private DataStore insertRows(Evaluator evaluator, TableReference table,
Checker checker) {
DataStore store = evaluator.store();
OptimizedSelect optimized = subselect.plan(evaluator);
ColumnNames names = ColumnNames.fromParser(store, table, columnNames);
checkColumnCount(names.asList(), optimized.selected);
MayflyResultSet rows = optimized.asResultSet();
while (rows.next()) {
ValueList values = rows.asValues(subselect.location);
store = store.addRow(table, names.asList(), values, checker);
}
return store;
}
private void checkColumnCount(ImmutableList<String> columnNames,
Selected selected) {
if (columnNames.size() != selected.size()) {
if (selected.size() > columnNames.size()) {
throw makeException("Too many values.\n", columnNames, selected);
} else {
throw makeException("Too few values.\n", columnNames, selected);
}
}
}
private MayflyException makeException(String message,
ImmutableList<String> columnNames, Selected selected) {
return new MayflyException(
message + describeNamesAndValues(columnNames, selected),
subselect.location);
}
private String describeNamesAndValues(
ImmutableList<String> columns, Selected selected) {
StringBuilder result = new StringBuilder();
result.append("Columns and values from subselect were:\n");
Iterator<String> nameIterator = columns.iterator();
Iterator<Expression> valueIterator = selected.iterator();
while (nameIterator.hasNext() || valueIterator.hasNext()) {
if (nameIterator.hasNext()) {
String columnName = nameIterator.next();
result.append(columnName);
} else {
result.append("(none)");
}
result.append(' ');
if (valueIterator.hasNext()) {
Expression value = valueIterator.next();
result.append(value.displayName());
} else {
result.append("(none)");
}
result.append('\n');
}
return result.toString();
}
}