package jeql.syntax;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import jeql.api.error.ExecutionException;
import jeql.api.table.Table;
import jeql.engine.Scope;
import jeql.engine.query.BaseQueryScope;
/**
* A reference to a table.
* An alias can be supplied.
* If the ref is in a FROM, the alias is a table alias.
* If the ref is in a SELECT list, the alias is a column alias.
*
* If the reference is a "*" in a SELECT list, there may be no table name
* (it will be null).
*
* @author Martin Davis
*
*/
public class TableRefNode
extends ParseTreeNode
{
public static boolean isStar(ParseTreeNode node)
{
if (node instanceof TableRefNode) {
return ((TableRefNode) node).isStar();
}
return false;
}
private String tableName = null;
private String alias = null;
private List exceptColList = null;
public TableRefNode() {
}
public TableRefNode(String table) {
this.tableName = table;
}
public TableRefNode(String table, String alias) {
this.tableName = table;
this.alias = alias;
}
public void setExceptCols(List exceptColList)
{
this.exceptColList = exceptColList;
}
public String getTableName() { return tableName; }
public String getAlias() { return alias; }
public boolean isStar() {
return tableName == null;
}
/**
* Used only as a select tbl ref.
*
* @param scope
* @return
*/
public List prepareStarSelectItems(Scope scope) {
Table tbl = resolveTable(scope);
if (tbl == null) {
throw new ExecutionException(this, "table is null in '" + tableName + ".*'");
}
// TODO: check that except columns are legal column names
List items = new ArrayList();
for (int i = 0; i < tbl.size(); i++) {
String colName = tbl.getColumnName(i);
// skip EXCEPT columns
if (isExceptCol(colName)) continue;
TableColumnNode col = new TableColumnNode(tableName, tbl.getColumnName(i));
col.setLoc(this);
SelectItemNode item = new SelectItemNode(col);
// uniquifying disabled for now...
//SelectItemNode item = new SelectItemNode(col, generateUniqueName(col.getColName(), currentColNames));
item.bind(scope);
items.add(item);
}
// TODO: check that table column list has at least one item
return items;
}
private static final int MAX_UNIQUE_SUFFIX_NUM = 9999;
private static String generateUniqueName(String baseName, Set excludedNames)
{
if (! excludedNames.contains(baseName))
return baseName;
int i = 2;
while (i < MAX_UNIQUE_SUFFIX_NUM) {
String candidate = baseName + "_" + i;
if (! excludedNames.contains(candidate))
return candidate;
}
// oh well - risk failure
return baseName + MAX_UNIQUE_SUFFIX_NUM;
}
private boolean isExceptCol(String col)
{
if (exceptColList == null) return false;
for (int i = 0; i < exceptColList.size(); i++) {
if (col.equals(exceptColList.get(i)))
return true;
}
return false;
}
private Table resolveTable(Scope scope)
{
if (! isStar()) {
return scope.resolveTable(tableName);
}
// require single table in scope
if (! ((BaseQueryScope) scope).hasDefaultTable())
throw new ExecutionException(this, "Unqualified '*' ambiguous or undefined");
return ((BaseQueryScope) scope).getDefaultTable();
}
public void bind(Scope scope)
{
}
public Object eval(Scope scope)
{
// TableRefs are never evaled
throw new UnsupportedOperationException();
}
public Class getType(Scope scope)
{
// TableRefs do not have a type
throw new UnsupportedOperationException();
}
}