blob: fcb0e27eeac55bd4b232d5b8e2c12d76b67d7cc6 [file] [log] [blame]
/**
* 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.apache.metamodel.jdbc.dialects;
import java.util.Date;
import java.util.List;
import org.apache.metamodel.jdbc.JdbcDataContext;
import org.apache.metamodel.query.FilterItem;
import org.apache.metamodel.query.FromItem;
import org.apache.metamodel.query.OperatorType;
import org.apache.metamodel.query.Query;
import org.apache.metamodel.query.SelectItem;
import org.apache.metamodel.schema.ColumnType;
import org.apache.metamodel.util.FormatHelper;
import org.apache.metamodel.util.TimeComparator;
/**
* Query rewriter for IBM DB2
*/
public class DB2QueryRewriter extends DefaultQueryRewriter implements IQueryRewriter {
public DB2QueryRewriter(JdbcDataContext dataContext) {
super(dataContext);
}
@Override
public String escapeQuotes(String filterItemOperand) {
return filterItemOperand.replaceAll("\\'", "\\\\'");
}
/**
* DB2 expects the fully qualified column name, including schema, in select
* items.
*/
@Override
public boolean isSchemaIncludedInColumnPaths() {
return true;
}
@Override
public boolean isMaxRowsSupported() {
return true;
}
@Override
public boolean isFirstRowSupported() {
return true;
}
@Override
public String rewriteQuery(Query query) {
final Integer firstRow = query.getFirstRow();
final Integer maxRows = query.getMaxRows();
if (maxRows == null && firstRow == null) {
return super.rewriteQuery(query);
}
if (firstRow == null || firstRow.intValue() == 1) {
// We prefer to use the "FETCH FIRST [n] ROWS ONLY" approach, if
// firstRow is not specified.
return super.rewriteQuery(query) + " FETCH FIRST " + maxRows + " ROWS ONLY";
} else {
// build a ROW_NUMBER() query like this:
// SELECT [original select clause]
// FROM ([original select clause],
// ROW_NUMBER() AS metamodel_row_number
// FROM [remainder of regular query])
// WHERE metamodel_row_number BETWEEN [firstRow] and [maxRows];
final Query innerQuery = query.clone();
innerQuery.setFirstRow(null);
innerQuery.setMaxRows(null);
final Query outerQuery = new Query();
final FromItem subQuerySelectItem = new FromItem(innerQuery).setAlias("metamodel_subquery");
outerQuery.from(subQuerySelectItem);
final List<SelectItem> innerSelectItems = innerQuery.getSelectClause().getItems();
for (SelectItem selectItem : innerSelectItems) {
outerQuery.select(new SelectItem(selectItem, subQuerySelectItem));
}
final String rewrittenOrderByClause = rewriteOrderByClause(innerQuery, innerQuery.getOrderByClause());
final String rowOver = "ROW_NUMBER() " + "OVER(" + rewrittenOrderByClause + ")";
innerQuery.select(new SelectItem(rowOver, "metamodel_row_number"));
innerQuery.getOrderByClause().removeItems();
final String baseQueryString = rewriteQuery(outerQuery);
if (maxRows == null) {
return baseQueryString + " WHERE metamodel_row_number > " + (firstRow - 1);
}
return baseQueryString + " WHERE metamodel_row_number BETWEEN " + firstRow + " AND " + (firstRow - 1 + maxRows);
}
}
@Override
public String rewriteColumnType(ColumnType columnType) {
switch (columnType) {
case BOOLEAN:
case BIT:
return "SMALLINT";
default:
return super.rewriteColumnType(columnType);
}
}
@Override
public String rewriteFilterItem(FilterItem item) {
SelectItem _selectItem = item.getSelectItem();
Object _operand = item.getOperand();
OperatorType _operator = item.getOperator();
if (null != _selectItem && _operand != null) {
ColumnType columnType = _selectItem.getExpectedColumnType();
if (columnType != null) {
if (columnType.isTimeBased()) {
// special logic for DB2 based time operands.
StringBuilder sb = new StringBuilder();
sb.append(_selectItem.getSameQueryAlias(true));
final Object operand = FilterItem.appendOperator(sb, _operand, _operator);
if (operand instanceof SelectItem) {
final String selectItemString = ((SelectItem) operand).getSameQueryAlias(true);
sb.append(selectItemString);
} else {
Date date = TimeComparator.toDate(_operand);
if (date == null) {
throw new IllegalStateException("Could not convert " + _operand + " to date");
}
final String sqlValue = FormatHelper.formatSqlTime(columnType, date, true, "('", "')");
sb.append(sqlValue);
}
return sb.toString();
}
}
}
return super.rewriteFilterItem(item);
}
}