package org.jooq.impl;
import static java.lang.Boolean.TRUE;
import static org.jooq.RenderContext.CastMode.NEVER;
import static org.jooq.SQLDialect.H2;
import static org.jooq.conf.ParamType.INLINED;
import static org.jooq.impl.DSL.one;
import static org.jooq.impl.DSL.val;
import static org.jooq.impl.DSL.zero;
import static org.jooq.impl.Internal.iadd;
import static org.jooq.impl.Keywords.K_FETCH_FIRST;
import static org.jooq.impl.Keywords.K_FETCH_NEXT;
import static org.jooq.impl.Keywords.K_FIRST;
import static org.jooq.impl.Keywords.K_LIMIT;
import static org.jooq.impl.Keywords.K_OFFSET;
import static org.jooq.impl.Keywords.K_PERCENT;
import static org.jooq.impl.Keywords.K_ROWS;
import static org.jooq.impl.Keywords.K_ROWS_ONLY;
import static org.jooq.impl.Keywords.K_ROWS_WITH_TIES;
import static org.jooq.impl.Keywords.K_SKIP;
import static org.jooq.impl.Keywords.K_START_AT;
import static org.jooq.impl.Keywords.K_TO;
import static org.jooq.impl.Keywords.K_TOP;
import static org.jooq.impl.Keywords.K_WITH_TIES;
import static org.jooq.impl.SQLDataType.BIGINT;
import org.jooq.Context;
import org.jooq.Field;
import org.jooq.Param;
import org.jooq.RenderContext.CastMode;
import org.jooq.conf.ParamType;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.Tools.BooleanDataKey;
import org.jooq.tools.Convert;
final class Limit extends AbstractQueryPart {
private static final long serialVersionUID = 2053741242981425602L;
private static final Param<Integer> ZERO = zero();
private static final Param<Integer> ONE = one();
private static final Param<Integer> MAX = DSL.inline(Integer.MAX_VALUE);
Param<?> numberOfRows;
private Param<?> numberOfRowsOrMax = MAX;
Param<?> offset;
private Param<?> offsetOrZero = ZERO;
private Param<?> offsetPlusOne = ONE;
private boolean rendersParams;
boolean withTies;
boolean percent;
@Override
public final void accept(Context<?> ctx) {
ParamType paramType = ctx.paramType();
CastMode castMode = ctx.castMode();
switch (ctx.dialect()) {
case CUBRID: {
ctx.castMode(NEVER)
.formatSeparator()
.visit(K_LIMIT)
.sql(' ').visit(offsetOrZero)
.sql(", ").visit(numberOfRowsOrMax)
.castMode(castMode);
break;
}
case FIREBIRD:
case H2:
case DERBY: {
if (ctx.family() == H2 && !withTies() && !percent())
acceptDefault(ctx, castMode);
else
acceptStandard(ctx, castMode);
break;
}
case MARIADB:
case MYSQL:
case SQLITE: {
ctx.castMode(NEVER)
.formatSeparator()
.visit(K_LIMIT)
.sql(' ').visit(numberOfRowsOrMax);
if (!offsetZero())
ctx.formatSeparator()
.visit(K_OFFSET)
.sql(' ').visit(offsetOrZero);
ctx.castMode(castMode);
break;
}
case HSQLDB:
case POSTGRES:
default: {
acceptDefault(ctx, castMode);
break;
}
}
}
private final void acceptStandard(Context<?> ctx, CastMode castMode) {
ctx.castMode(NEVER);
if ( !offsetZero())
ctx.formatSeparator()
.visit(K_OFFSET)
.sql(' ').visit(offsetOrZero)
.sql(' ').visit(K_ROWS);
if (!limitZero()) {
ctx.formatSeparator()
.visit(K_FETCH_NEXT).sql(' ').visit(numberOfRows);
if (percent)
ctx.sql(' ').visit(K_PERCENT);
ctx.sql(' ').visit(withTies ? K_ROWS_WITH_TIES : K_ROWS_ONLY);
}
ctx.castMode(castMode);
}
private final void acceptDefault(Context<?> ctx, CastMode castMode) {
ctx.castMode(NEVER);
if (!limitZero())
ctx.formatSeparator()
.visit(K_LIMIT)
.sql(' ').visit(numberOfRows);
if (!offsetZero())
ctx.formatSeparator()
.visit(K_OFFSET)
.sql(' ').visit(offsetOrZero);
ctx.castMode(castMode);
}
final boolean limitZero() {
return numberOfRows == null;
}
final boolean limitOne() {
return !limitZero()
&& !withTies()
&& !percent()
&& Long.valueOf(1L).equals(numberOfRows.getValue());
}
final boolean offsetZero() {
return offset == null;
}
final Field<?> getLowerRownum() {
return offsetOrZero;
}
final Field<?> getUpperRownum() {
return iadd(offsetOrZero, numberOfRowsOrMax);
}
final boolean isApplicable() {
return offset != null || numberOfRows != null;
}
final boolean rendersParams() {
return rendersParams;
}
final void setOffset(Number offset) {
this.offset = val(offset.longValue(), BIGINT);
this.offsetOrZero = this.offset;
this.offsetPlusOne = val(offset.longValue() + 1L, BIGINT);
}
final void setOffset(Param<?> offset) {
this.offset = offset;
this.offsetOrZero = offset;
this.rendersParams = rendersParams |= offset.isInline();
}
final void setNumberOfRows(Number numberOfRows) {
this.numberOfRows = val(numberOfRows.longValue(), SQLDataType.BIGINT);
this.numberOfRowsOrMax = this.numberOfRows;
}
final void setNumberOfRows(Param<?> numberOfRows) {
this.numberOfRows = numberOfRows;
this.numberOfRowsOrMax = numberOfRows;
this.rendersParams |= numberOfRows.isInline();
}
final Long getNumberOfRows() {
return Convert.convert((numberOfRows != null ? numberOfRows : numberOfRowsOrMax).getValue(), long.class);
}
final void setPercent(boolean percent) {
this.percent = percent;
}
final boolean percent() {
return percent;
}
final void setWithTies(boolean withTies) {
this.withTies = withTies;
}
final boolean withTies() {
return withTies;
}
final Limit from(Limit limit) {
if (limit.numberOfRows != null)
if (numberOfRows == null)
this.setNumberOfRows(limit.numberOfRows);
else
this.setNumberOfRows(((Val<?>) limit.numberOfRows).copy(Math.min(getNumberOfRows(), limit.getNumberOfRows())));
if (limit.offset != null)
this.setOffset(limit.offset);
this.setPercent(limit.percent);
this.setWithTies(limit.withTies);
return this;
}
final void clear() {
offset = null;
numberOfRows = null;
withTies = false;
percent = false;
}
}