Select.java
package org.microspace.table.query.sql;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import org.microspace.space.SimpleSpace;
import org.microspace.table.Entry;
import org.microspace.table.column.Accessor;
import org.microspace.table.column.ColumnReferences;
import org.microspace.table.column.GetSetPair;
import org.microspace.table.column.IndexedMap;
import org.microspace.table.query.SqlFormat;
import org.microspace.util.PojoUtil;
/**
*
* @author Gaspar Sinai - {@literal gaspar.sinai@microspace.org}
* @version 2017-09-29
* @param <T>
* The type of the Table.
*/
public class Select<T> implements Serializable {
private static final long serialVersionUID = -8265165469283289385L;
final Class<T> tableClass;
final List<String> columnNames;
final List<String> columnNamesLiteral;
final List<String> columnNamesGiga;
final boolean distinct;
String nativeSqlQuery;
String id;
boolean topLevel;
Expression<T> expression = null;
RowNumSortExpression<T> rowNumSortExpression;
List<Select<?>> innerSelects;
static ThreadLocal<InnerSelectContext<?>> innerSelectContext = new ThreadLocal<> ();
/**
* Select all fields, SELECT * FROM ... SELECT DISTINCT
*
* columnName1, columnName2
*
* @param tableClass
* The table class.
* @param distinct True if record merges occur.
*/
public Select(Class<T> tableClass, boolean distinct) {
this.tableClass = tableClass;
this.columnNames = new LinkedList<>();
this.columnNamesGiga = new LinkedList<>();
this.columnNamesLiteral = new LinkedList<>();
this.distinct = distinct;
rowNumSortExpression = new RowNumSortExpression<T>(tableClass);
}
public boolean isTopLevel () {
return this.topLevel;
}
public void setInnerSelectContext (SimpleSpace space) {
if (!topLevel) {
return;
}
InnerSelectContext<T> context = new InnerSelectContext<T>(space, tableClass);
innerSelectContext.set(context);
for (Select<?> inner : innerSelects) {
context.putInnerSelect(inner);
}
}
public void clearInnerSelectContext () {
if (!topLevel) {
return;
}
innerSelectContext.set(null);
}
/**
* Finalize building the Select.
* @param nativeSqlQuery The text of this Query.
* @param innerSelects The list of inner selects in dependency order.
*/
public void finalize (String nativeSqlQuery, List<Select<?>> innerSelects) {
//System.err.println("finalize: " + innerSelects);
this.innerSelects = innerSelects;
this.topLevel = (innerSelects != null);
this.nativeSqlQuery = nativeSqlQuery;
this.id = formatSqlQuery(SqlFormat.MICROSPACE);
}
void addColumn(String columnName, String columnEscape) {
GetSetPair<T> pair = PojoUtil.getColumnGetSetPair(tableClass, columnName);
if (pair == null) {
throw new IllegalArgumentException("Unkown Column: " + columnName);
}
columnNames.add(columnName);
columnNamesLiteral.add(columnEscape + columnName + columnEscape);
columnNamesGiga.add("`" + columnName + "`");
}
List<String> getColumnNames() {
return columnNames;
}
public Class<T> getTableClass() {
return tableClass;
}
public IndexedMap<Object, Entry<T>> apply(Accessor<T> accessor, IndexedMap<Object, Entry<T>> entries,
ColumnReferences<T>[] indexedColumns) {
if (expression != null) {
InnerSelectContext<?> context = innerSelectContext.get();
entries = expression.apply(accessor, entries, indexedColumns, context);
}
return entries;
}
public List<Entry<T>> sortAndLimit (Accessor<T> accessor, IndexedMap<Object, Entry<T>> entries) {
return rowNumSortExpression.sortApply(accessor, entries, columnNames, distinct);
}
public boolean match(T object, Accessor<T> accessor) {
if (expression == null)
return true;
return expression.match(object, accessor);
}
public void setExpression(Expression<T> expression) {
this.expression = expression;
}
public void setRowNumSortExpression(RowNumSortExpression<T> rowNumSortExpression) {
this.rowNumSortExpression = rowNumSortExpression;
}
public Expression<T> getExpression() {
return expression;
}
public RowNumSortExpression<T> getRowNumSortExpression() {
return rowNumSortExpression;
}
public String formatSqlQuery(SqlFormat format) {
if (format == SqlFormat.NATIVE) {
return nativeSqlQuery;
}
if (format == SqlFormat.GIGASPACES || format == SqlFormat.GIGASPACES_LITERAL) {
StringBuilder ret = new StringBuilder();
// inner select.
StringBuilder cols = new StringBuilder();
if (!topLevel) {
ret.append("SELECT");
if (columnNames.size() == 0) {
ret.prefix(" ", "*");
cols.append("*");
} else {
if (format == SqlFormat.GIGASPACES_LITERAL) {
cols.join(", ", columnNamesLiteral);
} else {
cols.join(", ", columnNamesGiga);
}
ret.prefix(" ", cols.toString());
}
ret.join(" ", " FROM", tableClass.getName());
}
if (distinct) {
ret.prefix(" ", cols.toString());
}
StringBuilder where = new StringBuilder();
String sqlQuery = expression == null ? "" : expression.formatSqlQuery(format);
where.join(" AND ", sqlQuery, rowNumSortExpression.formatRowNumString(format));
if (topLevel) {
ret.prefix(" ", where.toString());
} else {
ret.prefix(" WHERE ", where.toString());
}
if (distinct) {
ret.prefix(" ", "GROUP BY");
if (columnNames.size() == 0) {
// Should not happen
ret.prefix(" ", "*");
} else {
StringBuilder cn = new StringBuilder();
if (format == SqlFormat.GIGASPACES_LITERAL) {
cn.join(", ", columnNamesLiteral);
} else {
cn.join(", ", columnNamesGiga);
}
ret.prefix(" ", cn.toString());
}
}
ret.prefix(" ", rowNumSortExpression.formatCmpString(format));
return ret.toString();
} else if (format == SqlFormat.MICROSPACE) {
StringBuilder ret = new StringBuilder();
ret.append("SELECT");
if (distinct) {
ret.prefix(" ", "DISTINCT");
}
if (columnNames.size() == 0) {
ret.prefix(" ", "*");
} else {
StringBuilder cn = new StringBuilder();
cn.join(", ", columnNamesLiteral);
ret.prefix(" ", cn.toString());
}
ret.join(" ", " FROM", tableClass.getName());
StringBuilder where = new StringBuilder();
String sqlQuery = expression == null ? "" : expression.formatSqlQuery(format);
where.join(" AND ",
sqlQuery, rowNumSortExpression.formatRowNumString(format));
ret.prefix(" WHERE ", where.toString());
ret.prefix(" ", rowNumSortExpression.formatCmpString(format));
return ret.toString();
} else {
throw new IllegalArgumentException("Unkown SqlFormat " + format);
}
}
public String getId () {
return id;
}
public int hasCode () {
return getId().hashCode();
}
public boolean equals (Object o) {
if (! (o instanceof Select)) {
return false;
}
@SuppressWarnings("unchecked")
Select<T> sel = (Select<T>) o;
return getId().equals(sel.getId());
}
}