Home | History | Annotate | Download | only in db
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.sqlite.db;
     18 
     19 import java.util.regex.Pattern;
     20 
     21 /**
     22  * A simple query builder to create SQL SELECT queries.
     23  */
     24 @SuppressWarnings("unused")
     25 public final class SupportSQLiteQueryBuilder {
     26     private static final Pattern sLimitPattern =
     27             Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
     28 
     29     private boolean mDistinct = false;
     30     private final String mTable;
     31     private String[] mColumns = null;
     32     private String mSelection;
     33     private Object[] mBindArgs;
     34     private String mGroupBy = null;
     35     private String mHaving = null;
     36     private String mOrderBy = null;
     37     private String mLimit = null;
     38 
     39     /**
     40      * Creates a query for the given table name.
     41      *
     42      * @param tableName The table name(s) to query.
     43      *
     44      * @return A builder to create a query.
     45      */
     46     public static SupportSQLiteQueryBuilder builder(String tableName) {
     47         return new SupportSQLiteQueryBuilder(tableName);
     48     }
     49 
     50     private SupportSQLiteQueryBuilder(String table) {
     51         mTable = table;
     52     }
     53 
     54     /**
     55      * Adds DISTINCT keyword to the query.
     56      *
     57      * @return this
     58      */
     59     public SupportSQLiteQueryBuilder distinct() {
     60         mDistinct = true;
     61         return this;
     62     }
     63 
     64     /**
     65      * Sets the given list of columns as the columns that will be returned.
     66      *
     67      * @param columns The list of column names that should be returned.
     68      *
     69      * @return this
     70      */
     71     public SupportSQLiteQueryBuilder columns(String[] columns) {
     72         mColumns = columns;
     73         return this;
     74     }
     75 
     76     /**
     77      * Sets the arguments for the WHERE clause.
     78      *
     79      * @param selection The list of selection columns
     80      * @param bindArgs The list of bind arguments to match against these columns
     81      *
     82      * @return this
     83      */
     84     public SupportSQLiteQueryBuilder selection(String selection, Object[] bindArgs) {
     85         mSelection = selection;
     86         mBindArgs = bindArgs;
     87         return this;
     88     }
     89 
     90     /**
     91      * Adds a GROUP BY statement.
     92      *
     93      * @param groupBy The value of the GROUP BY statement.
     94      *
     95      * @return this
     96      */
     97     @SuppressWarnings("WeakerAccess")
     98     public SupportSQLiteQueryBuilder groupBy(String groupBy) {
     99         mGroupBy = groupBy;
    100         return this;
    101     }
    102 
    103     /**
    104      * Adds a HAVING statement. You must also provide {@link #groupBy(String)} for this to work.
    105      *
    106      * @param having The having clause.
    107      *
    108      * @return this
    109      */
    110     public SupportSQLiteQueryBuilder having(String having) {
    111         mHaving = having;
    112         return this;
    113     }
    114 
    115     /**
    116      * Adds an ORDER BY statement.
    117      *
    118      * @param orderBy The order clause.
    119      *
    120      * @return this
    121      */
    122     public SupportSQLiteQueryBuilder orderBy(String orderBy) {
    123         mOrderBy = orderBy;
    124         return this;
    125     }
    126 
    127     /**
    128      * Adds a LIMIT statement.
    129      *
    130      * @param limit The limit value.
    131      *
    132      * @return this
    133      */
    134     public SupportSQLiteQueryBuilder limit(String limit) {
    135         if (!isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
    136             throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
    137         }
    138         mLimit = limit;
    139         return this;
    140     }
    141 
    142     /**
    143      * Creates the {@link SupportSQLiteQuery} that can be passed into
    144      * {@link SupportSQLiteDatabase#query(SupportSQLiteQuery)}.
    145      *
    146      * @return a new query
    147      */
    148     public SupportSQLiteQuery create() {
    149         if (isEmpty(mGroupBy) && !isEmpty(mHaving)) {
    150             throw new IllegalArgumentException(
    151                     "HAVING clauses are only permitted when using a groupBy clause");
    152         }
    153         StringBuilder query = new StringBuilder(120);
    154 
    155         query.append("SELECT ");
    156         if (mDistinct) {
    157             query.append("DISTINCT ");
    158         }
    159         if (mColumns != null && mColumns.length != 0) {
    160             appendColumns(query, mColumns);
    161         } else {
    162             query.append(" * ");
    163         }
    164         query.append(" FROM ");
    165         query.append(mTable);
    166         appendClause(query, " WHERE ", mSelection);
    167         appendClause(query, " GROUP BY ", mGroupBy);
    168         appendClause(query, " HAVING ", mHaving);
    169         appendClause(query, " ORDER BY ", mOrderBy);
    170         appendClause(query, " LIMIT ", mLimit);
    171 
    172         return new SimpleSQLiteQuery(query.toString(), mBindArgs);
    173     }
    174 
    175     private static void appendClause(StringBuilder s, String name, String clause) {
    176         if (!isEmpty(clause)) {
    177             s.append(name);
    178             s.append(clause);
    179         }
    180     }
    181 
    182     /**
    183      * Add the names that are non-null in columns to s, separating
    184      * them with commas.
    185      */
    186     private static void appendColumns(StringBuilder s, String[] columns) {
    187         int n = columns.length;
    188 
    189         for (int i = 0; i < n; i++) {
    190             String column = columns[i];
    191             if (i > 0) {
    192                 s.append(", ");
    193             }
    194             s.append(column);
    195         }
    196         s.append(' ');
    197     }
    198 
    199     private static boolean isEmpty(String input) {
    200         return input == null || input.length() == 0;
    201     }
    202 }
    203