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