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