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 java.util.*;
020
021import static org.apache.juneau.commons.utils.Shorts.*;
022
023/**
024 * A parameterized SQL predicate fragment: a boolean SQL expression plus the ordered bind values its {@code ?}
025 * placeholders consume.
026 *
027 * <p>
028 * A {@link SearchSqlRenderer} for a custom operator returns one of these; the SQL context splices the {@link #sql()}
029 * into the assembled {@code WHERE} clause and appends the {@link #binds()} to the prepared-statement parameter list in
030 * order.  Values are always bound as parameters (never string-concatenated) so a custom operator cannot open an
031 * injection hole.
032 *
033 * @since 10.0.0
034 */
035public final class SqlFragment {
036
037   private final String sql;
038   private final List<Object> binds;
039
040   private SqlFragment(String sql, List<Object> binds) {
041      this.sql = sql;
042      this.binds = binds;
043   }
044
045   /**
046    * Creates a fragment.
047    *
048    * @param sql The boolean SQL expression, with {@code ?} placeholders for each bind value.  Must not be
049    *    <jk>null</jk> or blank.
050    * @param binds The bind values, in placeholder order.  May be empty; must not be <jk>null</jk>.
051    * @return A new fragment.
052    */
053   public static SqlFragment of(String sql, List<Object> binds) {
054      req(inb(sql), "SqlFragment sql must not be null or blank.");
055      req(binds != null, "SqlFragment binds must not be null (use an empty list).");
056      return new SqlFragment(sql, List.copyOf(binds));
057   }
058
059   /**
060    * Creates a fragment from a varargs bind list.
061    *
062    * @param sql The boolean SQL expression, with {@code ?} placeholders for each bind value.  Must not be
063    *    <jk>null</jk> or blank.
064    * @param binds The bind values, in placeholder order.
065    * @return A new fragment.
066    */
067   public static SqlFragment of(String sql, Object...binds) {
068      return of(sql, Arrays.asList(binds));
069   }
070
071   /**
072    * The boolean SQL expression.
073    *
074    * @return The SQL, never <jk>null</jk> or blank.
075    */
076   public String sql() {
077      return sql;
078   }
079
080   /**
081    * The bind values in placeholder order.
082    *
083    * @return An unmodifiable list of bind values, never <jk>null</jk>.
084    */
085   public List<Object> binds() {
086      return binds;
087   }
088
089   @Override /* Object */
090   public String toString() {
091      return sql + " " + binds;
092   }
093}