/** * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * For more information, please refer to <http://unlicense.org/> */ package org.getlantern.querybuilder.query; import org.getlantern.querybuilder.Expression; import org.getlantern.querybuilder.Join; import org.getlantern.querybuilder.OrderBy; import org.getlantern.querybuilder.SQLLang; import org.getlantern.querybuilder.SQLQuery; import org.getlantern.querybuilder.SQLQueryException; import org.getlantern.querybuilder.Selectable; import java.util.ArrayList; import java.util.List; public class SQLSelectQuery implements SQLQuery, Selectable { private final List<InternalQuery> internalQueries = new ArrayList<>(); private InternalQuery currentInternalQuery; private OrderBy orderBy; private Integer limit = null, offset = null; SQLSelectQuery() { initCurrentQuery(); } @Override public String getSQL() { final StringBuilder sb = new StringBuilder(); final int size = internalQueries.size(); for (int i = 0; i < size; i++) { if (i != 0) { sb.append("UNION "); } final InternalQuery query = internalQueries.get(i); sb.append(query.getSQL()); } if (orderBy != null) { sb.append(String.format("ORDER BY %s ", orderBy.getSQL())); } if (limit != null) { sb.append(String.format("LIMIT %s ", limit)); if (offset != null) { sb.append(String.format("OFFSET %s ", offset)); } } return sb.toString(); } private void initCurrentQuery() { currentInternalQuery = new InternalQuery(); internalQueries.add(currentInternalQuery); } void setDistinct(final boolean distinct) { currentInternalQuery.setDistinct(distinct); } void setFrom(final Selectable from) { currentInternalQuery.setFrom(from); } void setGroupBy(final Selectable groupBy) { currentInternalQuery.setGroupBy(groupBy); } void setHaving(final Expression having) { currentInternalQuery.setHaving(having); } void setJoin(final Join join) { currentInternalQuery.setJoin(join); } void setLimit(final int limit) { this.limit = limit; } void setOffset(final int offset) { this.offset = offset; } void setOrderBy(final OrderBy orderBy) { this.orderBy = orderBy; } void setSelect(final Selectable select) { currentInternalQuery.setSelect(select); } void setWhere(final Expression where) { currentInternalQuery.setWhere(where); } void union() { initCurrentQuery(); } public static final class Builder implements IBuilder<SQLSelectQuery> { private final SQLSelectQuery query = new SQLSelectQuery(); private boolean buildCalled; @Override public SQLSelectQuery build() { buildCalled = true; return query; } @Override public String buildSQL() { return build().getSQL(); } public Builder from(final Selectable from) { checkNotBuilt(); query.setFrom(from); return this; } public Builder groupBy(final Selectable groupBy) { checkNotBuilt(); query.setGroupBy(groupBy); return this; } public Builder having(final Expression having) { checkNotBuilt(); query.setHaving(having); return this; } public Builder limit(final int limit) { checkNotBuilt(); query.setLimit(limit); return this; } public Builder join(final Join join) { checkNotBuilt(); query.setJoin(join); return this; } public Builder offset(final int offset) { query.setOffset(offset); return this; } public Builder orderBy(final OrderBy orderBy) { checkNotBuilt(); query.setOrderBy(orderBy); return this; } public Builder select(final boolean distinct, final Selectable select) { checkNotBuilt(); query.setSelect(select); query.setDistinct(distinct); return this; } public Builder select(final Selectable select) { checkNotBuilt(); select(false, select); return this; } public Builder union() { checkNotBuilt(); query.union(); return this; } public Builder where(final Expression where) { checkNotBuilt(); query.setWhere(where); return this; } private void checkNotBuilt() { if (buildCalled) throw new IllegalStateException(); } } private static class InternalQuery implements SQLLang { private boolean distinct; private Selectable select, from, groupBy; private Expression where, having; private Join join; @Override public String getSQL() { if (select == null) throw new SQLQueryException("selectable is null"); final StringBuilder sb = new StringBuilder("SELECT "); if (distinct) { sb.append("DISTINCT "); } sb.append(String.format("%s ", select.getSQL())); if (!(select instanceof SQLSelectQuery) && from == null) throw new SQLQueryException("FROM not specified"); else if (from != null) { if (from instanceof SQLSelectQuery) { sb.append(String.format("FROM (%s) ", from.getSQL())); } else { sb.append(String.format("FROM %s ", from.getSQL())); } } if (join != null) { sb.append(String.format("%s ", join.getSQL())); } if (where != null) { sb.append(String.format("WHERE %s ", where.getSQL())); } if (groupBy != null) { sb.append(String.format("GROUP BY %s ", groupBy.getSQL())); if (having != null) { sb.append(String.format("HAVING %s ", having.getSQL())); } } return sb.toString(); } void setJoin(final Join join) { this.join = join; } void setDistinct(final boolean distinct) { this.distinct = distinct; } void setFrom(final Selectable from) { this.from = from; } void setGroupBy(final Selectable groupBy) { this.groupBy = groupBy; } void setHaving(final Expression having) { this.having = having; } void setSelect(final Selectable select) { this.select = select; } void setWhere(final Expression where) { this.where = where; } } }