/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
*/
package org.xenei.jdbc4sparql.sparql;
import java.lang.reflect.Field;
import java.sql.SQLDataException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xenei.jdbc4sparql.iface.Catalog;
import org.xenei.jdbc4sparql.iface.Column;
import org.xenei.jdbc4sparql.iface.Key;
import org.xenei.jdbc4sparql.iface.KeySegment;
import org.xenei.jdbc4sparql.iface.Schema;
import org.xenei.jdbc4sparql.iface.Table;
import org.xenei.jdbc4sparql.iface.name.ColumnName;
import org.xenei.jdbc4sparql.iface.name.ItemName;
import org.xenei.jdbc4sparql.iface.name.NameSegments;
import org.xenei.jdbc4sparql.iface.name.SearchName;
import org.xenei.jdbc4sparql.iface.name.TableName;
import org.xenei.jdbc4sparql.impl.NameUtils;
import org.xenei.jdbc4sparql.impl.virtual.VirtualCatalog;
import org.xenei.jdbc4sparql.impl.virtual.VirtualSchema;
import org.xenei.jdbc4sparql.impl.virtual.VirtualTable;
import org.xenei.jdbc4sparql.sparql.items.QueryColumnInfo;
import org.xenei.jdbc4sparql.sparql.items.QueryItemCollection;
import org.xenei.jdbc4sparql.sparql.items.QueryTableInfo;
import org.xenei.jdbc4sparql.sparql.parser.SparqlParser;
import org.xenei.jdbc4sparql.sparql.parser.jsqlparser.functions.FunctionColumn;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.sparql.core.Var;
import com.hp.hpl.jena.sparql.core.VarExprList;
import com.hp.hpl.jena.sparql.expr.E_Equals;
import com.hp.hpl.jena.sparql.expr.E_Function;
import com.hp.hpl.jena.sparql.expr.E_LogicalAnd;
import com.hp.hpl.jena.sparql.expr.Expr;
import com.hp.hpl.jena.sparql.expr.ExprAggregator;
import com.hp.hpl.jena.sparql.expr.ExprVar;
import com.hp.hpl.jena.sparql.expr.NodeValue;
import com.hp.hpl.jena.sparql.expr.aggregate.Aggregator;
import com.hp.hpl.jena.sparql.syntax.Element;
import com.hp.hpl.jena.sparql.syntax.ElementBind;
import com.hp.hpl.jena.sparql.syntax.ElementFilter;
import com.hp.hpl.jena.sparql.syntax.ElementGroup;
import com.hp.hpl.jena.sparql.syntax.ElementService;
import com.hp.hpl.jena.sparql.syntax.ElementSubQuery;
import com.hp.hpl.jena.sparql.syntax.ElementUnion;
import com.hp.hpl.jena.util.iterator.Filter;
/**
* Creates a SparqlQuery while tracking naming changes between nomenclatures.
*/
public class SparqlQueryBuilder {
private static ElementGroup getElementGroup(final Query query) {
ElementGroup retval;
final Element e = query.getQueryPattern();
if (e == null) {
retval = new ElementGroup();
query.setQueryPattern(retval);
}
else if (e instanceof ElementGroup) {
retval = (ElementGroup) e;
}
else {
retval = new ElementGroup();
retval.addElement(e);
}
return retval;
}
// a set of error messages.
static final String NOT_FOUND_IN_QUERY = "%s not found in SPARQL query";
public static final String FOUND_IN_MULTIPLE_ = "%s was found in multiple %s";
static final String NOT_FOUND_IN_ANY_ = "%s was not found in any %s";
public static final String NOT_FOUND_IN_ = "%s was not found in %s";
public static final boolean OPTIONAL = true;
public static final boolean REQUIRED = false;
private final SparqlParser parser;
private final Map<String, Catalog> catalogs;
private final QueryInfoSet infoSet;
// the query we are building.
private Query query;
// query was built flag;
private boolean isBuilt;
// sparql catalog we are running against.
private final Catalog catalog;
// sparql schema for default tables
private final Schema schema;
// the count of aliases used in this query.
private int aliasCount;
// the list of columns not to be included in the "all columns" result.
// perhaps this should store column so that tables may be checked in case
// tables are added to the query later. But I don't think so.
private final List<String> columnsInUsing;
// columns indexed by var.
// private final List<Column> columnsInResult;
private static Logger LOG = LoggerFactory
.getLogger(SparqlQueryBuilder.class);
/**
* Create a query builder for a catalog and schema
*
* @param catalog
* The catalog to build the query for.
* @param schema
* The schema to build the query for.
* @throws IllegalArgumentException
* if catalog is null.
*/
public SparqlQueryBuilder(final Map<String, Catalog> catalogs,
final SparqlParser parser, final Catalog catalog,
final Schema schema) {
if (catalogs == null) {
throw new IllegalArgumentException("Catalogs may not be null");
}
if (catalog == null) {
throw new IllegalArgumentException("Catalog may not be null");
}
if (!catalogs.containsKey(catalog.getShortName())) {
catalogs.put(catalog.getShortName(), catalog);
}
if (parser == null) {
throw new IllegalArgumentException("SparqlParser may not be null");
}
this.aliasCount = 0;
this.catalogs = catalogs;
this.parser = parser;
this.catalog = catalog;
this.schema = schema;
this.query = new Query();
this.isBuilt = false;
this.infoSet = new QueryInfoSet();
this.columnsInUsing = new ArrayList<String>();
this.infoSet.setUseGUID(catalog.isService());
query.setQuerySelectType();
}
/**
* Create a sub query builder
*
* @param parent
* The QueryBuilder that this is a sub query for.
*/
public SparqlQueryBuilder(final SparqlQueryBuilder parent) {
this(parent.catalogs, parent.parser, parent.catalog, parent.schema);
this.infoSet.setUseGUID(parent.infoSet.useGUID());
}
/**
* Set the use GUID flag;
*
* @param state
* @return the last state.
*/
public boolean setUseGUID(final boolean state) {
return this.infoSet.setUseGUID(state);
}
public QueryColumnInfo addAlias(final ColumnName orig,
final ColumnName alias) throws SQLDataException {
final QueryTableInfo tableInfo = infoSet.getTable(orig.getTableName());
if (tableInfo == null) {
throw new IllegalArgumentException(String.format(
SparqlQueryBuilder.NOT_FOUND_IN_QUERY, orig.getTableName()));
}
// find the column will check infoset for existing column first.
QueryColumnInfo columnInfo = tableInfo.getColumn(orig);
if (columnInfo == null) {
// ok now we have to look for column with the proper name.
final Column column = tableInfo.getTable().getColumn(
orig.getShortName());
if (column == null) {
throw new IllegalArgumentException(String.format(
SparqlQueryBuilder.NOT_FOUND_IN_QUERY, orig));
}
columnInfo = tableInfo.addColumnToQuery(column);
}
// tableInfo.addDataFilter(columnInfo);
// infoSet.addColumn(columnInfo);
final QueryColumnInfo aliasInfo = columnInfo.createAlias(alias);
tableInfo.addDataFilter(aliasInfo);
infoSet.addColumn(aliasInfo);
return aliasInfo;
}
public int getAliasCount() {
return aliasCount++;
}
/**
* Add the column to the query. Column must be in a table that has already
* been added to the query.
*
* @param cName
* The column name.
* @param optional
* True if the column is optional.
* @return The QueryColumnInfo for the column
* @throws SQLException
*/
public QueryColumnInfo addColumn(final ColumnName cName,
final boolean optional) throws SQLException {
if (SparqlQueryBuilder.LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Adding Column {}", cName);
}
checkBuilt();
final QueryColumnInfo columnInfo = infoSet.scanTablesForColumn(cName);
if (columnInfo == null) {
final TableName tName = cName.getTableName();
if (tName.isWild()) {
throw new SQLException(String.format(
SparqlQueryBuilder.NOT_FOUND_IN_ANY_, cName, "table"));
}
else {
throw new SQLException(String.format(
SparqlQueryBuilder.NOT_FOUND_IN_, cName, tName));
}
}
return columnInfo;
}
/**
* Add a filter to the query.
*
* @param filter
* The filter to add.
*/
public void addFilter(final Expr filter) {
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("adding Filter: {}", filter);
}
checkBuilt();
final ElementFilter el = new ElementFilter(filter);
SparqlQueryBuilder.getElementGroup(query).addElementFilter(el);
}
public QueryColumnInfo addColumnToQuery(final ColumnName cName,
final boolean optional) {
cName.setUsedSegments(getSegments());
final QueryColumnInfo columnInfo = infoSet.scanTablesForColumn(cName);
if (columnInfo == null) {
throw new IllegalArgumentException(String.format(
SparqlQueryBuilder.NOT_FOUND_IN_QUERY, cName));
}
return columnInfo;
}
public void addGroupBy(final Expr expr) {
query.addGroupBy(expr);
}
/**
* Add an order by to the query.
*
* @param expr
* The expression to order by.
* @param ascending
* true of order should be ascending, false = descending.
*/
public void addOrderBy(final Expr expr, final boolean ascending) {
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Adding order by {} {}", expr,
ascending ? "Ascending" : "Descending");
}
checkBuilt();
query.addOrderBy(expr, ascending ? Query.ORDER_ASCENDING
: Query.ORDER_DESCENDING);
}
public void addDefinedColumns() throws SQLDataException {
infoSet.addDefinedColumns(columnsInUsing);
}
public QueryTableInfo addTable(final Table table, final TableName name,
final boolean optional) {
if (SparqlQueryBuilder.LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Adding table {} as {}",
table.getSQLName(), name);
}
// make sure the table is in the query.
QueryTableInfo tableInfo = infoSet.getTable(name);
if (tableInfo == null) {
tableInfo = new QueryTableInfo(infoSet, getElementGroup(), table,
name, optional);
infoSet.addTable(tableInfo);
}
return tableInfo;
}
/**
* Add a table to the query.
*
* @param name
* The table name
* @param asName
* The name of the table as referenced in the query.
* @param optional
* if true table is optional.
* @return The node that represents the table.
* @throws SQLException
* if the table is in multiple schemas or not found.
*/
public QueryTableInfo addTable(final TableName name,
final TableName asName, final boolean optional) throws SQLException {
if (SparqlQueryBuilder.LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug(String.format(
"Adding %s table %s as %s", optional ? "optional"
: "required", name, asName));
}
checkBuilt();
final Collection<Table> tables = findTables(name);
if (tables.size() > 1) {
throw new SQLException(String.format(
SparqlQueryBuilder.FOUND_IN_MULTIPLE_ + " of catalog '%s'",
name, "schemas", catalog.getName()));
}
if (tables.isEmpty()) {
throw new SQLException(String.format(
SparqlQueryBuilder.NOT_FOUND_IN_ANY_ + " of catalog '%s'",
name, "schema", catalog.getName()));
}
return addTable(tables.iterator().next(), asName, optional);
}
public void addTableColumns(final QueryTableInfo tableInfo) {
tableInfo.addTableColumns(query, columnsInUsing);
}
public void addUnion(final List<SparqlQueryBuilder> unionBuilders)
throws SQLDataException {
final ElementUnion unionElement = new ElementUnion();
for (final SparqlQueryBuilder sqb : unionBuilders) {
unionElement.addElement(new ElementSubQuery(sqb.build()));
}
getElementGroup().addElement(unionElement);
}
public void addUsing(final String columnName) {
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("adding Using {}", columnName);
}
final Collection<QueryTableInfo> tables = infoSet.getTables();
if (tables.size() < 2) {
throw new IllegalArgumentException(
"There must be at least 2 tables in the query");
}
final Iterator<QueryTableInfo> iter = tables.iterator();
final QueryTableInfo tableInfo = iter.next();
ColumnName cName = tableInfo.getName().getColumnName(columnName);
final QueryColumnInfo columnInfo = tableInfo.getColumn(cName, false);
if (columnInfo == null) {
throw new IllegalArgumentException(String.format(
"column %s not found in %s", columnName,
tableInfo.getSQLName()));
}
columnsInUsing.add(columnName);
while (iter.hasNext()) {
final QueryTableInfo tableInfo2 = iter.next();
cName = tableInfo2.getName().getColumnName(columnName);
final QueryColumnInfo columnInfo2 = tableInfo2.getColumn(cName,
false);
if (columnInfo2 == null) {
throw new IllegalArgumentException(String.format(
"column %s not found in %s", columnInfo2,
tableInfo2.getSQLName()));
}
}
}
/**
* Adds the the expression as a variable to the query. As a variable the
* result will be returned from the query.
*
* @param expr
* The expression that defines the variable
* @param name
* the alias for the expression, if null no alias is used.
*/
public void addVar(final Expr expr, final String name) {
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Adding Var {} as {}", expr, name);
}
checkBuilt();
final NodeValue nv = expr.getConstant();
if ((name != null) || (nv == null) || !nv.asNode().isVariable()) {
final String s = StringUtils.defaultString(expr.getVarName());
if (StringUtils.isNotEmpty(s)
&& s.equals(StringUtils.defaultIfBlank(name, s))) {
query.addResultVar(s);
}
else {
if (name != null) {
query.addResultVar(name, expr);
}
else {
query.addResultVar(nv.asNode());
}
}
}
else {
query.addResultVar(nv.asNode());
}
query.getResultVars();
}
/**
* Adds the the expression as a variable to the query. As a variable the
* result will be returned from the query.
*
* @param expr
* The expression that defines the variable
* @param name
* the alias for the expression.
*/
public void addVar(final Expr expr, final ColumnName name) {
checkBuilt();
final QueryColumnInfo columnInfo = infoSet.getColumn(name);
if (!query.getProjectVars().contains(columnInfo.getVar())) {
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Adding {} as {}", expr, name);
}
query.addResultVar(columnInfo.getVar(), expr);
query.getResultVars();
}
}
/**
* Adds the the expression as a variable to the query. As a variable the
* result will be returned from the query.
*
* @param columnName
* The column to add.
* @throws SQLException
*/
public void addVar(final ColumnName columnName) throws SQLException {
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Adding Var {}", columnName);
}
checkBuilt();
final QueryColumnInfo columnInfo = addColumn(columnName, false);
getTable(columnName.getTableName()).addDataFilter(columnInfo);
query.addResultVar(columnInfo.getVar());
query.getResultVars();
}
/**
* Get the SPARQL query.
*
* @return The constructed SPARQL query.
* @throws SQLDataException
*/
public Query build() throws SQLDataException {
if (!isBuilt) {
if (!catalog.isService()) {
// apply the type filters to each subpart.
for (final QueryTableInfo tableInfo : infoSet.getTables()) {
try {
tableInfo.addQueryFilters(infoSet);
} catch (final SQLDataException e1) {
throw new IllegalStateException(e1.getMessage(), e1);
}
}
}
// renumber the Bnodes.
final Element e = new BnodeRenumber().renumber(query
.getQueryPattern());
query.setQueryPattern(e);
if (catalog.isService()) {
// create a copy of the query so that we can verify that it is
// good.
final Query serviceCall = query.cloneQuery();
final VarExprList vars = serviceCall.getProject();
// reset the serviceCall select vars
// protected VarExprList projectVars = new VarExprList() ;
try {
Field f = Query.class.getDeclaredField("projectVars");
f.setAccessible(true);
f.set( serviceCall, new VarExprList() );
} catch (NoSuchFieldException e2) {
throw new IllegalStateException( e2.getMessage(),e2);
} catch (SecurityException e2) {
throw new IllegalStateException( e2.getMessage(),e2);
} catch (IllegalAccessException e2) {
throw new IllegalStateException( e2.getMessage(),e2);
}
final ElementService service = new ElementService(
catalog.getServiceNode(), new ElementSubQuery(
serviceCall), false);
final Query newResult = new Query();
newResult.setQuerySelectType();
final ElementGroup filterGroup = SparqlQueryBuilder
.getElementGroup(newResult);
filterGroup.addElement(service);
final ElementGroup typeGroup = new ElementGroup();
typeGroup.addElement(filterGroup);
infoSet.setUseGUID(false); // we are now building complete set.
// create the service call
// make sure we project all vars for the filters.
final Collection<QueryColumnInfo> typeFilters = new HashSet<QueryColumnInfo>();
final Collection<QueryColumnInfo> dataFilters = new HashSet<QueryColumnInfo>();
final Collection<QueryColumnInfo> columnsInQuery = new ArrayList<QueryColumnInfo>();
for (final Var v : vars.getVars()) {
final QueryColumnInfo colInfo = infoSet
.findColumnByGUIDVar(v.getName());
if (colInfo == null)
{
// may be a variable associated with a function
if (vars.getExpr(v) != null)
{
newResult.addResultVar(v, vars.getExpr(v));
}
else
{
throw new IllegalStateException( String.format("can not find column %s", v));
}
}
else
{
columnsInQuery.add( colInfo );
newResult.addResultVar(colInfo.getVar());
}
}
// add the columns to the query.
// the columns are named by GUID in the query.
boolean firstTable = true;
for (final QueryTableInfo tableInfo : infoSet.getTables()) {
// add the data type filters
for (final Column tblCol : tableInfo.getTable().getColumnList())
{
QueryColumnInfo columnInfo = new QueryColumnInfo(tblCol);
typeFilters.add( columnInfo );
serviceCall.addResultVar(columnInfo.getGUIDVar());
}
// add the binds
for (QueryColumnInfo colInfo : columnsInQuery)
{
if(colInfo.getBaseColumnInfo().getName().getTableName().equals( tableInfo.getName() ))
{
if (firstTable || ! this.columnsInUsing.contains(colInfo.getName().getShortName()))
{
dataFilters.add( colInfo );
}
}
}
try {
QueryTableInfo.addTypeFilters(infoSet, typeFilters,
dataFilters, tableInfo.getJoinElements(),
filterGroup, typeGroup);
} catch (final SQLDataException e1) {
throw new IllegalStateException(e1.getMessage(), e1);
}
dataFilters.clear();
typeFilters.clear();
firstTable = false;
}
// add equality check into service Call
for (final String columnName : columnsInUsing) {
QueryColumnInfo first = null;
Expr expr = null;
for (final QueryColumnInfo columnInfo : infoSet
.listColumns(new SearchName(null, null, null,
columnName))) {
if (first == null) {
first = columnInfo;
}
else {
final E_Equals eq = new E_Equals(
new ExprVar(first.getGUIDVar()),
new ExprVar(columnInfo.getGUIDVar()));
if (expr == null) {
expr = eq;
}
else {
expr = new E_LogicalAnd(expr, eq);
}
}
}
if (expr != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Adding filter: {}", expr);
}
((ElementGroup)serviceCall.getQueryPattern()).addElementFilter(new ElementFilter(expr));
}
}
newResult.setQueryPattern(typeGroup);
query = newResult;
}
isBuilt = true;
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Query parsed as {}", query);
}
}
return query;
}
private void checkBuilt() {
if (isBuilt) {
throw new IllegalStateException("Query was already built");
}
}
private Collection<Table> findTables(final ItemName name) {
if (SparqlQueryBuilder.LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug(String.format(
"Looking for Table %s.%s in '%s' catalog",
name.getSchema(), name.getTable(), catalog.getName()));
}
final List<Table> tables = new ArrayList<Table>();
for (final Schema schema : catalog.findSchemas(name.getSchema())) {
for (final Table table : schema.findTables(name.getTable())) {
tables.add(table);
}
}
return tables;
}
/**
* Get the catalog this builder is working with.
*
* @return a Catalog.
*/
public Catalog getCatalog() {
return catalog;
}
public String getCatalogName() {
return catalog.getShortName();
}
public Catalog getCatalog(final String catalog) {
return catalogs.get(catalog);
}
public QueryColumnInfo getColumn(final ColumnName cName) {
return infoSet.getColumn(cName);
}
public QueryColumnInfo getColumn(final int i) {
if (!isBuilt) {
throw new IllegalStateException(
"Columns may not be retrieved from a builder until after query is built");
}
final Var v = query.getProjectVars().get(i);
return getColumn(v);
}
public int getColumnCount() {
if (!isBuilt) {
throw new IllegalStateException(
"Column count may not be retrieved from a builder until after query is built");
}
return query.getProjectVars().size();
}
public int getColumnIndex(final String columnName) {
final ColumnName cName = ColumnName.getNameInstance(
catalog.getShortName(), schema.getName().getShortName(), null,
columnName);
return infoSet.getColumnIndex(cName);
}
private ElementGroup getElementGroup() {
return SparqlQueryBuilder.getElementGroup(query);
}
private ColumnName createColumnName(final Var v) {
final int segs = v.getName().split(NameUtils.SPARQL_DOT).length;
if (segs > 4) {
throw new IllegalArgumentException(
"Name may not have more than 4 segments");
}
final ColumnName cName = ColumnName.getNameInstance("", "", "",
v.getName());
cName.setUsedSegments(NameSegments.getInstance(segs == 4, segs >= 3,
segs >= 2, true));
return cName;
}
public QueryColumnInfo getColumn(final Var v) {
return infoSet.getColumn(createColumnName(v));
}
public QueryTableInfo getTable(final Var v) {
checkBuilt();
final int segs = v.getName().split(NameUtils.SPARQL_DOT).length;
if (segs > 3) {
throw new IllegalArgumentException(
"Name may not have more than 3 segments");
}
final TableName tName = TableName.getNameInstance("", "", v.getName());
tName.setUsedSegments(NameSegments.getInstance(segs == 3, segs >= 2,
true, false));
return infoSet.getTable(tName);
}
public List<QueryColumnInfo> getResultColumns() {
final List<QueryColumnInfo> retval = new ArrayList<QueryColumnInfo>();
for (final Var v : query.getProjectVars()) {
retval.add(getColumn(v));
}
return retval;
}
/**
* Get a table from the query.
*
* @param name
* The table name.
* @return The query table info for the name or null if none found
* @throws IllegalArgumentException
* if more than one object matches.
*/
public QueryTableInfo getTable(final TableName name) {
return infoSet.getTable(name);
}
public ExprAggregator register(final Aggregator agg, final int type,
final String alias) throws SQLException {
final ExprAggregator expr = (ExprAggregator) query.allocAggregate(agg);
final ColumnName columnName = ColumnName.getNameInstance(
VirtualCatalog.NAME, VirtualSchema.NAME, VirtualTable.NAME,
alias);
if (infoSet.findColumn(columnName) == null) {
registerFunctionColumn(columnName, type);
}
return expr;
}
public QueryColumnInfo registerFunction(final ColumnName funcName,
final int type) throws SQLException {
QueryColumnInfo retval = infoSet.findColumn(funcName);
if (retval == null) {
retval = registerFunctionColumn(funcName, type);
}
return retval;
}
public QueryColumnInfo registerFunctionColumn(
final ColumnName columnNameArg, final int type) {
final ColumnName columnName = new ColumnName(StringUtils.defaultString(
columnNameArg.getCatalog(), VirtualCatalog.NAME),
StringUtils.defaultString(columnNameArg.getSchema(),
VirtualSchema.NAME), StringUtils.defaultString(
columnNameArg.getTable(), VirtualTable.NAME),
columnNameArg.getShortName());
QueryTableInfo tableInfo = infoSet.getTable(columnName.getTableName());
Column column = null;
if (tableInfo == null) {
final Catalog cat = getCatalog(VirtualCatalog.NAME);
Schema schema = cat.getSchema(columnName.getSchema());
if (schema == null) {
schema = new VirtualSchema(cat, columnName.getSchema());
}
Table table = schema.getTable(columnName.getTable());
if (table == null) {
table = new VirtualTable(schema, columnName.getTable());
}
column = new FunctionColumn(table, columnName.getShortName(), type);
// new QueryTableInfo adds table to infoSet
tableInfo = new QueryTableInfo(infoSet, null, table,
columnName.getTableName(), false);
infoSet.addTable(tableInfo);
}
else {
column = new FunctionColumn(tableInfo.getTable(),
columnName.getShortName(), type);
}
final QueryColumnInfo columnInfo = new QueryColumnInfo(column,
columnName, false);
infoSet.addColumn(columnInfo);
return columnInfo;
}
/**
* Sets all the columns for all the tables currently defined. This method
* should be called after all tables have been added to the querybuilder.
*
* @throws SQLException
*/
public void setAllColumns() throws SQLException {
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Setting All Columns");
}
checkBuilt();
final Collection<QueryTableInfo> tableInfos = infoSet.getTables();
if (tableInfos.size() == 0) {
throw new IllegalArgumentException(
"There must be a least one table");
}
final QueryItemCollection<QueryColumnInfo, Column, ColumnName> colInfoList = new QueryItemCollection<QueryColumnInfo, Column, ColumnName>();
for (final QueryTableInfo tableInfo : tableInfos) {
final Iterator<Column> iter = tableInfo.getTable().getColumns();
while (iter.hasNext()) {
final Column col = iter.next();
final ColumnName cName = tableInfo.getName().getColumnName(
col.getName().getShortName());
final QueryColumnInfo columnInfo = tableInfo.getColumn(cName,
col.isOptional());
colInfoList.add(columnInfo);
tableInfo.addDataFilter(columnInfo);
}
}
// find shortest name without name collision. skipping columns in using.
NameSegments segs = NameSegments.getInstance(false, false, false, true);
for (final QueryColumnInfo columnInfo : colInfoList) {
if (!columnsInUsing.contains(columnInfo.getName().getShortName())) {
SearchName sn = new SearchName(columnInfo.getName(), segs);
while ((colInfoList.count(sn) > 1)
&& !sn.getUsedSegments().isCatalog()) {
if (segs.isSchema()) {
segs = NameSegments.getInstance(true, true, true, true);
}
else if (segs.isTable()) {
segs = NameSegments
.getInstance(false, true, true, true);
}
else {
segs = NameSegments.getInstance(false, false, true,
true);
}
sn = new SearchName(columnInfo.getName(), segs);
}
}
}
// remove the variables
for (final Var v : query.getProjectVars()) {
for (final QueryColumnInfo columnInfo : colInfoList.match(
createColumnName(v)).toList()) {
colInfoList.remove(columnInfo);
}
}
// anything left needs to be added
for (final QueryColumnInfo columnInfo : colInfoList) {
query.addResultVar(columnInfo.getVar());
}
}
/**
* Sets the distinct flag for the SPARQL query.
*/
public void setDistinct() {
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Setting Distinct");
}
checkBuilt();
query.setDistinct(true);
}
public void setHaving(final Expr expr) {
query.addHavingCondition(expr);
}
public SparqlQueryBuilder setKey(final Key<?> key) {
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Setting key {}", key);
}
if (key != null) {
setOrderBy(key);
if (key.isUnique()) {
setDistinct();
}
}
return this;
}
/**
* Sets the limit for the SPARQL query.
*
* @param limit
* The number of records to return
*/
public void setLimit(final Long limit) {
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Setting limit {}", limit);
}
checkBuilt();
query.setLimit(limit);
}
/**
* Set the offset for the SPARQL query.
*
* @param offset
* The number of rows to skip over.
*/
public void setOffset(final Long offset) {
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Setting Offset {}", offset);
}
checkBuilt();
query.setOffset(offset);
}
public void setOrderBy(final Key<?> key) {
if (LOG.isDebugEnabled()) {
SparqlQueryBuilder.LOG.debug("Setting orderBy {}", key);
}
final List<Var> vars = query.getProjectVars();
for (final KeySegment seg : key.getSegments()) {
query.addOrderBy(vars.get(seg.getIdx()),
seg.isAscending() ? Query.ORDER_ASCENDING
: Query.ORDER_DESCENDING);
}
}
/**
* Return the SPARQL query as the string value for the builder.
*/
@Override
public String toString() {
return String.format("QueryBuilder%s[%s]", (isBuilt ? "(builtargs)"
: ""), query.toString());
}
public Schema getDefaultSchema() {
return schema;
}
public String getDefaultSchemaName() {
return schema.getName().getShortName();
}
/**
* Returns the table name if only one exists in the query. If multiple
* tables exist or no table has been added return null.
*
* @return The default table name.
*/
public String getDefaultTableName() {
final Iterator<QueryTableInfo> iter = infoSet.getTables().iterator();
if (!iter.hasNext()) {
return null;
}
final QueryTableInfo table = iter.next();
if (iter.hasNext()) {
return null;
}
return table.getName().getShortName();
}
public void setSegmentCount() {
infoSet.setMinimumColumnSegments();
for (final String columnName : columnsInUsing) {
final SearchName sn = new SearchName(null, null, null, columnName);
for (final QueryColumnInfo columnInfo : infoSet.listColumns(sn)) {
columnInfo.setSegments(sn.getUsedSegments());
}
}
}
public NameSegments getSegments() {
return infoSet.getSegments();
}
}