154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project/*
254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project *
454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * you may not use this file except in compliance with the License.
654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * You may obtain a copy of the License at
754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project *
854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project *
1054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * See the License for the specific language governing permissions and
1454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * limitations under the License.
1554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project */
1654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
1754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectpackage android.database.sqlite;
1854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
1954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.database.Cursor;
2054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.database.DatabaseUtils;
21a7771df3696954f0e279407e8894a916a7cb26ccJeff Brownimport android.os.CancellationSignal;
22a7771df3696954f0e279407e8894a916a7cb26ccJeff Brownimport android.os.OperationCanceledException;
2354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.provider.BaseColumns;
2454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.text.TextUtils;
2554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport android.util.Log;
2654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
2754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport java.util.Iterator;
2854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport java.util.Map;
2954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectimport java.util.Map.Entry;
3050b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmannimport java.util.Set;
31ab18d1f46a0501f9a54da1ef08ff4967f4b63b68Owen Linimport java.util.regex.Pattern;
3254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
3354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project/**
3454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * This is a convience class that helps build SQL queries to be sent to
3554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project * {@link SQLiteDatabase} objects.
3654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project */
3754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Projectpublic class SQLiteQueryBuilder
3854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project{
3954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private static final String TAG = "SQLiteQueryBuilder";
40ab18d1f46a0501f9a54da1ef08ff4967f4b63b68Owen Lin    private static final Pattern sLimitPattern =
41ab18d1f46a0501f9a54da1ef08ff4967f4b63b68Owen Lin            Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
4254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
4354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private Map<String, String> mProjectionMap = null;
4454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private String mTables = "";
45ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick    private StringBuilder mWhereClause = null;  // lazily created
4654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private boolean mDistinct;
4754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private SQLiteDatabase.CursorFactory mFactory;
4850b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann    private boolean mStrict;
4954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
5054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public SQLiteQueryBuilder() {
5154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        mDistinct = false;
5254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        mFactory = null;
5354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
5454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
5554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
5654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Mark the query as DISTINCT.
5754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
5854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param distinct if true the query is DISTINCT, otherwise it isn't
5954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
6054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public void setDistinct(boolean distinct) {
6154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        mDistinct = distinct;
6254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
6354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
6454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
6554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Returns the list of tables being queried
6654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
6754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @return the list of tables being queried
6854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
6954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public String getTables() {
7054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return mTables;
7154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
7254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
7354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
7454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Sets the list of tables to query. Multiple tables can be specified to perform a join.
7554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * For example:
7654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   setTables("foo, bar")
7754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   setTables("foo LEFT OUTER JOIN bar ON (foo.id = bar.foo_id)")
7854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
7954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param inTables the list of tables to query on
8054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
8154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public void setTables(String inTables) {
8254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        mTables = inTables;
8354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
8454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
8554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
8654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
8754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * by parenthesis and ANDed with the selection passed to {@link #query}. The final
8854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * WHERE clause looks like:
8954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
9054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
9154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
9254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param inWhere the chunk of text to append to the WHERE clause.
9354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
9454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public void appendWhere(CharSequence inWhere) {
95ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick        if (mWhereClause == null) {
96ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick            mWhereClause = new StringBuilder(inWhere.length() + 16);
97ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick        }
9854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (mWhereClause.length() == 0) {
9954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            mWhereClause.append('(');
10054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
10154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        mWhereClause.append(inWhere);
10254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
10354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
10454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
10554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
10654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * by parenthesis and ANDed with the selection passed to {@link #query}. The final
10754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * WHERE clause looks like:
10854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
10954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
11054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
11154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param inWhere the chunk of text to append to the WHERE clause. it will be escaped
11254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * to avoid SQL injection attacks
11354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
11454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public void appendWhereEscapeString(String inWhere) {
115ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick        if (mWhereClause == null) {
116ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick            mWhereClause = new StringBuilder(inWhere.length() + 16);
117ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick        }
11854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (mWhereClause.length() == 0) {
11954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            mWhereClause.append('(');
12054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
12154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
12254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
12354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
12454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
12554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Sets the projection map for the query.  The projection map maps
12654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * from column names that the caller passes into query to database
12754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * column names. This is useful for renaming columns as well as
12854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * disambiguating column names when doing joins. For example you
12954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * could map "name" to "people.name".  If a projection map is set
13054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * it must contain all column names the user may request, even if
13154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * the key and value are the same.
13254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
13354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param columnMap maps from the user column names to the database column names
13454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
13554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public void setProjectionMap(Map<String, String> columnMap) {
13654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        mProjectionMap = columnMap;
13754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
13854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
13954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
14054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Sets the cursor factory to be used for the query.  You can use
14154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * one factory for all queries on a database but it is normally
14275ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * easier to specify the factory when doing this query.
14375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *
14475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @param factory the factory to use.
14554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
14654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public void setCursorFactory(SQLiteDatabase.CursorFactory factory) {
14754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        mFactory = factory;
14854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
14954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
15054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
15150b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * When set, the selection is verified against malicious arguments.
15250b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * When using this class to create a statement using
15350b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
15450b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * non-numeric limits will raise an exception. If a projection map is specified, fields
15550b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * not in that map will be ignored.
15650b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * If this class is used to execute the statement directly using
15750b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String)}
15850b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * or
15950b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String, String)},
16050b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * additionally also parenthesis escaping selection are caught.
16150b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     *
16250b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * To summarize: To get maximum protection against malicious third party apps (for example
16350b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * content provider consumers), make sure to do the following:
16450b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * <ul>
16550b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * <li>Set this value to true</li>
16650b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * <li>Use a projection map</li>
16750b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * <li>Use one of the query overloads instead of getting the statement as a sql string</li>
16850b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * </ul>
16950b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * By default, this value is false.
17050b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     */
17150b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann    public void setStrict(boolean flag) {
17250b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann        mStrict = flag;
17340eb4aad2bbf88abe21f5868cc09d49ee1b05bf8Dmitri Plotnikov    }
17440eb4aad2bbf88abe21f5868cc09d49ee1b05bf8Dmitri Plotnikov
17540eb4aad2bbf88abe21f5868cc09d49ee1b05bf8Dmitri Plotnikov    /**
17654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Build an SQL query string from the given clauses.
17754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
17854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param distinct true if you want each row to be unique, false otherwise.
17954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param tables The table names to compile the query against.
18054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param columns A list of which columns to return. Passing null will
18154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            return all columns, which is discouraged to prevent reading
18254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            data from storage that isn't going to be used.
18354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param where A filter declaring which rows to return, formatted as an SQL
18454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            WHERE clause (excluding the WHERE itself). Passing null will
18554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            return all rows for the given URL.
18654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param groupBy A filter declaring how to group rows, formatted as an SQL
18754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            GROUP BY clause (excluding the GROUP BY itself). Passing null
18854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            will cause the rows to not be grouped.
18954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param having A filter declare which row groups to include in the cursor,
19054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            if row grouping is being used, formatted as an SQL HAVING
19154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            clause (excluding the HAVING itself). Passing null will cause
19254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            all row groups to be included, and is required when row
19354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            grouping is not being used.
19454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
19554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            (excluding the ORDER BY itself). Passing null will use the
19654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            default sort order, which may be unordered.
19754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param limit Limits the number of rows returned by the query,
19854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *            formatted as LIMIT clause. Passing null denotes no LIMIT clause.
19954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @return the SQL query string
20054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
20154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public static String buildQueryString(
20254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            boolean distinct, String tables, String[] columns, String where,
20354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String groupBy, String having, String orderBy, String limit) {
20454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (TextUtils.isEmpty(groupBy) && !TextUtils.isEmpty(having)) {
20554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            throw new IllegalArgumentException(
20654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    "HAVING clauses are only permitted when using a groupBy clause");
20754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
208ab18d1f46a0501f9a54da1ef08ff4967f4b63b68Owen Lin        if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
209ab18d1f46a0501f9a54da1ef08ff4967f4b63b68Owen Lin            throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
210ab18d1f46a0501f9a54da1ef08ff4967f4b63b68Owen Lin        }
21154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
21254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        StringBuilder query = new StringBuilder(120);
21354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
21454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        query.append("SELECT ");
21554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (distinct) {
21654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            query.append("DISTINCT ");
21754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
21854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (columns != null && columns.length != 0) {
21954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            appendColumns(query, columns);
22054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        } else {
22154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            query.append("* ");
22254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
22354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        query.append("FROM ");
22454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        query.append(tables);
22554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        appendClause(query, " WHERE ", where);
22654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        appendClause(query, " GROUP BY ", groupBy);
22754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        appendClause(query, " HAVING ", having);
22854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        appendClause(query, " ORDER BY ", orderBy);
229ab18d1f46a0501f9a54da1ef08ff4967f4b63b68Owen Lin        appendClause(query, " LIMIT ", limit);
23054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
23154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return query.toString();
23254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
23354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
23454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private static void appendClause(StringBuilder s, String name, String clause) {
23554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (!TextUtils.isEmpty(clause)) {
23654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            s.append(name);
23754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            s.append(clause);
23854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
23954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
24054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
24154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
24254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Add the names that are non-null in columns to s, separating
24354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * them with commas.
24454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
24554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public static void appendColumns(StringBuilder s, String[] columns) {
24654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        int n = columns.length;
24754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
24854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        for (int i = 0; i < n; i++) {
24954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String column = columns[i];
25054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
25154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (column != null) {
25254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                if (i > 0) {
25354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    s.append(", ");
25454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                }
25554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                s.append(column);
25654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
25754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
25854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        s.append(' ');
25954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
26054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
26154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
26254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Perform a query by combining all current settings and the
26354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * information passed into this method.
26454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
26554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param db the database to query on
26654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param projectionIn A list of which columns to return. Passing
26754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   null will return all columns, which is discouraged to prevent
26854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   reading data from storage that isn't going to be used.
26954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param selection A filter declaring which rows to return,
27054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   formatted as an SQL WHERE clause (excluding the WHERE
27154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   itself). Passing null will return all rows for the given URL.
27254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param selectionArgs You may include ?s in selection, which
27354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   will be replaced by the values from selectionArgs, in order
27454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   that they appear in the selection. The values will be bound
27554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   as Strings.
27654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param groupBy A filter declaring how to group rows, formatted
27754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   as an SQL GROUP BY clause (excluding the GROUP BY
27854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   itself). Passing null will cause the rows to not be grouped.
27954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param having A filter declare which row groups to include in
28054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   the cursor, if row grouping is being used, formatted as an
28154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   SQL HAVING clause (excluding the HAVING itself).  Passing
28254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   null will cause all row groups to be included, and is
28354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   required when row grouping is not being used.
28454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param sortOrder How to order the rows, formatted as an SQL
28554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   ORDER BY clause (excluding the ORDER BY itself). Passing null
28654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   will use the default sort order, which may be unordered.
28754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @return a cursor over the result set
28854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @see android.content.ContentResolver#query(android.net.Uri, String[],
28954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *      String, String[], String)
29054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
29154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public Cursor query(SQLiteDatabase db, String[] projectionIn,
29254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String selection, String[] selectionArgs, String groupBy,
29354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String having, String sortOrder) {
29454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return query(db, projectionIn, selection, selectionArgs, groupBy, having, sortOrder,
2954c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                null /* limit */, null /* cancellationSignal */);
29654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
29754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
29854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
29954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Perform a query by combining all current settings and the
30054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * information passed into this method.
30154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
30254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param db the database to query on
30354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param projectionIn A list of which columns to return. Passing
30454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   null will return all columns, which is discouraged to prevent
30554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   reading data from storage that isn't going to be used.
30654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param selection A filter declaring which rows to return,
30754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   formatted as an SQL WHERE clause (excluding the WHERE
30854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   itself). Passing null will return all rows for the given URL.
30954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param selectionArgs You may include ?s in selection, which
31054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   will be replaced by the values from selectionArgs, in order
31154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   that they appear in the selection. The values will be bound
31254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   as Strings.
31354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param groupBy A filter declaring how to group rows, formatted
31454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   as an SQL GROUP BY clause (excluding the GROUP BY
31554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   itself). Passing null will cause the rows to not be grouped.
31654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param having A filter declare which row groups to include in
31754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   the cursor, if row grouping is being used, formatted as an
31854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   SQL HAVING clause (excluding the HAVING itself).  Passing
31954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   null will cause all row groups to be included, and is
32054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   required when row grouping is not being used.
32154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param sortOrder How to order the rows, formatted as an SQL
32254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   ORDER BY clause (excluding the ORDER BY itself). Passing null
32354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   will use the default sort order, which may be unordered.
32454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param limit Limits the number of rows returned by the query,
32554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   formatted as LIMIT clause. Passing null denotes no LIMIT clause.
32654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @return a cursor over the result set
32754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @see android.content.ContentResolver#query(android.net.Uri, String[],
32854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *      String, String[], String)
32954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
33054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public Cursor query(SQLiteDatabase db, String[] projectionIn,
33154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String selection, String[] selectionArgs, String groupBy,
33254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String having, String sortOrder, String limit) {
33375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown        return query(db, projectionIn, selection, selectionArgs,
33475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                groupBy, having, sortOrder, limit, null);
33575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    }
33675ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown
33775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    /**
33875ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * Perform a query by combining all current settings and the
33975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * information passed into this method.
34075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *
34175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @param db the database to query on
34275ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @param projectionIn A list of which columns to return. Passing
34375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   null will return all columns, which is discouraged to prevent
34475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   reading data from storage that isn't going to be used.
34575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @param selection A filter declaring which rows to return,
34675ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   formatted as an SQL WHERE clause (excluding the WHERE
34775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   itself). Passing null will return all rows for the given URL.
34875ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @param selectionArgs You may include ?s in selection, which
34975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   will be replaced by the values from selectionArgs, in order
35075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   that they appear in the selection. The values will be bound
35175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   as Strings.
35275ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @param groupBy A filter declaring how to group rows, formatted
35375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   as an SQL GROUP BY clause (excluding the GROUP BY
35475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   itself). Passing null will cause the rows to not be grouped.
35575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @param having A filter declare which row groups to include in
35675ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   the cursor, if row grouping is being used, formatted as an
35775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   SQL HAVING clause (excluding the HAVING itself).  Passing
35875ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   null will cause all row groups to be included, and is
35975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   required when row grouping is not being used.
36075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @param sortOrder How to order the rows, formatted as an SQL
36175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   ORDER BY clause (excluding the ORDER BY itself). Passing null
36275ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   will use the default sort order, which may be unordered.
36375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @param limit Limits the number of rows returned by the query,
36475ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *   formatted as LIMIT clause. Passing null denotes no LIMIT clause.
3654c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
36675ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * If the operation is canceled, then {@link OperationCanceledException} will be thrown
36775ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * when the query is executed.
36875ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @return a cursor over the result set
36975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     * @see android.content.ContentResolver#query(android.net.Uri, String[],
37075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     *      String, String[], String)
37175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown     */
37275ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    public Cursor query(SQLiteDatabase db, String[] projectionIn,
37375ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown            String selection, String[] selectionArgs, String groupBy,
3744c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            String having, String sortOrder, String limit, CancellationSignal cancellationSignal) {
37554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (mTables == null) {
37654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            return null;
37754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
37854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
37950b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann        if (mStrict && selection != null && selection.length() > 0) {
38050b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann            // Validate the user-supplied selection to detect syntactic anomalies
38150b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann            // in the selection string that could indicate a SQL injection attempt.
38250b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann            // The idea is to ensure that the selection clause is a valid SQL expression
38350b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann            // by compiling it twice: once wrapped in parentheses and once as
38450b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann            // originally specified. An attacker cannot create an expression that
38550b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann            // would escape the SQL expression while maintaining balanced parentheses
38650b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann            // in both the wrapped and original forms.
38750b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann            String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,
38850b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann                    having, sortOrder, limit);
38975ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown            validateQuerySql(db, sqlForValidation,
3904c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                    cancellationSignal); // will throw if query is invalid
39150b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann        }
39250b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann
39354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String sql = buildQuery(
39484029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger                projectionIn, selection, groupBy, having,
39554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                sortOrder, limit);
39654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
39754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (Log.isLoggable(TAG, Log.DEBUG)) {
39854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            Log.d(TAG, "Performing query: " + sql);
39954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
40054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return db.rawQueryWithFactory(
40154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                mFactory, sql, selectionArgs,
40275ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                SQLiteDatabase.findEditTable(mTables),
4034c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                cancellationSignal); // will throw if query is invalid
40450b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann    }
40550b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann
40650b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann    /**
407e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown     * Verifies that a SQL SELECT statement is valid by compiling it.
40850b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     * If the SQL statement is not valid, this method will throw a {@link SQLiteException}.
40950b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann     */
41075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown    private void validateQuerySql(SQLiteDatabase db, String sql,
4114c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown            CancellationSignal cancellationSignal) {
412e5360fbf3efe85427f7e7f59afe7bbeddb4949acJeff Brown        db.getThreadSession().prepare(sql,
4134c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                db.getThreadDefaultConnectionFlags(true /*readOnly*/), cancellationSignal, null);
41454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
41554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
41654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
41754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Construct a SELECT statement suitable for use in a group of
41854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * SELECT statements that will be joined through UNION operators
41954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * in buildUnionQuery.
42054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
42154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param projectionIn A list of which columns to return. Passing
42254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *    null will return all columns, which is discouraged to
42354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *    prevent reading data from storage that isn't going to be
42454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *    used.
42554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param selection A filter declaring which rows to return,
42654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   formatted as an SQL WHERE clause (excluding the WHERE
42754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   itself).  Passing null will return all rows for the given
42854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   URL.
42954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param groupBy A filter declaring how to group rows, formatted
43054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   as an SQL GROUP BY clause (excluding the GROUP BY itself).
43154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   Passing null will cause the rows to not be grouped.
43254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param having A filter declare which row groups to include in
43354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   the cursor, if row grouping is being used, formatted as an
43454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   SQL HAVING clause (excluding the HAVING itself).  Passing
43554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   null will cause all row groups to be included, and is
43654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   required when row grouping is not being used.
43754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param sortOrder How to order the rows, formatted as an SQL
43854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   ORDER BY clause (excluding the ORDER BY itself). Passing null
43954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   will use the default sort order, which may be unordered.
44054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param limit Limits the number of rows returned by the query,
44154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   formatted as LIMIT clause. Passing null denotes no LIMIT clause.
44254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @return the resulting SQL SELECT statement
44354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
44454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public String buildQuery(
44584029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String[] projectionIn, String selection, String groupBy,
44684029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String having, String sortOrder, String limit) {
44754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String[] projection = computeProjection(projectionIn);
44854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
449accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)        StringBuilder where = new StringBuilder();
450ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick        boolean hasBaseWhereClause = mWhereClause != null && mWhereClause.length() > 0;
451accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)
452ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick        if (hasBaseWhereClause) {
453accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)            where.append(mWhereClause.toString());
454accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)            where.append(')');
45554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
45654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
45754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // Tack on the user's selection, if present.
45854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (selection != null && selection.length() > 0) {
459ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick            if (hasBaseWhereClause) {
460accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)                where.append(" AND ");
46154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
46254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
463accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)            where.append('(');
464accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)            where.append(selection);
465accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)            where.append(')');
46654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
46754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
46854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return buildQueryString(
469accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)                mDistinct, mTables, projection, where.toString(),
47054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                groupBy, having, sortOrder, limit);
47154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
47254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
47354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
47484029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * @deprecated This method's signature is misleading since no SQL parameter
47584029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * substitution is carried out.  The selection arguments parameter does not get
47684029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * used at all.  To avoid confusion, call
47784029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * {@link #buildQuery(String[], String, String, String, String, String)} instead.
47884029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     */
47984029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    @Deprecated
48084029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    public String buildQuery(
48184029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String[] projectionIn, String selection, String[] selectionArgs,
48284029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String groupBy, String having, String sortOrder, String limit) {
48384029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger        return buildQuery(projectionIn, selection, groupBy, having, sortOrder, limit);
48484029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    }
48584029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger
48684029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    /**
48754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Construct a SELECT statement suitable for use in a group of
48854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * SELECT statements that will be joined through UNION operators
48954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * in buildUnionQuery.
49054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
49154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param typeDiscriminatorColumn the name of the result column
49254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   whose cells will contain the name of the table from which
49354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   each row was drawn.
49454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param unionColumns the names of the columns to appear in the
49554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   result.  This may include columns that do not appear in the
49654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   table this SELECT is querying (i.e. mTables), but that do
49754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   appear in one of the other tables in the UNION query that we
49854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   are constructing.
49954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param columnsPresentInTable a Set of the names of the columns
50054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   that appear in this table (i.e. in the table whose name is
50154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   mTables).  Since columns in unionColumns include columns that
50254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   appear only in other tables, we use this array to distinguish
50354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   which ones actually are present.  Other columns will have
50454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   NULL values for results from this subquery.
50554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param computedColumnsOffset all columns in unionColumns before
50654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   this index are included under the assumption that they're
50754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   computed and therefore won't appear in columnsPresentInTable,
50854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   e.g. "date * 1000 as normalized_date"
50954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param typeDiscriminatorValue the value used for the
51054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   type-discriminator column in this subquery
51154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param selection A filter declaring which rows to return,
51254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   formatted as an SQL WHERE clause (excluding the WHERE
51354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   itself).  Passing null will return all rows for the given
51454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   URL.
51554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param groupBy A filter declaring how to group rows, formatted
51654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   as an SQL GROUP BY clause (excluding the GROUP BY itself).
51754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   Passing null will cause the rows to not be grouped.
51854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param having A filter declare which row groups to include in
51954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   the cursor, if row grouping is being used, formatted as an
52054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   SQL HAVING clause (excluding the HAVING itself).  Passing
52154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   null will cause all row groups to be included, and is
52254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   required when row grouping is not being used.
52354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @return the resulting SQL SELECT statement
52454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
52554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public String buildUnionSubQuery(
52654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String typeDiscriminatorColumn,
52754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String[] unionColumns,
52854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            Set<String> columnsPresentInTable,
52954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            int computedColumnsOffset,
53054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String typeDiscriminatorValue,
53154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String selection,
53254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String groupBy,
53354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String having) {
53454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        int unionColumnsCount = unionColumns.length;
53554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String[] projectionIn = new String[unionColumnsCount];
53654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
53754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        for (int i = 0; i < unionColumnsCount; i++) {
53854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String unionColumn = unionColumns[i];
53954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
54054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (unionColumn.equals(typeDiscriminatorColumn)) {
54154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                projectionIn[i] = "'" + typeDiscriminatorValue + "' AS "
54254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                        + typeDiscriminatorColumn;
54354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else if (i <= computedColumnsOffset
54454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                       || columnsPresentInTable.contains(unionColumn)) {
54554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                projectionIn[i] = unionColumn;
54654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else {
54754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                projectionIn[i] = "NULL AS " + unionColumn;
54854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
54954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
55054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return buildQuery(
55184029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger                projectionIn, selection, groupBy, having,
55254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                null /* sortOrder */,
55354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                null /* limit */);
55454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
55554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
55654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
55784029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * @deprecated This method's signature is misleading since no SQL parameter
55884029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * substitution is carried out.  The selection arguments parameter does not get
55984029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * used at all.  To avoid confusion, call
560f4072fcc14ec44072d31d7beeb4524550bead531Jean-Baptiste Queru     * {@link #buildUnionSubQuery}
56184029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * instead.
56284029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     */
56384029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    @Deprecated
56484029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    public String buildUnionSubQuery(
56584029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String typeDiscriminatorColumn,
56684029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String[] unionColumns,
56784029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            Set<String> columnsPresentInTable,
56884029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            int computedColumnsOffset,
56984029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String typeDiscriminatorValue,
57084029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String selection,
57184029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String[] selectionArgs,
57284029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String groupBy,
57384029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String having) {
57484029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger        return buildUnionSubQuery(
57584029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger                typeDiscriminatorColumn, unionColumns, columnsPresentInTable,
57684029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger                computedColumnsOffset, typeDiscriminatorValue, selection,
57784029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger                groupBy, having);
57884029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    }
57984029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger
58084029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    /**
58154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Given a set of subqueries, all of which are SELECT statements,
58254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * construct a query that returns the union of what those
58354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * subqueries return.
58454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param subQueries an array of SQL SELECT statements, all of
58554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   which must have the same columns as the same positions in
58654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   their results
58754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param sortOrder How to order the rows, formatted as an SQL
58854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   ORDER BY clause (excluding the ORDER BY itself).  Passing
58954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   null will use the default sort order, which may be unordered.
59054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param limit The limit clause, which applies to the entire union result set
59154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
59254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @return the resulting SQL SELECT statement
59354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
59454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public String buildUnionQuery(String[] subQueries, String sortOrder, String limit) {
59554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        StringBuilder query = new StringBuilder(128);
59654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        int subQueryCount = subQueries.length;
59754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String unionOperator = mDistinct ? " UNION " : " UNION ALL ";
59854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
59954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        for (int i = 0; i < subQueryCount; i++) {
60054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (i > 0) {
60154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                query.append(unionOperator);
60254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
60354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            query.append(subQueries[i]);
60454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
60554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        appendClause(query, " ORDER BY ", sortOrder);
60654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        appendClause(query, " LIMIT ", limit);
60754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return query.toString();
60854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
60954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
61054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private String[] computeProjection(String[] projectionIn) {
61154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (projectionIn != null && projectionIn.length > 0) {
61254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (mProjectionMap != null) {
61354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                String[] projection = new String[projectionIn.length];
61454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                int length = projectionIn.length;
61554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
61654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                for (int i = 0; i < length; i++) {
61754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    String userColumn = projectionIn[i];
61854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    String column = mProjectionMap.get(userColumn);
61954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
62099c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                    if (column != null) {
62154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                        projection[i] = column;
62299c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                        continue;
62354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    }
62499c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan
62550b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann                    if (!mStrict &&
62640eb4aad2bbf88abe21f5868cc09d49ee1b05bf8Dmitri Plotnikov                            ( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
62799c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                        /* A column alias already exist */
62899c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                        projection[i] = userColumn;
62999c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                        continue;
63099c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                    }
63199c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan
63299c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                    throw new IllegalArgumentException("Invalid column "
63399c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                            + projectionIn[i]);
63454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                }
63554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                return projection;
63654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else {
63754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                return projectionIn;
63854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
63954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        } else if (mProjectionMap != null) {
64054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            // Return all columns in projection map.
64154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            Set<Entry<String, String>> entrySet = mProjectionMap.entrySet();
64254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String[] projection = new String[entrySet.size()];
64354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            Iterator<Entry<String, String>> entryIter = entrySet.iterator();
64454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            int i = 0;
64554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
64654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            while (entryIter.hasNext()) {
64754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                Entry<String, String> entry = entryIter.next();
64854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
64954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                // Don't include the _count column when people ask for no projection.
65054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                if (entry.getKey().equals(BaseColumns._COUNT)) {
65154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    continue;
65254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                }
65354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                projection[i++] = entry.getValue();
65454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
65554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            return projection;
65654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
65754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return null;
65854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
65954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project}
660