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