All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dashbuilder.dataprovider.sql.SQLDataSetProvider Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed 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.dashbuilder.dataprovider.sql;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.naming.InitialContext;
import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.dashbuilder.DataSetCore;
import org.dashbuilder.dataprovider.DataSetProvider;
import org.dashbuilder.dataprovider.DataSetProviderType;
import org.dashbuilder.dataprovider.StaticDataSetProvider;
import org.dashbuilder.dataprovider.sql.dialect.Dialect;
import org.dashbuilder.dataprovider.sql.model.Column;
import org.dashbuilder.dataprovider.sql.model.Condition;
import org.dashbuilder.dataprovider.sql.model.Select;
import org.dashbuilder.dataprovider.sql.model.SortColumn;
import org.dashbuilder.dataprovider.sql.model.Table;
import org.dashbuilder.dataset.ColumnType;
import org.dashbuilder.dataset.DataColumn;
import org.dashbuilder.dataset.DataSet;
import org.dashbuilder.dataset.DataSetFactory;
import org.dashbuilder.dataset.DataSetLookup;
import org.dashbuilder.dataset.DataSetMetadata;
import org.dashbuilder.dataset.DataSetOp;
import org.dashbuilder.dataset.DataSetOpEngine;
import org.dashbuilder.dataset.IntervalBuilderDynamicDate;
import org.dashbuilder.dataset.date.DateUtils;
import org.dashbuilder.dataset.date.TimeFrame;
import org.dashbuilder.dataset.def.DataColumnDef;
import org.dashbuilder.dataset.def.DataSetDef;
import org.dashbuilder.dataset.def.DataSetDefRegistry;
import org.dashbuilder.dataset.def.DataSetDefRegistryListener;
import org.dashbuilder.dataset.def.SQLDataSetDef;
import org.dashbuilder.dataset.def.SQLDataSourceDef;
import org.dashbuilder.dataset.engine.group.IntervalBuilder;
import org.dashbuilder.dataset.engine.group.IntervalBuilderLocator;
import org.dashbuilder.dataset.engine.group.IntervalList;
import org.dashbuilder.dataset.filter.ColumnFilter;
import org.dashbuilder.dataset.filter.CoreFunctionFilter;
import org.dashbuilder.dataset.filter.CoreFunctionType;
import org.dashbuilder.dataset.filter.DataSetFilter;
import org.dashbuilder.dataset.filter.FilterFactory;
import org.dashbuilder.dataset.filter.LogicalExprFilter;
import org.dashbuilder.dataset.filter.LogicalExprType;
import org.dashbuilder.dataset.group.AggregateFunctionType;
import org.dashbuilder.dataset.group.ColumnGroup;
import org.dashbuilder.dataset.group.DataSetGroup;
import org.dashbuilder.dataset.group.DateIntervalType;
import org.dashbuilder.dataset.group.GroupFunction;
import org.dashbuilder.dataset.group.GroupStrategy;
import org.dashbuilder.dataset.group.Interval;
import org.dashbuilder.dataset.impl.DataColumnImpl;
import org.dashbuilder.dataset.impl.DataSetMetadataImpl;
import org.dashbuilder.dataset.impl.MemSizeEstimator;
import org.dashbuilder.dataset.sort.ColumnSort;
import org.dashbuilder.dataset.sort.DataSetSort;
import org.dashbuilder.dataset.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *  DataSetProvider implementation for JDBC-compliant data sources.
 *
 *  

The SQL provider resolves every data set lookup request by transforming such request into the proper SQL query. * In some cases, an extra processing of the resulting data is required since some lookup requests do not map directly * into the SQL world. In such cases, specially the grouping of date based data, the core data set operation engine is * used.

* *

* Pending stuff: * - Filter on foreign data sets * - Group (fixed) by date of week *

*/ public class SQLDataSetProvider implements DataSetProvider, DataSetDefRegistryListener { private static SQLDataSetProvider SINGLETON = null; public static SQLDataSetProvider get() { if (SINGLETON == null) { DataSetCore dataSetCore = DataSetCore.get(); StaticDataSetProvider staticDataSetProvider = dataSetCore.getStaticDataSetProvider(); DataSetDefRegistry dataSetDefRegistry = dataSetCore.getDataSetDefRegistry(); DataSetOpEngine dataSetOpEngine = dataSetCore.getSharedDataSetOpEngine(); IntervalBuilderLocator intervalBuilderLocator = dataSetCore.getIntervalBuilderLocator(); IntervalBuilderDynamicDate intervalBuilderDynamicDate = dataSetCore.getIntervalBuilderDynamicDate(); SINGLETON = new SQLDataSetProvider( staticDataSetProvider, intervalBuilderLocator, intervalBuilderDynamicDate, dataSetOpEngine); SINGLETON.setDataSourceLocator(new SQLDataSourceLocator() { @Override public DataSource lookup(SQLDataSetDef def) throws Exception { InitialContext ctx = new InitialContext(); return (DataSource) ctx.lookup(def.getDataSource()); } @Override public List list() { return JDBCUtils.listDatasourceDefs(); } }); dataSetDefRegistry.addListener(SINGLETON); } return SINGLETON; } protected Logger log = LoggerFactory.getLogger(SQLDataSetProvider.class); protected StaticDataSetProvider staticDataSetProvider; protected SQLDataSourceLocator dataSourceLocator; protected IntervalBuilderLocator intervalBuilderLocator; protected IntervalBuilderDynamicDate intervalBuilderDynamicDate; protected DataSetOpEngine opEngine; public SQLDataSetProvider() { } public SQLDataSetProvider(StaticDataSetProvider staticDataSetProvider, IntervalBuilderLocator intervalBuilderLocator, IntervalBuilderDynamicDate intervalBuilderDynamicDate, DataSetOpEngine opEngine) { this.staticDataSetProvider = staticDataSetProvider; this.intervalBuilderLocator = intervalBuilderLocator; this.intervalBuilderDynamicDate = intervalBuilderDynamicDate; this.opEngine = opEngine; } public StaticDataSetProvider getStaticDataSetProvider() { return staticDataSetProvider; } public void setStaticDataSetProvider(StaticDataSetProvider staticDataSetProvider) { this.staticDataSetProvider = staticDataSetProvider; } public SQLDataSourceLocator getDataSourceLocator() { return dataSourceLocator; } public void setDataSourceLocator(SQLDataSourceLocator dataSourceLocator) { this.dataSourceLocator = dataSourceLocator; } public IntervalBuilderLocator getIntervalBuilderLocator() { return intervalBuilderLocator; } public void setIntervalBuilderLocator(IntervalBuilderLocator intervalBuilderLocator) { this.intervalBuilderLocator = intervalBuilderLocator; } public IntervalBuilderDynamicDate getIntervalBuilderDynamicDate() { return intervalBuilderDynamicDate; } public void setIntervalBuilderDynamicDate(IntervalBuilderDynamicDate intervalBuilderDynamicDate) { this.intervalBuilderDynamicDate = intervalBuilderDynamicDate; } public DataSetOpEngine getOpEngine() { return opEngine; } public void setOpEngine(DataSetOpEngine opEngine) { this.opEngine = opEngine; } public DataSetProviderType getType() { return DataSetProviderType.SQL; } public DataSet lookupDataSet(DataSetDef def, DataSetLookup lookup) throws Exception { SQLDataSetDef sqlDef = (SQLDataSetDef) def; if (StringUtils.isBlank(sqlDef.getDataSource())) { throw new IllegalArgumentException("Missing data source in SQL data set definition: " + sqlDef); } if (StringUtils.isBlank(sqlDef.getDbSQL()) && StringUtils.isBlank(sqlDef.getDbTable())) { throw new IllegalArgumentException("Missing DB table or SQL in the data set definition: " + sqlDef); } // Look first into the static data set provider cache. if (sqlDef.isCacheEnabled()) { DataSet dataSet = staticDataSetProvider.lookupDataSet(def.getUUID(), null); if (dataSet != null) { // Lookup from cache. return staticDataSetProvider.lookupDataSet(def.getUUID(), lookup); } else { // Fetch always from database if existing rows are greater than the cache max. rows DataSetMetadata metadata = getDataSetMetadata(def); int rows = metadata.getNumberOfRows(); if (rows > sqlDef.getCacheMaxRows()) { return _lookupDataSet(sqlDef, lookup); } // Fetch from database and register into the static cache. Further requests will lookup from cache. dataSet = _lookupDataSet(sqlDef, null); dataSet.setUUID(def.getUUID()); dataSet.setDefinition(def); staticDataSetProvider.registerDataSet(dataSet); return staticDataSetProvider.lookupDataSet(def.getUUID(), lookup); } } // If cache is disabled then always fetch from database. return _lookupDataSet(sqlDef, lookup); } public boolean isDataSetOutdated(DataSetDef def) { // Non fetched data sets can't get outdated. MetadataHolder last = _metadataMap.remove(def.getUUID()); if (last == null) return false; // Check if the metadata has changed since the last time it was fetched. try { DataSetMetadata current = getDataSetMetadata(def); return !current.equals(last.metadata); } catch (Exception e) { log.error("Error fetching metadata: " + def, e); return false; } } public DataSetMetadata getDataSetMetadata(DataSetDef def) throws Exception { SQLDataSetDef sqlDef = (SQLDataSetDef) def; DataSource ds = dataSourceLocator.lookup(sqlDef); Connection conn = ds.getConnection(); try { return _getDataSetMetadata(sqlDef, conn, true); } finally { conn.close(); } } // Listen to changes on the data set definition registry @Override public void onDataSetDefStale(DataSetDef def) { if (DataSetProviderType.SQL.equals(def.getProvider())) { staticDataSetProvider.removeDataSet(def.getUUID()); } } @Override public void onDataSetDefModified(DataSetDef olDef, DataSetDef newDef) { if (DataSetProviderType.SQL.equals(olDef.getProvider())) { String uuid = olDef.getUUID(); _metadataMap.remove(uuid); staticDataSetProvider.removeDataSet(uuid); } } @Override public void onDataSetDefRemoved(DataSetDef oldDef) { if (DataSetProviderType.SQL.equals(oldDef.getProvider())) { String uuid = oldDef.getUUID(); _metadataMap.remove(uuid); staticDataSetProvider.removeDataSet(uuid); } } @Override public void onDataSetDefRegistered(DataSetDef newDef) { } // Internal implementation logic protected class MetadataHolder { DataSetMetadataImpl metadata; List columns; } protected transient Map _metadataMap = new HashMap(); protected Column _getDbColumn(Collection dbColumns, String columnId) { for (Column dbColumn: dbColumns) { if (dbColumn.getName().equalsIgnoreCase(columnId)) { return dbColumn; } } return null; } protected DataSetMetadata _getDataSetMetadata(SQLDataSetDef def, Connection conn, boolean skipCache) throws Exception { // Check the cache if (!skipCache) { MetadataHolder result = _metadataMap.get(def.getUUID()); if (result != null) { return result.metadata; } } // Fetch the DB columns from the table or sql List dbColumns = _getColumns(def, conn); List targetDbColumnIds = new ArrayList(); List targetDbColumnTypes = new ArrayList(); List targetDbColumnsLength = new ArrayList(); // Check the definition columns match those in the DB if (def.getColumns() != null) { for (DataColumnDef column : def.getColumns()) { Column dbColumn = _getDbColumn(dbColumns, column.getId()); if (dbColumn == null) { throw new IllegalArgumentException("The DataSetDef's column does not exist in DB: " + column.getId()); } targetDbColumnIds.add(dbColumn.getName()); targetDbColumnTypes.add(column.getColumnType()); targetDbColumnsLength.add(dbColumn.getLength()); } } // Add or skip non-existing columns depending on the data set definition. for (Column dbColumn : dbColumns) { String dbColumnId = dbColumn.getName(); int columnIdx = targetDbColumnIds.indexOf(dbColumnId); boolean columnExists = columnIdx != -1; if (!columnExists) { // Add any table column if (def.isAllColumnsEnabled()) { targetDbColumnIds.add(dbColumnId); targetDbColumnTypes.add(dbColumn.getType()); targetDbColumnsLength.add(dbColumn.getLength()); } // Skip non existing columns else { continue; } } } // Ensure the column set is valid if (targetDbColumnIds.isEmpty()) { throw new IllegalArgumentException("No data set columns found: " + def); } // Creates a brand new metadata holder instance MetadataHolder result = new MetadataHolder(); result.columns = dbColumns; result.metadata = new DataSetMetadataImpl(def, def.getUUID(), 0, targetDbColumnIds.size(), targetDbColumnIds, targetDbColumnTypes, 0); // Calculate the estimated size int rowCount = _getRowCount(result.metadata, def, conn); int estimatedSize = 0; for (int i=0; i _getColumns(SQLDataSetDef def, Connection conn) throws Exception { Dialect dialect = JDBCUtils.dialect(conn); if (!StringUtils.isBlank(def.getDbSQL())) { Select query = SQLFactory.select(conn).from(def.getDbSQL()).limit(1); return JDBCUtils.getColumns(logSQL(query).fetch(), dialect.getExcludedColumns()); } else { Select query = SQLFactory.select(conn).from(_createTable(def)).limit(1); return JDBCUtils.getColumns(logSQL(query).fetch(), dialect.getExcludedColumns()); } } protected int _getRowCount(DataSetMetadata metadata, SQLDataSetDef def, Connection conn) throws Exception { // Count rows, either on an SQL or a DB table Select _query = SQLFactory.select(conn); _appendFrom(def, _query); // Filters set must be taken into account DataSetFilter filterOp = def.getDataSetFilter(); if (filterOp != null) { List filterList = filterOp.getColumnFilterList(); for (ColumnFilter filter : filterList) { _appendFilterBy(metadata, def, filter, _query); } } return _query.fetchCount(); } protected DataSet _lookupDataSet(SQLDataSetDef def, DataSetLookup lookup) throws Exception { LookupProcessor processor = new LookupProcessor(def, lookup); return processor.run(); } protected Table _createTable(SQLDataSetDef def) { if (StringUtils.isBlank(def.getDbSchema())) return SQLFactory.table(def.getDbTable()); else return SQLFactory.table(def.getDbSchema(), def.getDbTable()); } protected void _appendFrom(SQLDataSetDef def, Select _query) { if (!StringUtils.isBlank(def.getDbSQL())) _query.from(def.getDbSQL()); else _query.from(_createTable(def)); } protected void _appendFilterBy(DataSetMetadata metadata, SQLDataSetDef def, DataSetFilter filterOp, Select _query) { List filterList = filterOp.getColumnFilterList(); for (ColumnFilter filter : filterList) { _appendFilterBy(metadata, def, filter, _query); } } protected void _appendFilterBy(DataSetMetadata metadata, SQLDataSetDef def, ColumnFilter filter, Select _query) { Condition condition = _createCondition(metadata, def, filter); if (condition != null) { _query.where(condition); } } protected Condition _createCondition(DataSetMetadata metadata, SQLDataSetDef def, ColumnFilter filter) { if (filter instanceof CoreFunctionFilter) { String filterId = _columnFromMetadata(metadata, filter.getColumnId()); Column _column = SQLFactory.column(filterId); CoreFunctionFilter f = (CoreFunctionFilter) filter; CoreFunctionType type = f.getType(); List params = f.getParameters(); if (CoreFunctionType.IS_NULL.equals(type)) { return _column.isNull(); } if (CoreFunctionType.NOT_NULL.equals(type)) { return _column.notNull(); } if (CoreFunctionType.EQUALS_TO.equals(type)) { if (params.isEmpty()) { return null; } if (params.size() == 1) { return _column.equalsTo(params.get(0)); } return _column.in(params); } if (CoreFunctionType.NOT_EQUALS_TO.equals(type)) { if (params.isEmpty()) { return null; } if (params.size() == 1) { return _column.notEquals(params.get(0)); } return _column.in(params).not(); } if (CoreFunctionType.LIKE_TO.equals(type)) { String pattern = (String) params.get(0); boolean caseSensitive = params.size() < 2 || Boolean.parseBoolean(params.get(1).toString()); if (caseSensitive) { return _column.like(pattern); } else { return _column.lower().like(pattern.toLowerCase()); } } if (CoreFunctionType.LOWER_THAN.equals(type)) { return _column.lowerThan(params.get(0)); } if (CoreFunctionType.LOWER_OR_EQUALS_TO.equals(type)) { return _column.lowerOrEquals(params.get(0)); } if (CoreFunctionType.GREATER_THAN.equals(type)) { return _column.greaterThan(params.get(0)); } if (CoreFunctionType.GREATER_OR_EQUALS_TO.equals(type)) { return _column.greaterOrEquals(params.get(0)); } if (CoreFunctionType.BETWEEN.equals(type)) { Object low = params.get(0); Object high= params.get(1); if (low == null && high == null) { return null; } if (low != null && high == null) { return _column.greaterOrEquals(low); } if (low == null && high != null) { return _column.lowerOrEquals(high); } return _column.between(low, high); } if (CoreFunctionType.TIME_FRAME.equals(type)) { TimeFrame timeFrame = TimeFrame.parse(params.get(0).toString()); if (timeFrame != null) { java.sql.Date past = new java.sql.Date(timeFrame.getFrom().getTimeInstant().getTime()); java.sql.Date future = new java.sql.Date(timeFrame.getTo().getTimeInstant().getTime()); return _column.between(past, future); } } if (CoreFunctionType.IN.equals(type) && params instanceof List) { if (params.isEmpty()) { return null; } return _column.inSql((List)params); } if (CoreFunctionType.NOT_IN.equals(type) && params instanceof List) { if (params.isEmpty()) { return null; } return _column.notInSql((List)params); } } if (filter instanceof LogicalExprFilter) { LogicalExprFilter f = (LogicalExprFilter) filter; LogicalExprType type = f.getLogicalOperator(); Condition condition = null; List logicalTerms = f.getLogicalTerms(); for (int i=0; i postProcessingOps = new ArrayList(); public LookupProcessor(SQLDataSetDef def, DataSetLookup lookup) { this.def = def; this.lookup = lookup; DataSetFilter dataSetFilter = def.getDataSetFilter(); if (dataSetFilter != null) { if (lookup == null) { this.lookup = new DataSetLookup(def.getUUID(), dataSetFilter); } else { this.lookup.addOperation(dataSetFilter); } } } public boolean groupColumnMustBeIncluded(DataSetGroup groupOp) { if (groupOp != null) { ColumnGroup cg = groupOp.getColumnGroup(); if (cg != null) { for (GroupFunction gf : groupOp.getGroupFunctions()) { if (cg.getSourceId().equals(gf.getSourceId()) && gf.getFunction() == null) { return false; } } for (GroupFunction gf : groupOp.getGroupFunctions()) { if (!cg.getSourceId().equals(gf.getSourceId()) && gf.getFunction() == null) { return true; } } } } return false; } public DataSet run() throws Exception { DataSource ds = dataSourceLocator.lookup(def); conn = ds.getConnection(); try { boolean skipCache = lookup != null && lookup.testMode(); metadata = _getDataSetMetadata(def, conn, skipCache); int totalRows = metadata.getNumberOfRows(); boolean trim = (lookup != null && (lookup.getNumberOfRows() > 0 || lookup.getRowOffset() > 0)); // The whole data set if (lookup == null || lookup.getOperationList().isEmpty()) { // Prepare the select _query = SQLFactory.select(conn).columns(_createAllColumns()); _appendFrom(def, _query); // Row limits if (trim && postProcessingOps.isEmpty()) { totalRows = _query.fetchCount(); _query.limit(lookup.getNumberOfRows()).offset(lookup.getRowOffset()); } // Fetch the results and build the data set ResultSet _results = logSQL(_query).fetch(); List columns = calculateColumns(null); DataSet dataSet = _buildDataSet(columns, _results); if (trim) { dataSet.setRowCountNonTrimmed(totalRows); } return dataSet; } // ... or a list of operations. else { DataSetGroup groupOp = null; int groupIdx = lookup.getFirstGroupOpIndex(0, null, false); if (groupIdx != -1) groupOp = lookup.getOperation(groupIdx); // Prepare the select _query = SQLFactory.select(conn).columns(_createColumns(groupOp)); _appendFrom(def, _query); // Append the filter clauses for (DataSetFilter filterOp : lookup.getOperationList(DataSetFilter.class)) { _appendFilterBy(metadata, def, filterOp, _query); } // Append the interval selections List intervalSelects = lookup.getFirstGroupOpSelections(); for (DataSetGroup intervalSelect : intervalSelects) { _appendIntervalSelection(intervalSelect, _query); } // ... the group by clauses ColumnGroup cg = null; boolean groupColumnAdded = groupColumnMustBeIncluded(groupOp); if (groupOp != null) { cg = groupOp.getColumnGroup(); if (cg != null) { _appendGroupBy(groupOp); // The in-memory post processing requires that the group column is also included. // (see DASHBUILDE-181: Error "Column not found" when adding group by column from SQL dataset) if (groupColumnAdded) { GroupFunction gf = new GroupFunction(cg.getSourceId(), cg.getColumnId(), null); groupOp.getGroupFunctions().add(gf); _query.columns(_createColumn(cg)); } } } // ... the sort clauses DataSetSort sortOp = lookup.getFirstSortOp(); if (sortOp != null) { if (cg != null) { _appendOrderGroupBy(groupOp, sortOp, groupColumnAdded); } else { _appendOrderBy(sortOp); } } else if (cg != null) { _appendOrderGroupBy(groupOp); } // ... and the row limits. // If post-processing then defer the trim operation in order to not leave out rows if (trim && postProcessingOps.isEmpty()) { totalRows = _query.fetchCount(); _query.limit(lookup.getNumberOfRows()).offset(lookup.getRowOffset()); } // Fetch the results and build the data set ResultSet _results = logSQL(_query).fetch(); List columns = calculateColumns(groupOp); DataSet dataSet = _buildDataSet(columns, _results); if (trim && postProcessingOps.isEmpty()) { dataSet.setRowCountNonTrimmed(totalRows); } return dataSet; } } finally { conn.close(); } } protected DateIntervalType calculateDateInterval(ColumnGroup cg) { if (dateIntervalType != null) { return dateIntervalType; } if (GroupStrategy.DYNAMIC.equals(cg.getStrategy())) { Date[] limits = calculateDateLimits(cg.getSourceId()); if (limits != null) { dateIntervalType = intervalBuilderDynamicDate.calculateIntervalSize(limits[0], limits[1], cg); return dateIntervalType; } } dateIntervalType = DateIntervalType.getByName(cg.getIntervalSize()); return dateIntervalType; } protected Date[] calculateDateLimits(String dateColumnId) { if (dateLimits != null) { return dateLimits; } Date minDate = calculateDateLimit(dateColumnId, true); Date maxDate = calculateDateLimit(dateColumnId, false); return dateLimits = new Date[] {minDate, maxDate}; } protected Date calculateDateLimit(String dateColumnId, boolean min) { String dbColumnId = _columnFromMetadata(metadata, dateColumnId); Column _dateColumn = SQLFactory.column(dbColumnId); Select _limitsQuery = SQLFactory.select(conn).columns(_dateColumn); _appendFrom(def, _limitsQuery); // Append the filter clauses for (DataSetFilter filterOp : lookup.getOperationList(DataSetFilter.class)) { _appendFilterBy(metadata, def, filterOp, _limitsQuery); } // Append group interval selection filters List intervalSelects = lookup.getFirstGroupOpSelections(); for (DataSetGroup intervalSelect : intervalSelects) { _appendIntervalSelection(intervalSelect, _limitsQuery); } try { // Fetch the date ResultSet rs = logSQL(_limitsQuery .where(_dateColumn.notNull()) .orderBy(min ? _dateColumn.asc() : _dateColumn.desc()) .limit(1)).fetch(); if (!rs.next()) { return null; } else { return rs.getDate(1); } } catch (SQLException e) { log.error("Error reading date limit from query results", e); return null; } } protected List calculateColumns(DataSetGroup gOp) { List result = new ArrayList<>(); if (gOp == null) { for (int i = 0; i < metadata.getNumberOfColumns(); i++) { String columnId = metadata.getColumnId(i); ColumnType columnType = metadata.getColumnType(i); DataColumn column = new DataColumnImpl(columnId, columnType); result.add(column); } } else { ColumnGroup cg = gOp.getColumnGroup(); for (GroupFunction gf : gOp.getGroupFunctions()) { String sourceId = gf.getSourceId(); String columnId = _getTargetColumnId(gf); ColumnType columnType = metadata.getColumnType(sourceId); DataColumnImpl column = new DataColumnImpl(); column.setId(columnId); column.setGroupFunction(gf); result.add(column); // Group column if (cg != null && cg.getSourceId().equals(sourceId) && gf.getFunction() == null) { column.setColumnType(ColumnType.LABEL); column.setColumnGroup(cg); if (ColumnType.DATE.equals(columnType)) { column.setIntervalType(dateIntervalType != null ? dateIntervalType.toString() : null); column.setMinValue(dateLimits != null ? dateLimits[0] : null); column.setMaxValue(dateLimits != null ? dateLimits[1] : null); } } // Function column else if (gf.getFunction() != null) { ColumnType resultType = gf.getFunction().getResultType(columnType); column.setColumnType(resultType); } // Existing Column else { column.setColumnType(columnType); } } // DASHBUILDE-181: Error "Column not found" when adding group by column from SQL dataset if (groupColumnMustBeIncluded(gOp)) { GroupFunction gf = new GroupFunction(cg.getSourceId(), cg.getColumnId(), null); gOp.getGroupFunctions().add(gf); } } return result; } protected void _appendOrderBy(DataSetSort sortOp) { List _columns = new ArrayList<>(); List sortList = sortOp.getColumnSortList(); for (ColumnSort columnSort : sortList) { String dbColumnId = _columnFromMetadata(metadata, columnSort.getColumnId()); if (SortOrder.DESCENDING.equals(columnSort.getOrder())) { _columns.add(SQLFactory.column(dbColumnId).desc()); } else { _columns.add(SQLFactory.column(dbColumnId).asc()); } } _query.orderBy(_columns); } protected boolean isDynamicDateGroup(DataSetGroup groupOp) { ColumnGroup cg = groupOp.getColumnGroup(); if (!ColumnType.DATE.equals(metadata.getColumnType(cg.getSourceId()))) { return false; } if (!GroupStrategy.DYNAMIC.equals(cg.getStrategy())) { return false; } return true; } protected void _appendOrderGroupBy(DataSetGroup groupOp) { if (isDynamicDateGroup(groupOp)) { ColumnGroup cg = groupOp.getColumnGroup(); _query.orderBy(_createColumn(cg).asc()); } } protected void _appendOrderGroupBy(DataSetGroup groupOp, DataSetSort sortOp, boolean post) { List _columns = new ArrayList<>(); List sortList = sortOp.getColumnSortList(); ColumnGroup cg = groupOp.getColumnGroup(); boolean sortPost = post; if (post) { postProcessingOps.add(sortOp); sortPost = false; } for (ColumnSort cs : sortList) { GroupFunction gf = groupOp.getGroupFunction(cs.getColumnId()); // Sort by the group column if (cg.getSourceId().equals(cs.getColumnId()) || cg.getColumnId().equals(cs.getColumnId())) { if (SortOrder.DESCENDING.equals(cs.getOrder())) { _columns.add(_createColumn(cg).desc()); if (isDynamicDateGroup(groupOp) && !sortPost) { postProcessingOps.add(sortOp); } } else { _columns.add(_createColumn(cg).asc()); if (isDynamicDateGroup(groupOp) && !sortPost) { postProcessingOps.add(sortOp); } } } // Sort by an aggregation else if (gf != null) { // In SQL, sort is only permitted for columns belonging to the result set. if (SortOrder.DESCENDING.equals(cs.getOrder())) { _columns.add(_createColumn(gf).desc()); } else { _columns.add(_createColumn(gf).asc()); } } } _query.orderBy(_columns); } protected void _appendIntervalSelection(DataSetGroup intervalSel, Select _query) { if (intervalSel != null && intervalSel.isSelect()) { ColumnGroup cg = intervalSel.getColumnGroup(); List intervalList = intervalSel.getSelectedIntervalList(); // Get the filter values List names = new ArrayList(); Comparable min = null; Comparable max = null; for (Interval interval : intervalList) { names.add(interval.getName()); Comparable intervalMin = (Comparable) interval.getMinValue(); Comparable intervalMax = (Comparable) interval.getMaxValue(); if (intervalMin != null) { if (min == null) min = intervalMin; else if (min.compareTo(intervalMin) > 0) min = intervalMin; } if (intervalMax != null) { if (max == null) max = intervalMax; else if (max.compareTo(intervalMax) > 0) max = intervalMax; } } // Min can't be greater than max. if (min != null && max != null && min.compareTo(max) > 0) { min = max; } // Apply the filter ColumnFilter filter; if (min != null && max != null) { filter = FilterFactory.between(cg.getSourceId(), min, max); } else if (min != null) { filter = FilterFactory.greaterOrEqualsTo(cg.getSourceId(), min); } else if (max != null) { filter = FilterFactory.lowerOrEqualsTo(cg.getSourceId(), max); } else { filter = FilterFactory.equalsTo(cg.getSourceId(), names); } _appendFilterBy(metadata, def, filter, _query); } } protected void _appendGroupBy(DataSetGroup groupOp) { ColumnGroup cg = groupOp.getColumnGroup(); String sourceId = cg.getSourceId(); String dbColumnId = _columnFromMetadata(metadata, sourceId); ColumnType columnType = metadata.getColumnType(dbColumnId); boolean postProcessing = false; // Group by Text => not supported if (ColumnType.TEXT.equals(columnType)) { throw new IllegalArgumentException("Group by text '" + sourceId + NOT_SUPPORTED); } // Group by Date else if (ColumnType.DATE.equals(columnType)) { _query.groupBy(_createColumn(cg)); postProcessing = true; } // Group by Label or Number (treated as label) else { _query.groupBy(SQLFactory.column(dbColumnId)); for (GroupFunction gf : groupOp.getGroupFunctions()) { if (!sourceId.equals(gf.getSourceId()) && gf.getFunction() == null) { postProcessing = true; } } } // Also add any non-aggregated column (columns pick up) to the group statement for (GroupFunction gf : groupOp.getGroupFunctions()) { if (gf.getFunction() == null && !gf.getSourceId().equalsIgnoreCase(cg.getSourceId())) { String dbGfId = _columnFromMetadata(metadata, gf.getSourceId()); _query.groupBy(SQLFactory.column(dbGfId)); } } // The group operation might require post processing if (postProcessing) { DataSetGroup postGroup = groupOp.cloneInstance(); GroupFunction gf = postGroup.getGroupFunction(sourceId); if (gf != null) { String targetId = _getTargetColumnId(gf); postGroup.getColumnGroup().setSourceId(targetId); postGroup.getColumnGroup().setColumnId(targetId); } for (GroupFunction pgf : postGroup.getGroupFunctions()) { AggregateFunctionType pft = pgf.getFunction(); pgf.setSourceId(_getTargetColumnId(pgf)); if (pft != null && (AggregateFunctionType.DISTINCT.equals(pft) || AggregateFunctionType.COUNT.equals(pft))) { pgf.setFunction(AggregateFunctionType.SUM); } } postProcessingOps.add(postGroup); } } protected DataSet _buildDataSet(List columns, ResultSet _rs) throws Exception { DataSet dataSet = DataSetFactory.newEmptyDataSet(); dataSet.setUUID(def.getUUID()); dataSet.setDefinition(def); DataColumn dateGroupColumn = null; boolean dateIncludeEmptyIntervals = false; // Create an empty data set for (int i = 0; i < columns.size(); i++) { DataColumn column = columns.get(i).cloneEmpty(); dataSet.addColumn(column); } // Offset post-processing if (_query.isOffsetPostProcessing() && _query.getOffset() > 0) { // Move the cursor to the specified offset or until the end of the result set is reached for (int i=0; i<_query.getOffset() && _rs.next(); i++); } // Populate the data set int rowIdx = 0; int numRows = _query.getLimit(); while (_rs.next() && (numRows < 0 || rowIdx++ < numRows)) { for (int i=0; i dataSet.getRowCount() && dataSet.getRowCountNonTrimmed() < 0) { List values = dateGroupColumn.getValues(); int valueIdx = 0; for (int intervalIdx = 0; intervalIdx < intervalList.size(); intervalIdx++) { String interval = intervalList.get(intervalIdx).getName(); String value = values.isEmpty() ? null : (String) values.get(valueIdx++); if (value == null || !value.equals(interval)) { dataSet.addEmptyRowAt(intervalIdx); dateGroupColumn.getValues().set(intervalIdx, interval); } } } } return dataSet; } protected Collection _createAllColumns() { Collection columns = new ArrayList<>(); for (int i = 0; i < metadata.getNumberOfColumns(); i++) { String columnId = metadata.getColumnId(i); columns.add(SQLFactory.column(columnId)); } return columns; } protected Collection _createColumns(DataSetGroup gOp) { if (gOp == null) { return _createAllColumns(); } ColumnGroup cg = gOp.getColumnGroup(); Collection _columns = new ArrayList(); for (GroupFunction gf : gOp.getGroupFunctions()) { String sourceId = gf.getSourceId(); if (StringUtils.isBlank(sourceId)) { sourceId = metadata.getColumnId(0); } else { _assertColumnExists(metadata, sourceId); } String targetId = gf.getColumnId(); if (StringUtils.isBlank(targetId)) { targetId = sourceId; } if (cg != null && cg.getSourceId().equals(sourceId) && gf.getFunction() == null) { _columns.add(_createColumn(cg).as(targetId)); } else { _columns.add(_createColumn(gf).as(targetId)); } } return _columns; } protected Column _createColumn(GroupFunction gf) { String sourceId = gf.getSourceId(); if (sourceId == null) { sourceId = metadata.getColumnId(0); } AggregateFunctionType ft = gf.getFunction(); String dbColumnId = _columnFromMetadata(metadata, sourceId); return SQLFactory.column(dbColumnId).function(ft); } protected Column _createColumn(ColumnGroup cg) { String sourceId = cg.getSourceId(); String dbColumnId = _columnFromMetadata(metadata, sourceId); ColumnType columnType = metadata.getColumnType(dbColumnId); if (ColumnType.DATE.equals(columnType)) { DateIntervalType intervalType = calculateDateInterval(cg); if (DateIntervalType.DAY_OF_WEEK.equals(intervalType)) { throw new IllegalArgumentException("Group by DAY_OF_WEEK not supported in SQL data sets"); } return SQLFactory.column(dbColumnId, cg.getStrategy(), intervalType); } if (ColumnType.TEXT.equals(columnType)) { throw new IllegalArgumentException("Group by text '" + sourceId + NOT_SUPPORTED); } return SQLFactory.column(dbColumnId); } protected String _getTargetColumnId(GroupFunction gf) { String sourceId = gf.getSourceId(); if (sourceId != null) { _assertColumnExists(metadata, sourceId); } return gf.getColumnId() == null ? sourceId : gf.getColumnId(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy