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 * Reads rows into a buffer. This method acquires the database lock. 57 * 58 * @param window The window to fill into 59 * @return number of total rows in the query 60 */ 61 /* package */ int fillWindow(CursorWindow window, 62 int maxRead, int lastPos) { 63 long timeStart = SystemClock.uptimeMillis(); 64 mDatabase.lock(); 65 mDatabase.logTimeStat(mSql, timeStart, SQLiteDatabase.GET_LOCK_LOG_PREFIX); 66 try { 67 acquireReference(); 68 try { 69 window.acquireReference(); 70 // if the start pos is not equal to 0, then most likely window is 71 // too small for the data set, loading by another thread 72 // is not safe in this situation. the native code will ignore maxRead 73 int numRows = native_fill_window(window, window.getStartPosition(), mOffsetIndex, 74 maxRead, lastPos); 75 76 // Logging 77 if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { 78 Log.d(TAG, "fillWindow(): " + mSql); 79 } 80 mDatabase.logTimeStat(mSql, timeStart); 81 return numRows; 82 } catch (IllegalStateException e){ 83 // simply ignore it 84 return 0; 85 } catch (SQLiteDatabaseCorruptException e) { 86 mDatabase.onCorruption(); 87 throw e; 88 } finally { 89 window.releaseReference(); 90 } 91 } finally { 92 releaseReference(); 93 mDatabase.unlock(); 94 } 95 } 96 97 /** 98 * Get the column count for the statement. Only valid on query based 99 * statements. The database must be locked 100 * when calling this method. 101 * 102 * @return The number of column in the statement's result set. 103 */ 104 /* package */ int columnCountLocked() { 105 acquireReference(); 106 try { 107 return native_column_count(); 108 } finally { 109 releaseReference(); 110 } 111 } 112 113 /** 114 * Retrieves the column name for the given column index. The database must be locked 115 * when calling this method. 116 * 117 * @param columnIndex the index of the column to get the name for 118 * @return The requested column's name 119 */ 120 /* package */ String columnNameLocked(int columnIndex) { 121 acquireReference(); 122 try { 123 return native_column_name(columnIndex); 124 } finally { 125 releaseReference(); 126 } 127 } 128 129 @Override 130 public String toString() { 131 return "SQLiteQuery: " + mSql; 132 } 133 134 @Override 135 public void close() { 136 super.close(); 137 mClosed = true; 138 } 139 140 /** 141 * Called by SQLiteCursor when it is requeried. 142 */ 143 /* package */ void requery() { 144 if (mBindArgs != null) { 145 int len = mBindArgs.length; 146 try { 147 for (int i = 0; i < len; i++) { 148 super.bindString(i + 1, mBindArgs[i]); 149 } 150 } catch (SQLiteMisuseException e) { 151 StringBuilder errMsg = new StringBuilder("mSql " + mSql); 152 for (int i = 0; i < len; i++) { 153 errMsg.append(" "); 154 errMsg.append(mBindArgs[i]); 155 } 156 errMsg.append(" "); 157 IllegalStateException leakProgram = new IllegalStateException( 158 errMsg.toString(), e); 159 throw leakProgram; 160 } 161 } 162 } 163 164 @Override 165 public void bindNull(int index) { 166 mBindArgs[index - 1] = null; 167 if (!mClosed) super.bindNull(index); 168 } 169 170 @Override 171 public void bindLong(int index, long value) { 172 mBindArgs[index - 1] = Long.toString(value); 173 if (!mClosed) super.bindLong(index, value); 174 } 175 176 @Override 177 public void bindDouble(int index, double value) { 178 mBindArgs[index - 1] = Double.toString(value); 179 if (!mClosed) super.bindDouble(index, value); 180 } 181 182 @Override 183 public void bindString(int index, String value) { 184 mBindArgs[index - 1] = value; 185 if (!mClosed) super.bindString(index, value); 186 } 187 188 private final native int native_fill_window(CursorWindow window, 189 int startPos, int offsetParam, int maxRead, int lastPos); 190 191 private final native int native_column_count(); 192 193 private final native String native_column_name(int columnIndex); 194} 195