/*
 * 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.
 *
 * Other licenses:
 * -----------------------------------------------------------------------------
 * Commercial licenses for this work are available. These replace the above
 * ASL 2.0 and offer limited warranties, support, maintenance, and commercial
 * database integrations.
 *
 * For more information, please visit: http://www.jooq.org/licenses
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package org.jooq.impl;

import static org.jooq.Clause.CONDITION;
import static org.jooq.Clause.CONDITION_COMPARISON;
import static org.jooq.Comparator.EQUALS;
import static org.jooq.Comparator.GREATER;
import static org.jooq.Comparator.GREATER_OR_EQUAL;
import static org.jooq.Comparator.IN;
import static org.jooq.Comparator.LESS;
import static org.jooq.Comparator.LESS_OR_EQUAL;
import static org.jooq.Comparator.NOT_EQUALS;
import static org.jooq.Comparator.NOT_IN;
// ...
// ...
// ...
// ...
// ...
import static org.jooq.SQLDialect.DERBY;
import static org.jooq.SQLDialect.FIREBIRD;
import static org.jooq.SQLDialect.H2;
import static org.jooq.SQLDialect.HSQLDB;
// ...
import static org.jooq.SQLDialect.MARIADB;
// ...
import static org.jooq.SQLDialect.MYSQL;
// ...
import static org.jooq.SQLDialect.POSTGRES;
// ...
// ...
import static org.jooq.SQLDialect.SQLITE;
// ...
import static org.jooq.impl.DSL.exists;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.noCondition;
import static org.jooq.impl.DSL.notExists;
import static org.jooq.impl.DSL.row;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.Quantifier.ALL;
import static org.jooq.impl.Tools.embeddedFieldsRow;
import static org.jooq.impl.Tools.fieldNames;
import static org.jooq.impl.Tools.fieldsByName;
import static org.jooq.impl.Tools.visitSubquery;

import java.util.Set;

import org.jooq.Clause;
import org.jooq.Comparator;
import org.jooq.Context;
import org.jooq.Name;
// ...
import org.jooq.QuantifiedSelect;
import org.jooq.QueryPartInternal;
import org.jooq.Record;
import org.jooq.RenderContext;
import org.jooq.Row;
import org.jooq.SQLDialect;
import org.jooq.Select;
import org.jooq.SelectOrderByStep;
import org.jooq.impl.Tools.BooleanDataKey;

Author:Lukas Eder
/** * @author Lukas Eder */
final class RowSubqueryCondition extends AbstractCondition {
Generated UID
/** * Generated UID */
private static final long serialVersionUID = -1806139685201770706L; private static final Clause[] CLAUSES = { CONDITION, CONDITION_COMPARISON }; private static final Set<SQLDialect> SUPPORT_NATIVE = SQLDialect.supportedBy(H2, HSQLDB, MARIADB, MYSQL, POSTGRES, SQLITE); private static final Set<SQLDialect> NO_SUPPORT_NATIVE_QUANTIFIED = SQLDialect.supportedBy(DERBY, FIREBIRD, MARIADB, MYSQL, SQLITE); private final Row left; private final Select<?> right; private final QuantifiedSelect<?> rightQuantified; private final Comparator comparator; RowSubqueryCondition(Row left, Select<?> right, Comparator comparator) { this.left = left; this.right = right; this.rightQuantified = null; this.comparator = comparator; } RowSubqueryCondition(Row left, QuantifiedSelect<?> right, Comparator comparator) { this.left = left; this.right = null; this.rightQuantified = right; this.comparator = comparator; } @Override public final void accept(Context<?> ctx) { ctx.visit(delegate(ctx)); } @Override // Avoid AbstractCondition implementation public final Clause[] clauses(Context<?> ctx) { return null; } private final QueryPartInternal delegate(Context<?> ctx) { // [#3505] TODO: Emulate this where it is not supported if (rightQuantified != null) { if (NO_SUPPORT_NATIVE_QUANTIFIED.contains(ctx.dialect())) { // TODO: Handle all cases, not just the query one QuantifiedSelectImpl<?> q = (QuantifiedSelectImpl<?>) rightQuantified; switch (comparator) { case EQUALS: case NOT_EQUALS: { if (comparator == NOT_EQUALS ^ q.quantifier == ALL) return emulationUsingExists(ctx, left, q.query, comparator == EQUALS ? NOT_EQUALS : EQUALS, comparator == EQUALS); else return new RowSubqueryCondition(left, q.query, comparator == EQUALS ? IN : NOT_IN); } case GREATER: case GREATER_OR_EQUAL: case LESS: case LESS_OR_EQUAL: default: return emulationUsingExists(ctx, left, q.query, q.quantifier == ALL ? comparator.inverse() : comparator, q.quantifier == ALL); } } else return new Native(); } // [#2395] These dialects have full native support for comparison // predicates with row value expressions and subqueries: else if (SUPPORT_NATIVE.contains(ctx.dialect())) return new Native(); // [#2395] All other configurations have to be emulated else return emulationUsingExists(ctx, left, right, comparator == GREATER || comparator == GREATER_OR_EQUAL || comparator == LESS || comparator == LESS_OR_EQUAL ? comparator : EQUALS, comparator == NOT_IN || comparator == NOT_EQUALS ); } private static final QueryPartInternal emulationUsingExists(Context<?> ctx, Row row, Select<?> select, Comparator comparator, boolean notExists) { Select<Record> subselect = emulatedSubselect(ctx, row, select, comparator); return (QueryPartInternal) (notExists ? notExists(subselect) : exists(subselect)); } private static final SelectOrderByStep<Record> emulatedSubselect(Context<?> ctx, Row row, Select<?> s, Comparator c) { RenderContext render = ctx instanceof RenderContext ? (RenderContext) ctx : null; Row l = embeddedFieldsRow(row); Name table = name(render == null ? "t" : render.nextAlias()); Name[] names = fieldNames(l.size()); return select() .from(new AliasedSelect<>(s, names).as(table)) .where(c == null ? noCondition() : new RowCondition(l, row(fieldsByName(table, names)), c)); } private class Native extends AbstractCondition {
Generated UID
/** * Generated UID */
private static final long serialVersionUID = -1552476981094856727L; @Override public final void accept(Context<?> ctx) { ctx.visit(left) .sql(' ') .visit(comparator.toKeyword()) .sql(' '); if (rightQuantified == null) { // Some databases need extra parentheses around the RHS boolean extraParentheses = false ; ctx.sql(extraParentheses ? "((" : "("); visitSubquery(ctx, right); ctx.sql(extraParentheses ? "))" : ")"); } // [#2054] Quantified row value expression comparison predicates shouldn't have parentheses before ANY or ALL else { ctx.subquery(true) .visit(rightQuantified) .subquery(false); } } @Override public final Clause[] clauses(Context<?> ctx) { return CLAUSES; } } }