001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.beanquery.sql;
018
019import org.apache.juneau.commons.beanquery.*;
020
021/**
022 * The SQL-generation strategy for a SQL {@link BeanQueryContext}: it quotes identifiers, renders each built-in
023 * {@code $}-operator into a dialect-correct predicate, and renders pagination.
024 *
025 * <p>
026 * The shared JDBC context (the {@code juneau-beanquery-sql} module) is dialect-agnostic plumbing: it walks a parsed
027 * search tree, delegates every leaf to the dialect, and combines leaves with {@code AND}/{@code OR}/{@code NOT} itself.
028 * All dialect-specific SQL text &mdash; identifier quoting, the built-in operator predicates, the {@code LIMIT}/
029 * {@code OFFSET} clause &mdash; lives here.  A custom operator instead carries its own {@link SearchSqlRenderer}
030 * (attached with {@link org.apache.juneau.commons.beanquery.SearchOperator.Builder#extension(Class, Object) SearchOperator.Builder.extension}); the
031 * context calls that renderer, passing this dialect so it can branch on {@link #id()}.
032 *
033 * <p>
034 * Juneau ships exactly one dialect today (Postgres, in {@code juneau-beanquery-postgres}).  A dialect returns
035 * <jk>null</jk> from {@link #builtinRenderer(String)} for any built-in it cannot express (for example {@code $regex} on a
036 * database without regex support), and the context then rejects the query with
037 * {@link org.apache.juneau.commons.beanquery.BeanQuerySyntaxException.Code#BAD_VALUE BAD_VALUE} rather than emitting SQL that differs from the in-memory
038 * result.
039 *
040 * <h5 class='section'>Example:</h5>
041 * <p class='bjava'>
042 *    SqlDialect <jv>dialect</jv> = PostgresDialect.<jsf>INSTANCE</jsf>;
043 *    SqlFragment <jv>frag</jv> = <jv>dialect</jv>.renderBare(<jv>dialect</jv>.quote(<js>"status"</js>), SearchType.<jsf>ENUM</jsf>, <js>"OPEN"</js>, <jk>false</jk>);
044 *    <jc>// frag.sql() == "lower(\"status\"::text) = lower(?)", frag.binds() == ["OPEN"]</jc>
045 *
046 *    <jc>// A NUMERIC column receives the already-typed value, not the raw token.</jc>
047 *    SqlFragment <jv>age</jv> = <jv>dialect</jv>.renderBare(<jv>dialect</jv>.quote(<js>"age"</js>), SearchType.<jsf>NUMERIC</jsf>, <jk>new</jk> BigDecimal(<js>"30"</js>), <jk>false</jk>);
048 *    <jc>// age.sql() == "\"age\" = ?", age.binds() == [30]</jc>
049 * </p>
050 *
051 * @since 10.0.0
052 */
053public interface SqlDialect {
054
055   /**
056    * The stable lowercase dialect id (for example {@code "postgres"}).
057    *
058    * @return The dialect id, never <jk>null</jk> or blank.
059    */
060   String id();
061
062   /**
063    * Quotes a column or table identifier for this dialect (for example {@code age} &rarr; {@code "age"}).
064    *
065    * @param identifier The raw identifier.  Must not be <jk>null</jk>.
066    * @return The quoted identifier.
067    */
068   String quote(String identifier);
069
070   /**
071    * Renders a bare pattern term (design ยง5.1) &mdash; text with optional {@code *}/{@code ?} wildcards, a version
072    * prefix, or an exact value for the other types &mdash; into a predicate.
073    *
074    * @param columnSql The dialect-quoted column reference.  Must not be <jk>null</jk>.
075    * @param type The column's value type.  Must not be <jk>null</jk>.
076    * @param value The literal's already-typed value ({@link SearchExpression#typedValue()}): a
077    *    {@link java.math.BigDecimal} for {@code NUMERIC}, a {@link Boolean} for {@code BOOLEAN}, an
078    *    {@link java.time.OffsetDateTime}, {@link java.time.LocalDateTime} or {@link java.time.LocalDate} for
079    *    {@code TIMESTAMP}, and the already-{@code $$}-decoded {@link String} for {@code TEXT}, {@code ID}, {@code ENUM}
080    *    and {@code VERSION}.  Must not be <jk>null</jk>.
081    * @param quoted Whether the literal was quoted in the search string; a quoted literal is never given
082    *    wildcard interpretation, even if it contains {@code *} or {@code ?}.
083    * @return The predicate fragment.
084    */
085   SqlFragment renderBare(String columnSql, SearchType type, Object value, boolean quoted);
086
087   /**
088    * The renderer for a built-in leaf operator (for example {@code "$eq"}), or <jk>null</jk> if this dialect cannot
089    * express it.
090    *
091    * <p>
092    * The context calls this only for built-in leaves; it handles the {@code $and}/{@code $or}/{@code $not} combinators
093    * itself.  A <jk>null</jk> return makes the context fail with a clear error rather than emit wrong SQL.
094    *
095    * @param operatorName The built-in operator name, including the leading {@code $}.  Must not be <jk>null</jk>.
096    * @return The renderer, or <jk>null</jk> if unsupported.
097    */
098   SearchSqlRenderer builtinRenderer(String operatorName);
099
100   /**
101    * Whether this dialect orders {@link SearchType#VERSION} columns as dotted integer tuples, like the in-memory engine
102    * (so {@code 9.10} is greater than {@code 9.9}).
103    *
104    * <p>
105    * When <jk>false</jk> (the default), a range or ordering operator ({@code $gt}, {@code $gte}, {@code $lt},
106    * {@code $lte}, {@code $between}) on a {@code VERSION} column is rejected with
107    * {@link org.apache.juneau.commons.beanquery.BeanQuerySyntaxException.Code#BAD_VALUE BAD_VALUE} instead of silently comparing text (where {@code 9.10}
108    * sorts before {@code 9.9}).  Equality, {@code $in}, {@code $ne} and {@code $prefix} are not affected.
109    *
110    * <h5 class='section'>Example:</h5>
111    * <p class='bjava'>
112    *    <jc>// A dialect that compares versions as integer tuples opts in.</jc>
113    *    <ja>@Override</ja> <jk>public boolean</jk> supportsVersionOrdering() { <jk>return true</jk>; }
114    * </p>
115    *
116    * @return <jk>true</jk> if version range comparisons match the in-memory semantics.
117    */
118   default boolean supportsVersionOrdering() {
119      return false;
120   }
121
122   /**
123    * Renders the pagination clause appended to a {@code SELECT} (for example {@code " LIMIT 10 OFFSET 20"}).
124    *
125    * <p>
126    * The limit/offset are inlined as integer literals (never bind values), so the returned text carries no
127    * placeholders.
128    *
129    * @param limit The maximum row count, or <jk>null</jk> for no limit.
130    * @param offset The zero-based start offset, or <jk>null</jk>/{@code 0} for none.
131    * @return The pagination clause (possibly empty, never <jk>null</jk>).
132    */
133   String renderLimitOffset(Long limit, Long offset);
134}