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}