
dk.eobjects.metamodel.QueryPostprocessDataContextStrategy Maven / Gradle / Ivy
The newest version!
/**
* This file is part of MetaModel.
*
* MetaModel is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MetaModel 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MetaModel. If not, see .
*/
package dk.eobjects.metamodel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import dk.eobjects.metamodel.data.DataSet;
import dk.eobjects.metamodel.data.MaxRowsDataSetStrategyWrapper;
import dk.eobjects.metamodel.data.Row;
import dk.eobjects.metamodel.detect.ColumnTypeTransformer;
import dk.eobjects.metamodel.detect.ColumnTypeTransformerDataSetStrategy;
import dk.eobjects.metamodel.query.FilterItem;
import dk.eobjects.metamodel.query.FromItem;
import dk.eobjects.metamodel.query.GroupByItem;
import dk.eobjects.metamodel.query.JoinType;
import dk.eobjects.metamodel.query.OperatorType;
import dk.eobjects.metamodel.query.OrderByItem;
import dk.eobjects.metamodel.query.Query;
import dk.eobjects.metamodel.query.SelectItem;
import dk.eobjects.metamodel.schema.Column;
import dk.eobjects.metamodel.schema.ColumnType;
import dk.eobjects.metamodel.schema.Relationship;
import dk.eobjects.metamodel.schema.Schema;
import dk.eobjects.metamodel.schema.Table;
import dk.eobjects.metamodel.schema.TableType;
/**
* Abstract DataContextStrategy for data sources that do not support SQL
* queries. Instead this superclass only requires that a subclass can
* materialize a single table at a time. Then the query will be executed by post
* processing data client-side.
*/
public abstract class QueryPostprocessDataContextStrategy implements
IDataContextStrategy {
public static final String INFORMATION_SCHEMA_NAME = "information_schema";
protected final Log _log = LogFactory.getLog(getClass());
private Map _columnTypeTransformers = new HashMap();
private String[] _schemaNames;
private Schema _mainSchema;
public void setColumnTypeTransformer(String qualifiedColumnName,
ColumnTypeTransformer transformer) {
_columnTypeTransformers.put(qualifiedColumnName, transformer);
}
public void setColumnTypeTransformer(Column column,
ColumnTypeTransformer transformer) {
_columnTypeTransformers.put(column.getQualifiedLabel(), transformer);
}
public DataSet executeQuery(Query query) {
List selectItems = query.getSelectClause().getItems();
List fromItems = query.getFromClause().getItems();
List whereItems = query.getWhereClause().getItems();
List whereSelectItems = query.getWhereClause()
.getEvaluatedSelectItems();
List groupByItems = query.getGroupByClause().getItems();
List havingSelectItems = query.getHavingClause()
.getEvaluatedSelectItems();
List havingItems = query.getHavingClause().getItems();
List orderByItems = query.getOrderByClause().getItems();
// Check for very simple queries with max rows property set (typically
// preview), see Ticket #187
previewTable: if (query.getMaxRows() != null && whereItems.isEmpty()
&& groupByItems.isEmpty() && havingItems.isEmpty()
&& orderByItems.isEmpty() && fromItems.size() == 1) {
Table table = fromItems.get(0).getTable();
if (table != null) {
Column[] columns = new Column[selectItems.size()];
int i = 0;
for (Iterator it = selectItems.iterator(); it
.hasNext();) {
SelectItem item = it.next();
if (item.getFunction() != null
|| item.getExpression() != null) {
break previewTable;
}
columns[i] = item.getColumn();
i++;
}
DataSet dataSet = materializeTable(table, columns, query
.getMaxRows());
dataSet = MetaModelHelper.getSelection(selectItems, dataSet);
return dataSet;
}
}
// Materialize the tables in the from clause
DataSet[] fromDataSets = new DataSet[fromItems.size()];
for (int i = 0; i < fromDataSets.length; i++) {
FromItem fromItem = fromItems.get(i);
fromDataSets[i] = materializeFromItem(fromItem);
}
// Creates a list for all select items that are needed to execute query
// (some may only be used as part of a filter, but not shown in result)
List workSelectItems = collectSelectItems(selectItems,
whereSelectItems, havingSelectItems);
// Execute the query using the raw data
DataSet dataSet = MetaModelHelper.getCarthesianProduct(fromDataSets,
whereItems);
if (groupByItems.size() > 0) {
dataSet = MetaModelHelper.getGrouped(workSelectItems, dataSet,
groupByItems);
} else {
dataSet = MetaModelHelper.getAggregated(workSelectItems, dataSet);
}
dataSet = MetaModelHelper.getFiltered(dataSet, havingItems);
if (query.getSelectClause().isDistinct()) {
dataSet = MetaModelHelper.getSelection(selectItems, dataSet);
dataSet = MetaModelHelper.getDistinct(dataSet);
dataSet = MetaModelHelper.getOrdered(dataSet, orderByItems);
} else {
dataSet = MetaModelHelper.getOrdered(dataSet, orderByItems);
dataSet = MetaModelHelper.getSelection(selectItems, dataSet);
}
if (query.getMaxRows() != null) {
dataSet = new DataSet(new MaxRowsDataSetStrategyWrapper(dataSet,
query.getMaxRows()));
}
return dataSet;
}
public QueryPostprocessDataContextStrategy transformColumnTypes(Table table) {
return transformColumnTypes(table.getColumnsOfType(ColumnType.VARCHAR));
}
public QueryPostprocessDataContextStrategy transformColumnTypes(
Column... columns) {
Table[] tables = MetaModelHelper.getTables(columns);
for (Table table : tables) {
Column[] columnsToQuery = MetaModelHelper.getTableColumns(table,
columns);
columnsToQuery = MetaModelHelper.getColumnsByType(columnsToQuery,
ColumnType.VARCHAR);
DataSet dataSet = executeQuery(new Query().select(columnsToQuery)
.from(table));
for (Column column : columnsToQuery) {
ColumnTypeTransformer transformer = new ColumnTypeTransformer(
column);
_columnTypeTransformers.put(column.getQualifiedLabel(),
transformer);
}
while (dataSet.next()) {
Row row = dataSet.getRow();
for (Column column : columnsToQuery) {
ColumnTypeTransformer transformer = _columnTypeTransformers
.get(column.getQualifiedLabel());
Object value = row.getValue(column);
if (value instanceof String || value == null) {
transformer.registerValue((String) value);
}
}
}
for (Column column : columnsToQuery) {
ColumnTypeTransformer transformer = _columnTypeTransformers
.get(column.getQualifiedLabel());
transformer.narrowColumnType();
if (column.getType() == ColumnType.VARCHAR) {
_columnTypeTransformers.remove(column.getQualifiedLabel());
}
}
}
return this;
}
/**
* Performs column type transformation for the whole schema
*/
public QueryPostprocessDataContextStrategy autoTransformColumnTypes() {
Schema schema = getMainSchemaInternal();
Table[] tables = schema.getTables();
for (Table table : tables) {
transformColumnTypes(table);
}
return this;
}
public DataSet materializeFromItem(FromItem fromItem) {
DataSet dataSet;
JoinType joinType = fromItem.getJoin();
if (fromItem.getTable() != null) {
// We need to materialize a single table
Table table = fromItem.getTable();
Column[] columns = table.getColumns();
Query query = fromItem.getQuery();
if (query != null) {
List selectItems = collectSelectItems(query
.getSelectClause().getItems(), query.getWhereClause()
.getEvaluatedSelectItems(), query.getHavingClause()
.getEvaluatedSelectItems());
List colsToMaterialize = new ArrayList();
for (Iterator it = selectItems.iterator(); it
.hasNext();) {
SelectItem selectItem = it.next();
Column column = selectItem.getColumn();
if (column != null && column.getTable() != null
&& column.getTable().equals(table)) {
colsToMaterialize.add(column);
}
}
selectItems = null;
columns = colsToMaterialize
.toArray(new Column[colsToMaterialize.size()]);
}
// Dispatching to the concrete subclass of
// QueryPostprocessDataContextStrategy
if (_log.isDebugEnabled()) {
_log.debug("calling materializeTable(" + table.getName() + ","
+ ArrayUtils.toString(columns) + ",-1");
}
dataSet = materializeTable(table, columns, -1);
} else if (joinType != null) {
// We need to (recursively) materialize a joined FromItem
if (fromItem.getLeftSide() == null
|| fromItem.getRightSide() == null) {
throw new IllegalArgumentException(
"Joined FromItem requires both left and right side: "
+ fromItem);
}
DataSet[] fromItemDataSets = new DataSet[2];
fromItemDataSets[0] = materializeFromItem(fromItem.getLeftSide());
fromItemDataSets[1] = materializeFromItem(fromItem.getRightSide());
SelectItem[] leftOn = fromItem.getLeftOn();
SelectItem[] rightOn = fromItem.getRightOn();
FilterItem[] onConditions = new FilterItem[leftOn.length];
for (int i = 0; i < onConditions.length; i++) {
FilterItem whereItem = new FilterItem(leftOn[i],
OperatorType.EQUALS_TO, rightOn[i]);
onConditions[i] = whereItem;
}
if (joinType == JoinType.INNER) {
dataSet = MetaModelHelper.getCarthesianProduct(
fromItemDataSets, onConditions);
} else if (joinType == JoinType.LEFT) {
dataSet = MetaModelHelper.getLeftJoin(fromItemDataSets[0],
fromItemDataSets[1], onConditions);
} else if (joinType == JoinType.RIGHT) {
dataSet = MetaModelHelper.getRightJoin(fromItemDataSets[0],
fromItemDataSets[1], onConditions);
} else {
throw new IllegalArgumentException(
"FromItem type not supported: " + fromItem);
}
} else if (fromItem.getSubQuery() != null) {
// We need to (recursively) materialize a subquery
dataSet = executeQuery(fromItem.getSubQuery());
} else {
throw new IllegalArgumentException("FromItem type not supported: "
+ fromItem);
}
if (dataSet == null) {
throw new IllegalStateException(
"FromItem was not succesfully materialized: " + fromItem);
}
return dataSet;
}
protected DataSet materializeTable(Table table, Column[] columns,
int maxRows) {
if (table != null) {
Schema schema = table.getSchema();
if (INFORMATION_SCHEMA_NAME.equals(schema.getName())) {
return materializeInformationSchemaTable(table, columns,
maxRows);
} else {
DataSet tableDataSet = materializeMainSchemaTable(table,
columns, maxRows);
if (_columnTypeTransformers.isEmpty()) {
return tableDataSet;
} else {
DataSet transformedDataSet = new DataSet(
new ColumnTypeTransformerDataSetStrategy(
tableDataSet, _columnTypeTransformers));
return transformedDataSet;
}
}
}
return null;
}
private List collectSelectItems(
List selectClauseItems,
List whereClauseItems,
List havingClauseItems) {
ArrayList result = new ArrayList(
selectClauseItems);
for (SelectItem selectItem : whereClauseItems) {
if (!result.contains(selectItem)) {
result.add(selectItem);
}
}
for (SelectItem selectItem : havingClauseItems) {
if (!result.contains(selectItem)) {
result.add(selectItem);
}
}
return result;
}
public String[] getSchemaNames() throws MetaModelException {
if (_schemaNames == null) {
_schemaNames = new String[2];
_schemaNames[0] = INFORMATION_SCHEMA_NAME;
_schemaNames[1] = getMainSchemaName();
}
return _schemaNames;
}
public String getDefaultSchemaName() throws MetaModelException {
return getSchemaNames()[1];
}
public Schema getSchemaByName(String name) throws MetaModelException {
if (name != null) {
if (name.equals(getSchemaNames()[1])) {
return getMainSchemaInternal();
} else if (name.equals(INFORMATION_SCHEMA_NAME)) {
return getInformationSchema();
}
}
return null;
}
private Schema getInformationSchema() {
// Create schema
Schema informationSchema = new Schema(INFORMATION_SCHEMA_NAME);
Table tablesTable = new Table("tables", TableType.TABLE,
informationSchema);
Table columnsTable = new Table("columns", TableType.TABLE,
informationSchema);
Table relationshipsTable = new Table("relationships", TableType.TABLE,
informationSchema);
informationSchema.addTable(tablesTable).addTable(columnsTable)
.addTable(relationshipsTable);
// Create "tables" table: name, type, num_columns, remarks
tablesTable.addColumn(new Column("name", ColumnType.VARCHAR,
tablesTable, 0, false));
tablesTable.addColumn(new Column("type", ColumnType.VARCHAR,
tablesTable, 1, true));
tablesTable.addColumn(new Column("num_columns", ColumnType.INTEGER,
tablesTable, 2, true));
tablesTable.addColumn(new Column("remarks", ColumnType.VARCHAR,
tablesTable, 3, true));
// Create "columns" table: name, type, native_type, size, nullable,
// indexed, table, remarks
columnsTable.addColumn(new Column("name", ColumnType.VARCHAR,
columnsTable, 0, false));
columnsTable.addColumn(new Column("type", ColumnType.VARCHAR,
columnsTable, 1, true));
columnsTable.addColumn(new Column("native_type", ColumnType.VARCHAR,
columnsTable, 2, true));
columnsTable.addColumn(new Column("size", ColumnType.INTEGER,
columnsTable, 3, true));
columnsTable.addColumn(new Column("nullable", ColumnType.BOOLEAN,
columnsTable, 4, true));
columnsTable.addColumn(new Column("indexed", ColumnType.BOOLEAN,
columnsTable, 5, true));
columnsTable.addColumn(new Column("table", ColumnType.VARCHAR,
columnsTable, 6, false));
columnsTable.addColumn(new Column("remarks", ColumnType.VARCHAR,
columnsTable, 7, true));
// Create "relationships" table: primary_table, primary_column,
// foreign_table, foreign_column
relationshipsTable.addColumn(new Column("primary_table",
ColumnType.VARCHAR, relationshipsTable, 0, false));
relationshipsTable.addColumn(new Column("primary_column",
ColumnType.VARCHAR, relationshipsTable, 1, false));
relationshipsTable.addColumn(new Column("foreign_table",
ColumnType.VARCHAR, relationshipsTable, 2, false));
relationshipsTable.addColumn(new Column("foreign_column",
ColumnType.VARCHAR, relationshipsTable, 3, false));
Relationship.createRelationship(tablesTable.getColumnByName("name"),
columnsTable.getColumnByName("table"));
Relationship.createRelationship(tablesTable.getColumnByName("name"),
relationshipsTable.getColumnByName("primary_table"));
Relationship.createRelationship(tablesTable.getColumnByName("name"),
relationshipsTable.getColumnByName("foreign_table"));
Relationship.createRelationship(columnsTable.getColumnByName("name"),
relationshipsTable.getColumnByName("primary_column"));
Relationship.createRelationship(columnsTable.getColumnByName("name"),
relationshipsTable.getColumnByName("foreign_column"));
return informationSchema;
}
private DataSet materializeInformationSchemaTable(Table table,
Column[] columns, int maxRows) {
DataSet dataSet = null;
String tableName = table.getName();
if ("tables".equals(tableName)) {
// "tables" columns: name, type, num_columns, remarks
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy