/*
* Copyright 2014 - 2017 Blazebit.
*
* 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 com.blazebit.persistence.impl.function.limit;
import com.blazebit.persistence.spi.DbmsDialect;
import com.blazebit.persistence.spi.DbmsStatementType;
import com.blazebit.persistence.spi.FunctionRenderContext;
import com.blazebit.persistence.spi.JpqlFunction;
/**
*
* @author Christian Beikov
* @since 1.0.1
*/
public class LimitFunction implements JpqlFunction {
protected final DbmsDialect dbmsDialect;
protected final boolean limitIncludesOffset;
public LimitFunction(DbmsDialect dbmsDialect) {
// LIMIT(SUBQUERY, LIMIT, OFFSET)
this.dbmsDialect = dbmsDialect;
this.limitIncludesOffset = dbmsDialect.createLimitHandler().limitIncludesOffset();
}
@Override
public boolean hasArguments() {
return true;
}
@Override
public boolean hasParenthesesIfNoArguments() {
return true;
}
@Override
public Class<?> getReturnType(Class<?> firstArgumentType) {
return firstArgumentType;
}
@Override
public void render(FunctionRenderContext functionRenderContext) {
switch (functionRenderContext.getArgumentsSize()) {
case 3:
if (isNotNull(functionRenderContext.getArgument(1)) && isNotNull(functionRenderContext.getArgument(2))) {
renderLimitOffset(functionRenderContext);
return;
}
break;
case 2:
if (isNotNull(functionRenderContext.getArgument(1))) {
renderLimitOnly(functionRenderContext);
return;
}
break;
default:
break;
}
throw new RuntimeException("The limit function needs two or three non null arguments <sub_query>, <limit> and optionally <offset>! args="
+ functionRenderContext);
}
protected void renderLimitOffset(FunctionRenderContext functionRenderContext) {
StringBuilder sqlSb = getSql(functionRenderContext);
if (limitIncludesOffset) {
// Careful, parameters are not supported in this case as that would require parameter rewriting or something like that
String limit = functionRenderContext.getArgument(1);
String offset = functionRenderContext.getArgument(2);
if (limit.contains("?") || offset.contains("?")) {
throw new IllegalArgumentException("Limit and offset in subquery can not be a parameter!");
}
Integer limitValue = Integer.parseInt(limit);
Integer offsetValue = Integer.parseInt(offset);
dbmsDialect.appendExtendedSql(sqlSb, DbmsStatementType.SELECT, true, false, null, Integer.toString(limitValue + offsetValue), offset, null, null);
} else {
dbmsDialect.appendExtendedSql(sqlSb, DbmsStatementType.SELECT, true, false, null, functionRenderContext.getArgument(1), functionRenderContext.getArgument(2), null, null);
}
functionRenderContext.addChunk(sqlSb.toString());
}
protected void renderLimitOnly(FunctionRenderContext functionRenderContext) {
StringBuilder sqlSb = getSql(functionRenderContext);
dbmsDialect.appendExtendedSql(sqlSb, DbmsStatementType.SELECT, true, false, null, functionRenderContext.getArgument(1), null, null, null);
functionRenderContext.addChunk(sqlSb.toString());
}
private static boolean isNotNull(String argument) {
return argument != null && !"NULL".equalsIgnoreCase(argument);
}
private static StringBuilder getSql(FunctionRenderContext functionRenderContext) {
String subquery = functionRenderContext.getArgument(0);
if (startsWithIgnoreCase(subquery, "(select")) {
int endIndex = subquery.length() - (subquery.charAt(subquery.length() - 1) == ')' ? 1 : 0);
return new StringBuilder(subquery.length() - 2).append(subquery, 1, endIndex);
}
return new StringBuilder(subquery);
}
private static boolean startsWithIgnoreCase(String s1, String s2) {
return s1.regionMatches(true, 0, s2, 0, s2.length());
}
}