1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/*
2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project
3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License");
5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License.
6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at
7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *      http://www.apache.org/licenses/LICENSE-2.0
9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software
11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS,
12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and
14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License.
15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.datamodel;
18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.ContentValues;
20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context;
21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.Cursor;
22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.DatabaseUtils;
23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.sqlite.SQLiteDatabase;
24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.sqlite.SQLiteFullException;
25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.sqlite.SQLiteQueryBuilder;
26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.sqlite.SQLiteStatement;
27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.util.SparseArray;
28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.Factory;
30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.R;
31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert;
32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.BugleGservicesKeys;
33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.DebugUtils;
34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil;
35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.UiUtils;
36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Locale;
38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.Stack;
39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.regex.Pattern;
40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class DatabaseWrapper {
42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String TAG = LogUtil.BUGLE_DATABASE_TAG;
43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final SQLiteDatabase mDatabase;
45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final Context mContext;
46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final boolean mLog;
47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Set mExplainQueryPlanRegexp (via {@link BugleGservicesKeys#EXPLAIN_QUERY_PLAN_REGEXP}
49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * to regex matching queries to see query plans. For example, ".*" to show all query plans.
50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // See
52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final String mExplainQueryPlanRegexp;
53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final int sTimingThreshold = 50;        // in milliseconds
54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final int INDEX_INSERT_MESSAGE_PART = 0;
56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final int INDEX_INSERT_MESSAGE = 1;
57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final int INDEX_QUERY_CONVERSATIONS_LATEST_MESSAGE = 2;
58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final int INDEX_QUERY_MESSAGES_LATEST_MESSAGE = 3;
59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final SparseArray<SQLiteStatement> mCompiledStatements;
61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    static class TransactionData {
63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long time;
64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        boolean transactionSuccessful;
65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // track transaction on a per thread basis
68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static ThreadLocal<Stack<TransactionData>> sTransactionDepth =
69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            new ThreadLocal<Stack<TransactionData>>() {
70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @Override
71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public Stack<TransactionData> initialValue() {
72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return new Stack<TransactionData>();
73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    };
75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static String[] sFormatStrings = new String[] {
77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        "took %d ms to %s",
78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        "   took %d ms to %s",
79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        "      took %d ms to %s",
80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    };
81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    DatabaseWrapper(final Context context, final SQLiteDatabase db) {
83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mLog = LogUtil.isLoggable(LogUtil.BUGLE_DATABASE_PERF_TAG, LogUtil.VERBOSE);
84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mExplainQueryPlanRegexp = Factory.get().getBugleGservices().getString(
85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                BugleGservicesKeys.EXPLAIN_QUERY_PLAN_REGEXP, null);
86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mDatabase = db;
87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mContext = context;
88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mCompiledStatements = new SparseArray<SQLiteStatement>();
89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public SQLiteStatement getStatementInTransaction(final int index, final String statement) {
92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Use transaction to serialize access to statements
93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Assert.isTrue(mDatabase.inTransaction());
94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        SQLiteStatement compiled = mCompiledStatements.get(index);
95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (compiled == null) {
96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            compiled = mDatabase.compileStatement(statement);
97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Assert.isTrue(compiled.toString().contains(statement.trim()));
98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mCompiledStatements.put(index, compiled);
99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return compiled;
101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void maybePlayDebugNoise() {
104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        DebugUtils.maybePlayDebugNoise(mContext, DebugUtils.DEBUG_SOUND_DB_OP);
105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static void printTiming(final long t1, final String msg) {
108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int transactionDepth = sTransactionDepth.get().size();
109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final long t2 = System.currentTimeMillis();
110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final long delta = t2 - t1;
111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (delta > sTimingThreshold) {
112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.v(LogUtil.BUGLE_DATABASE_PERF_TAG, String.format(Locale.US,
113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    sFormatStrings[Math.min(sFormatStrings.length - 1, transactionDepth)],
114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    delta,
115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    msg));
116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public Context getContext() {
120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return mContext;
121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void beginTransaction() {
124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final long t1 = System.currentTimeMillis();
125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // push the current time onto the transaction stack
127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final TransactionData f = new TransactionData();
128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        f.time = t1;
129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        sTransactionDepth.get().push(f);
130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mDatabase.beginTransaction();
132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void setTransactionSuccessful() {
135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final TransactionData f = sTransactionDepth.get().peek();
136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        f.transactionSuccessful = true;
137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mDatabase.setTransactionSuccessful();
138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void endTransaction() {
141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long transactionStartTime = 0;
143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final TransactionData f = sTransactionDepth.get().pop();
144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (f.transactionSuccessful == false) {
145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.w(TAG, "endTransaction without setting successful");
146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            for (final StackTraceElement st : (new Exception()).getStackTrace()) {
147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                LogUtil.w(TAG, "    " + st.toString());
148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            transactionStartTime = f.time;
152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mDatabase.endTransaction();
156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (SQLiteFullException ex) {
157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(TAG, "Database full, unable to endTransaction", ex);
158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            UiUtils.showToastAtBottom(R.string.db_full);
159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(t1, String.format(Locale.US,
162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    ">>> endTransaction (total for this transaction: %d)",
163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    (System.currentTimeMillis() - transactionStartTime)));
164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void yieldTransaction() {
168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long yieldStartTime = 0;
169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            yieldStartTime = System.currentTimeMillis();
171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final boolean wasYielded = mDatabase.yieldIfContendedSafely();
173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (wasYielded && mLog) {
174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(yieldStartTime, "yieldTransaction");
175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void insertWithOnConflict(final String searchTable, final String nullColumnHack,
179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final ContentValues initialValues, final int conflictAlgorithm) {
180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mDatabase.insertWithOnConflict(searchTable, nullColumnHack, initialValues,
186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    conflictAlgorithm);
187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (SQLiteFullException ex) {
188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(TAG, "Database full, unable to insertWithOnConflict", ex);
189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            UiUtils.showToastAtBottom(R.string.db_full);
190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(t1, String.format(Locale.US,
193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    "insertWithOnConflict with ", searchTable));
194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void explainQueryPlan(final SQLiteQueryBuilder qb, final SQLiteDatabase db,
198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String[] projection, final String selection,
199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            @SuppressWarnings("unused")
200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    final String[] queryArgs,
201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String groupBy,
202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            @SuppressWarnings("unused")
203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    final String having,
204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String sortOrder, final String limit) {
205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final String queryString = qb.buildQuery(
206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                projection,
207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                selection,
208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                groupBy,
209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                null/*having*/,
210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                sortOrder,
211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                limit);
212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        explainQueryPlan(db, queryString, queryArgs);
213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void explainQueryPlan(final SQLiteDatabase db, final String sql,
216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String[] queryArgs) {
217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (!Pattern.matches(mExplainQueryPlanRegexp, sql)) {
218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Cursor planCursor = db.rawQuery("explain query plan " + sql, queryArgs);
221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (planCursor != null && planCursor.moveToFirst()) {
223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final int detailColumn = planCursor.getColumnIndex("detail");
224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final StringBuilder sb = new StringBuilder();
225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                do {
226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    sb.append(planCursor.getString(detailColumn));
227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    sb.append("\n");
228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                } while (planCursor.moveToNext());
229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (sb.length() > 0) {
230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    sb.setLength(sb.length() - 1);
231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                LogUtil.v(TAG, "for query " + sql + "\nplan is: "
233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        + sb.toString());
234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final Exception e) {
236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.w(TAG, "Query plan failed ", e);
237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } finally {
238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (planCursor != null) {
239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                planCursor.close();
240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public Cursor query(final String searchTable, final String[] projection,
245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String selection, final String[] selectionArgs, final String groupBy,
246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String having, final String orderBy, final String limit) {
247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mExplainQueryPlanRegexp != null) {
248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            qb.setTables(searchTable);
250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            explainQueryPlan(qb, mDatabase, projection, selection, selectionArgs,
251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    groupBy, having, orderBy, limit);
252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        maybePlayDebugNoise();
255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Cursor cursor = mDatabase.query(searchTable, projection, selection, selectionArgs,
260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                groupBy, having, orderBy, limit);
261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(
263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    t1,
264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    String.format(Locale.US, "query %s with %s ==> %d",
265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            searchTable, selection, cursor.getCount()));
266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return cursor;
268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public Cursor query(final String searchTable, final String[] columns,
271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String selection, final String[] selectionArgs, final String groupBy,
272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String having, final String orderBy) {
273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return query(
274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                searchTable, columns, selection, selectionArgs,
275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                groupBy, having, orderBy, null);
276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public Cursor query(final SQLiteQueryBuilder qb,
279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String[] projection, final String selection, final String[] queryArgs,
280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String groupBy, final String having, final String sortOrder, final String limit) {
281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mExplainQueryPlanRegexp != null) {
282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            explainQueryPlan(qb, mDatabase, projection, selection, queryArgs,
283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    groupBy, having, sortOrder, limit);
284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        maybePlayDebugNoise();
286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Cursor cursor = qb.query(mDatabase, projection, selection, queryArgs, groupBy,
291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                having, sortOrder, limit);
292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(
294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    t1,
295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    String.format(Locale.US, "query %s with %s ==> %d",
296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            qb.getTables(), selection, cursor.getCount()));
297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return cursor;
299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public long queryNumEntries(final String table, final String selection,
302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String[] selectionArgs) {
303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        maybePlayDebugNoise();
308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final long retval =
309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                DatabaseUtils.queryNumEntries(mDatabase, table, selection, selectionArgs);
310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog){
311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(
312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    t1,
313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    String.format(Locale.US, "queryNumEntries %s with %s ==> %d", table,
314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            selection, retval));
315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return retval;
317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public Cursor rawQuery(final String sql, final String[] args) {
320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mExplainQueryPlanRegexp != null) {
321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            explainQueryPlan(mDatabase, sql, args);
322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        maybePlayDebugNoise();
328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Cursor cursor = mDatabase.rawQuery(sql, args);
329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(
331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    t1,
332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    String.format(Locale.US, "rawQuery %s ==> %d", sql, cursor.getCount()));
333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return cursor;
335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public int update(final String table, final ContentValues values,
338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String selection, final String[] selectionArgs) {
339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        maybePlayDebugNoise();
344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int count = 0;
345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            count = mDatabase.update(table, values, selection, selectionArgs);
347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (SQLiteFullException ex) {
348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(TAG, "Database full, unable to update", ex);
349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            UiUtils.showToastAtBottom(R.string.db_full);
350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(t1, String.format(Locale.US, "update %s with %s ==> %d",
353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    table, selection, count));
354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return count;
356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public int delete(final String table, final String whereClause, final String[] whereArgs) {
359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        maybePlayDebugNoise();
364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int count = 0;
365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            count = mDatabase.delete(table, whereClause, whereArgs);
367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (SQLiteFullException ex) {
368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(TAG, "Database full, unable to delete", ex);
369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            UiUtils.showToastAtBottom(R.string.db_full);
370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(t1,
373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    String.format(Locale.US, "delete from %s with %s ==> %d", table,
374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            whereClause, count));
375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return count;
377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public long insert(final String table, final String nullColumnHack,
380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final ContentValues values) {
381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        maybePlayDebugNoise();
386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long rowId = -1;
387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            rowId = mDatabase.insert(table, nullColumnHack, values);
389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (SQLiteFullException ex) {
390d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(TAG, "Database full, unable to insert", ex);
391d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            UiUtils.showToastAtBottom(R.string.db_full);
392d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
393d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
394d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(t1, String.format(Locale.US, "insert to %s", table));
395d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
396d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return rowId;
397d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
398d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
399d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public long replace(final String table, final String nullColumnHack,
400d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final ContentValues values) {
401d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
402d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
403d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
404d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
405d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        maybePlayDebugNoise();
406d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long rowId = -1;
407d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
408d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            rowId = mDatabase.replace(table, nullColumnHack, values);
409d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (SQLiteFullException ex) {
410d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(TAG, "Database full, unable to replace", ex);
411d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            UiUtils.showToastAtBottom(R.string.db_full);
412d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
413d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
414d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(t1, String.format(Locale.US, "replace to %s", table));
415d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
416d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return rowId;
417d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
418d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
419d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void setLocale(final Locale locale) {
420d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mDatabase.setLocale(locale);
421d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
422d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
423d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void execSQL(final String sql, final String[] bindArgs) {
424d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
425d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
426d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
427d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
428d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        maybePlayDebugNoise();
429d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
430d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mDatabase.execSQL(sql, bindArgs);
431d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (SQLiteFullException ex) {
432d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(TAG, "Database full, unable to execSQL", ex);
433d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            UiUtils.showToastAtBottom(R.string.db_full);
434d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
435d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
436d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
437d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(t1, String.format(Locale.US, "execSQL %s", sql));
438d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
439d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
440d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
441d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void execSQL(final String sql) {
442d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
443d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
444d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
445d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
446d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        maybePlayDebugNoise();
447d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
448d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mDatabase.execSQL(sql);
449d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (SQLiteFullException ex) {
450d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(TAG, "Database full, unable to execSQL", ex);
451d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            UiUtils.showToastAtBottom(R.string.db_full);
452d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
453d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
454d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
455d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(t1, String.format(Locale.US, "execSQL %s", sql));
456d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
457d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
458d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
459d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public int execSQLUpdateDelete(final String sql) {
460d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        long t1 = 0;
461d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
462d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            t1 = System.currentTimeMillis();
463d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
464d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        maybePlayDebugNoise();
465d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final SQLiteStatement statement = mDatabase.compileStatement(sql);
466d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int rowsUpdated = 0;
467d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
468d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            rowsUpdated = statement.executeUpdateDelete();
469d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (SQLiteFullException ex) {
470d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.e(TAG, "Database full, unable to execSQLUpdateDelete", ex);
471d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            UiUtils.showToastAtBottom(R.string.db_full);
472d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
473d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mLog) {
474d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            printTiming(t1, String.format(Locale.US, "execSQLUpdateDelete %s", sql));
475d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
476d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return rowsUpdated;
477d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
478d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
479d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public SQLiteDatabase getDatabase() {
480d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return mDatabase;
481d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
482d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd}
483