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);
38917aa1b7ac799e308e3724f8c4170620c846dcc09Makoto Onuki            db.validateSql(sqlForValidation, cancellationSignal); // will throw if query is invalid
39050b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann        }
39150b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann
39254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String sql = buildQuery(
39384029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger                projectionIn, selection, groupBy, having,
39454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                sortOrder, limit);
39554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
39654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (Log.isLoggable(TAG, Log.DEBUG)) {
39754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            Log.d(TAG, "Performing query: " + sql);
39854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
39954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return db.rawQueryWithFactory(
40054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                mFactory, sql, selectionArgs,
40175ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                SQLiteDatabase.findEditTable(mTables),
4024c1241df8f8b7fd5ec3dff6c7e0f66271248e76eJeff Brown                cancellationSignal); // will throw if query is invalid
40350b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann    }
40450b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann
40550b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann    /**
40654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Construct a SELECT statement suitable for use in a group of
40754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * SELECT statements that will be joined through UNION operators
40854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * in buildUnionQuery.
40954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
41054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param projectionIn A list of which columns to return. Passing
41154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *    null will return all columns, which is discouraged to
41254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *    prevent reading data from storage that isn't going to be
41354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *    used.
41454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param selection A filter declaring which rows to return,
41554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   formatted as an SQL WHERE clause (excluding the WHERE
41654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   itself).  Passing null will return all rows for the given
41754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   URL.
41854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param groupBy A filter declaring how to group rows, formatted
41954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   as an SQL GROUP BY clause (excluding the GROUP BY itself).
42054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   Passing null will cause the rows to not be grouped.
42154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param having A filter declare which row groups to include in
42254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   the cursor, if row grouping is being used, formatted as an
42354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   SQL HAVING clause (excluding the HAVING itself).  Passing
42454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   null will cause all row groups to be included, and is
42554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   required when row grouping is not being used.
42654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param sortOrder How to order the rows, formatted as an SQL
42754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   ORDER BY clause (excluding the ORDER BY itself). Passing null
42854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   will use the default sort order, which may be unordered.
42954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param limit Limits the number of rows returned by the query,
43054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   formatted as LIMIT clause. Passing null denotes no LIMIT clause.
43154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @return the resulting SQL SELECT statement
43254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
43354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public String buildQuery(
43484029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String[] projectionIn, String selection, String groupBy,
43584029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String having, String sortOrder, String limit) {
43654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String[] projection = computeProjection(projectionIn);
43754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
438accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)        StringBuilder where = new StringBuilder();
439ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick        boolean hasBaseWhereClause = mWhereClause != null && mWhereClause.length() > 0;
440accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)
441ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick        if (hasBaseWhereClause) {
442accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)            where.append(mWhereClause.toString());
443accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)            where.append(')');
44454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
44554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
44654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        // Tack on the user's selection, if present.
44754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (selection != null && selection.length() > 0) {
448ae6cdd12ac7ded629971206efde331361604d442Brad Fitzpatrick            if (hasBaseWhereClause) {
449accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)                where.append(" AND ");
45054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
45154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
452accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)            where.append('(');
453accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)            where.append(selection);
454accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)            where.append(')');
45554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
45654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
45754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return buildQueryString(
458accbadeb52eda2d972fa6d9f41ebaf9168dc343aYusuke Ohmichi(maimuzo)                mDistinct, mTables, projection, where.toString(),
45954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                groupBy, having, sortOrder, limit);
46054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
46154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
46254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
46384029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * @deprecated This method's signature is misleading since no SQL parameter
46484029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * substitution is carried out.  The selection arguments parameter does not get
46584029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * used at all.  To avoid confusion, call
46684029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * {@link #buildQuery(String[], String, String, String, String, String)} instead.
46784029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     */
46884029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    @Deprecated
46984029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    public String buildQuery(
47084029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String[] projectionIn, String selection, String[] selectionArgs,
47184029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String groupBy, String having, String sortOrder, String limit) {
47284029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger        return buildQuery(projectionIn, selection, groupBy, having, sortOrder, limit);
47384029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    }
47484029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger
47584029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    /**
47654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Construct a SELECT statement suitable for use in a group of
47754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * SELECT statements that will be joined through UNION operators
47854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * in buildUnionQuery.
47954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
48054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param typeDiscriminatorColumn the name of the result column
48154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   whose cells will contain the name of the table from which
48254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   each row was drawn.
48354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param unionColumns the names of the columns to appear in the
48454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   result.  This may include columns that do not appear in the
48554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   table this SELECT is querying (i.e. mTables), but that do
48654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   appear in one of the other tables in the UNION query that we
48754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   are constructing.
48854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param columnsPresentInTable a Set of the names of the columns
48954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   that appear in this table (i.e. in the table whose name is
49054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   mTables).  Since columns in unionColumns include columns that
49154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   appear only in other tables, we use this array to distinguish
49254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   which ones actually are present.  Other columns will have
49354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   NULL values for results from this subquery.
49454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param computedColumnsOffset all columns in unionColumns before
49554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   this index are included under the assumption that they're
49654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   computed and therefore won't appear in columnsPresentInTable,
49754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   e.g. "date * 1000 as normalized_date"
49854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param typeDiscriminatorValue the value used for the
49954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   type-discriminator column in this subquery
50054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param selection A filter declaring which rows to return,
50154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   formatted as an SQL WHERE clause (excluding the WHERE
50254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   itself).  Passing null will return all rows for the given
50354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   URL.
50454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param groupBy A filter declaring how to group rows, formatted
50554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   as an SQL GROUP BY clause (excluding the GROUP BY itself).
50654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   Passing null will cause the rows to not be grouped.
50754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param having A filter declare which row groups to include in
50854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   the cursor, if row grouping is being used, formatted as an
50954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   SQL HAVING clause (excluding the HAVING itself).  Passing
51054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   null will cause all row groups to be included, and is
51154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   required when row grouping is not being used.
51254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @return the resulting SQL SELECT statement
51354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
51454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public String buildUnionSubQuery(
51554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String typeDiscriminatorColumn,
51654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String[] unionColumns,
51754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            Set<String> columnsPresentInTable,
51854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            int computedColumnsOffset,
51954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String typeDiscriminatorValue,
52054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String selection,
52154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String groupBy,
52254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String having) {
52354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        int unionColumnsCount = unionColumns.length;
52454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String[] projectionIn = new String[unionColumnsCount];
52554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
52654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        for (int i = 0; i < unionColumnsCount; i++) {
52754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String unionColumn = unionColumns[i];
52854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
52954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (unionColumn.equals(typeDiscriminatorColumn)) {
53054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                projectionIn[i] = "'" + typeDiscriminatorValue + "' AS "
53154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                        + typeDiscriminatorColumn;
53254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else if (i <= computedColumnsOffset
53354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                       || columnsPresentInTable.contains(unionColumn)) {
53454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                projectionIn[i] = unionColumn;
53554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else {
53654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                projectionIn[i] = "NULL AS " + unionColumn;
53754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
53854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
53954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return buildQuery(
54084029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger                projectionIn, selection, groupBy, having,
54154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                null /* sortOrder */,
54254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                null /* limit */);
54354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
54454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
54554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    /**
54684029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * @deprecated This method's signature is misleading since no SQL parameter
54784029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * substitution is carried out.  The selection arguments parameter does not get
54884029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * used at all.  To avoid confusion, call
549f4072fcc14ec44072d31d7beeb4524550bead531Jean-Baptiste Queru     * {@link #buildUnionSubQuery}
55084029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     * instead.
55184029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger     */
55284029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    @Deprecated
55384029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    public String buildUnionSubQuery(
55484029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String typeDiscriminatorColumn,
55584029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String[] unionColumns,
55684029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            Set<String> columnsPresentInTable,
55784029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            int computedColumnsOffset,
55884029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String typeDiscriminatorValue,
55984029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String selection,
56084029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String[] selectionArgs,
56184029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String groupBy,
56284029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger            String having) {
56384029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger        return buildUnionSubQuery(
56484029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger                typeDiscriminatorColumn, unionColumns, columnsPresentInTable,
56584029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger                computedColumnsOffset, typeDiscriminatorValue, selection,
56684029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger                groupBy, having);
56784029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    }
56884029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger
56984029037239fec6e54327519c3e5ad30088c28ceJonas Schwertfeger    /**
57054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * Given a set of subqueries, all of which are SELECT statements,
57154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * construct a query that returns the union of what those
57254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * subqueries return.
57354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param subQueries an array of SQL SELECT statements, all of
57454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   which must have the same columns as the same positions in
57554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   their results
57654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param sortOrder How to order the rows, formatted as an SQL
57754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   ORDER BY clause (excluding the ORDER BY itself).  Passing
57854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *   null will use the default sort order, which may be unordered.
57954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @param limit The limit clause, which applies to the entire union result set
58054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     *
58154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     * @return the resulting SQL SELECT statement
58254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project     */
58354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    public String buildUnionQuery(String[] subQueries, String sortOrder, String limit) {
58454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        StringBuilder query = new StringBuilder(128);
58554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        int subQueryCount = subQueries.length;
58654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        String unionOperator = mDistinct ? " UNION " : " UNION ALL ";
58754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
58854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        for (int i = 0; i < subQueryCount; i++) {
58954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (i > 0) {
59054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                query.append(unionOperator);
59154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
59254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            query.append(subQueries[i]);
59354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
59454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        appendClause(query, " ORDER BY ", sortOrder);
59554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        appendClause(query, " LIMIT ", limit);
59654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return query.toString();
59754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
59854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
59954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    private String[] computeProjection(String[] projectionIn) {
60054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        if (projectionIn != null && projectionIn.length > 0) {
60154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            if (mProjectionMap != null) {
60254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                String[] projection = new String[projectionIn.length];
60354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                int length = projectionIn.length;
60454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
60554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                for (int i = 0; i < length; i++) {
60654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    String userColumn = projectionIn[i];
60754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    String column = mProjectionMap.get(userColumn);
60854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
60999c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                    if (column != null) {
61054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                        projection[i] = column;
61199c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                        continue;
61254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    }
61399c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan
61450b1f8d3fc1379339119933e8f567547efb89aa5Daniel Lehmann                    if (!mStrict &&
61540eb4aad2bbf88abe21f5868cc09d49ee1b05bf8Dmitri Plotnikov                            ( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
61699c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                        /* A column alias already exist */
61799c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                        projection[i] = userColumn;
61899c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                        continue;
61999c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                    }
62099c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan
62199c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                    throw new IllegalArgumentException("Invalid column "
62299c4483cd77ff96c5181fda3d6e2fcf2ea50421bMichael Chan                            + projectionIn[i]);
62354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                }
62454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                return projection;
62554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            } else {
62654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                return projectionIn;
62754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
62854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        } else if (mProjectionMap != null) {
62954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            // Return all columns in projection map.
63054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            Set<Entry<String, String>> entrySet = mProjectionMap.entrySet();
63154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            String[] projection = new String[entrySet.size()];
63254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            Iterator<Entry<String, String>> entryIter = entrySet.iterator();
63354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            int i = 0;
63454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
63554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            while (entryIter.hasNext()) {
63654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                Entry<String, String> entry = entryIter.next();
63754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project
63854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                // Don't include the _count column when people ask for no projection.
63954b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                if (entry.getKey().equals(BaseColumns._COUNT)) {
64054b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                    continue;
64154b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                }
64254b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project                projection[i++] = entry.getValue();
64354b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            }
64454b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project            return projection;
64554b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        }
64654b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project        return null;
64754b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project    }
64854b6cfa9a9e5b861a9930af873580d6dc20f773The Android Open Source Project}
649