SQLiteQuery.java revision 65a8883f0e605bb8a73a692987b47ce5da632e72
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.database.sqlite;
18
19import android.database.CursorWindow;
20import android.os.SystemClock;
21import android.util.Log;
22
23/**
24 * A SQLite program that represents a query that reads the resulting rows into a CursorWindow.
25 * This class is used by SQLiteCursor and isn't useful itself.
26 *
27 * SQLiteQuery is not internally synchronized so code using a SQLiteQuery from multiple
28 * threads should perform its own synchronization when using the SQLiteQuery.
29 */
30public class SQLiteQuery extends SQLiteProgram {
31    private static final String TAG = "Cursor";
32
33    /** The index of the unbound OFFSET parameter */
34    private int mOffsetIndex;
35
36    /** Args to bind on requery */
37    private String[] mBindArgs;
38
39    private boolean mClosed = false;
40
41    /**
42     * Create a persistent query object.
43     *
44     * @param db The database that this query object is associated with
45     * @param query The SQL string for this query.
46     * @param offsetIndex The 1-based index to the OFFSET parameter,
47     */
48    /* package */ SQLiteQuery(SQLiteDatabase db, String query, int offsetIndex, String[] bindArgs) {
49        super(db, query);
50
51        mOffsetIndex = offsetIndex;
52        mBindArgs = bindArgs;
53    }
54
55    /**
56     * Constructor used to create new instance to replace a given instance of this class.
57     * This constructor is used when the current Query object is now associated with a different
58     * {@link SQLiteDatabase} object.
59     *
60     * @param db The database that this query object is associated with
61     * @param query the instance of {@link SQLiteQuery} to be replaced
62     */
63    /* package */ SQLiteQuery(SQLiteDatabase db, SQLiteQuery query) {
64        this(db, query.mSql, 0, query.mBindArgs);
65    }
66
67    /**
68     * Reads rows into a buffer. This method acquires the database lock.
69     *
70     * @param window The window to fill into
71     * @return number of total rows in the query
72     */
73    /* package */ int fillWindow(CursorWindow window,
74            int maxRead, int lastPos) {
75        long timeStart = SystemClock.uptimeMillis();
76        mDatabase.lock();
77        mDatabase.logTimeStat(mSql, timeStart, SQLiteDatabase.GET_LOCK_LOG_PREFIX);
78        try {
79            acquireReference();
80            try {
81                window.acquireReference();
82                // if the start pos is not equal to 0, then most likely window is
83                // too small for the data set, loading by another thread
84                // is not safe in this situation. the native code will ignore maxRead
85                int numRows = native_fill_window(window, window.getStartPosition(), mOffsetIndex,
86                        maxRead, lastPos);
87                mDatabase.logTimeStat(mSql, timeStart);
88                return numRows;
89            } catch (IllegalStateException e){
90                // simply ignore it
91                return 0;
92            } catch (SQLiteDatabaseCorruptException e) {
93                mDatabase.onCorruption();
94                throw e;
95            } finally {
96                window.releaseReference();
97            }
98        } finally {
99            releaseReference();
100            mDatabase.unlock();
101        }
102    }
103
104    /**
105     * Get the column count for the statement. Only valid on query based
106     * statements. The database must be locked
107     * when calling this method.
108     *
109     * @return The number of column in the statement's result set.
110     */
111    /* package */ int columnCountLocked() {
112        acquireReference();
113        try {
114            return native_column_count();
115        } finally {
116            releaseReference();
117        }
118    }
119
120    /**
121     * Retrieves the column name for the given column index. The database must be locked
122     * when calling this method.
123     *
124     * @param columnIndex the index of the column to get the name for
125     * @return The requested column's name
126     */
127    /* package */ String columnNameLocked(int columnIndex) {
128        acquireReference();
129        try {
130            return native_column_name(columnIndex);
131        } finally {
132            releaseReference();
133        }
134    }
135
136    @Override
137    public String toString() {
138        return "SQLiteQuery: " + mSql;
139    }
140
141    @Override
142    public void close() {
143        super.close();
144        mClosed = true;
145    }
146
147    /**
148     * Called by SQLiteCursor when it is requeried.
149     */
150    /* package */ void requery() {
151        if (mBindArgs != null) {
152            int len = mBindArgs.length;
153            try {
154                for (int i = 0; i < len; i++) {
155                    super.bindString(i + 1, mBindArgs[i]);
156                }
157            } catch (SQLiteMisuseException e) {
158                StringBuilder errMsg = new StringBuilder("mSql " + mSql);
159                for (int i = 0; i < len; i++) {
160                    errMsg.append(" ");
161                    errMsg.append(mBindArgs[i]);
162                }
163                errMsg.append(" ");
164                IllegalStateException leakProgram = new IllegalStateException(
165                        errMsg.toString(), e);
166                throw leakProgram;
167            }
168        }
169    }
170
171    @Override
172    public void bindNull(int index) {
173        mBindArgs[index - 1] = null;
174        if (!mClosed) super.bindNull(index);
175    }
176
177    @Override
178    public void bindLong(int index, long value) {
179        mBindArgs[index - 1] = Long.toString(value);
180        if (!mClosed) super.bindLong(index, value);
181    }
182
183    @Override
184    public void bindDouble(int index, double value) {
185        mBindArgs[index - 1] = Double.toString(value);
186        if (!mClosed) super.bindDouble(index, value);
187    }
188
189    @Override
190    public void bindString(int index, String value) {
191        mBindArgs[index - 1] = value;
192        if (!mClosed) super.bindString(index, value);
193    }
194
195    private final native int native_fill_window(CursorWindow window,
196            int startPos, int offsetParam, int maxRead, int lastPos);
197
198    private final native int native_column_count();
199
200    private final native String native_column_name(int columnIndex);
201}
202