Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Replace nativeExecuteForCursorWindow() with an implementation that builds with the NDK. Seems to work, but is not yet tested. Exception handling is almost certainly still wrong. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
365586dcafe43f880021b0de52b6a19f |
User & Date: | dan 2013-12-21 16:04:55.246 |
Context
2013-12-21
| ||
17:58 | Re-enable some disabled code required by user-defined function implementations. (check-in: e01e48cd52 user: dan tags: trunk) | |
16:04 | Replace nativeExecuteForCursorWindow() with an implementation that builds with the NDK. Seems to work, but is not yet tested. Exception handling is almost certainly still wrong. (check-in: 365586dcaf user: dan tags: trunk) | |
2013-12-20
| ||
17:02 | Start setting up some infrastructure code for a test suite. Add a test demonstrating the problem with type Cursor. (check-in: 69b389af43 user: dan tags: trunk) | |
Changes
Changes to jni/android_database_SQLiteConnection.cpp.
︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <JNIHelp.h> #include "ALog-priv.h" #include <sys/mman.h> #include <string.h> #include <unistd.h> #if 0 #include <androidfw/CursorWindow.h> #endif #include <sqlite3.h> #if 0 | > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #include <JNIHelp.h> #include "ALog-priv.h" #include <sys/mman.h> #include <string.h> #include <unistd.h> #include <assert.h> #if 0 #include <androidfw/CursorWindow.h> #endif #include <sqlite3.h> #if 0 |
︙ | ︙ | |||
602 603 604 605 606 607 608 | return createAshmemRegionWithData(env, blob, length); } } } return -1; } | > > > > | < > > | | > > > > > > | < < < < < < < < < > | > > > | < < < < < < < > | < < | | > > | > > | > > > | < | > | > > | | > > > | < > | < < < < < > | | | < | < | < < < < < > | | | | < | < < < < | > | | | > | | | | | | | | < | | > | < < < < | < < < < > | | | | > | > > | | > > | | > > > | | > > > > > > | | | | < > > > > > > > | < | > > > > > > > | > > | < < | | | < < < < < < < < < | | < | > | | > > | < < > | < > > > > > | | | > | < | < < < < < < < | < < < | > > > | | | | < < | < < | < | | < < < < < < | < < < < < < < < < < < < < < < | < < > > | | < | < > > > > > > | < > > > > > | > | > > > | > > > | > > | > | | > > > > > | | | > > > | 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 | return createAshmemRegionWithData(env, blob, length); } } } return -1; } /* ** Note: The following symbols must be in the same order as the corresponding ** elements in the aMethod[] array in function nativeExecuteForCursorWindow(). */ enum CWMethodNames { CW_CLEAR = 0, CW_SETNUMCOLUMNS = 1, CW_ALLOCROW = 2, CW_FREELASTROW = 3, CW_PUTNULL = 4, CW_PUTLONG = 5, CW_PUTDOUBLE = 6, CW_PUTSTRING = 7, CW_PUTBLOB = 8 }; /* ** An instance of this structure represents a single CursorWindow java method. */ struct CWMethod { jmethodID id; /* Method id */ const char *zName; /* Method name */ const char *zSig; /* Method JNI signature */ }; /* ** Append the contents of the row that SQL statement pStmt currently points to ** to the CursorWindow object passed as the second argument. The CursorWindow ** currently contains iRow rows. Return true on success or false if an error ** occurs. */ static jboolean copyRowToWindow( JNIEnv *pEnv, jobject win, int iRow, sqlite3_stmt *pStmt, CWMethod *aMethod ){ int nCol = sqlite3_column_count(pStmt); int i; jboolean bOk; bOk = pEnv->CallBooleanMethod(win, aMethod[CW_ALLOCROW].id); for(i=0; bOk && i<nCol; i++){ switch( sqlite3_column_type(pStmt, i) ){ case SQLITE_NULL: { bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTNULL].id, iRow, i); break; } case SQLITE_INTEGER: { jlong val = sqlite3_column_int64(pStmt, i); bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTLONG].id, val, iRow, i); break; } case SQLITE_FLOAT: { jdouble val = sqlite3_column_double(pStmt, i); bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTDOUBLE].id, val, iRow, i); break; } case SQLITE_TEXT: { const char *zVal = (const char*)sqlite3_column_text(pStmt, i); jstring val = pEnv->NewStringUTF(zVal); bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTSTRING].id, val, iRow, i); break; } default: { assert( sqlite3_column_type(pStmt, i)==SQLITE_BLOB ); const jbyte *p = (const jbyte*)sqlite3_column_blob(pStmt, i); int n = sqlite3_column_bytes(pStmt, i); jbyteArray val = pEnv->NewByteArray(n); pEnv->SetByteArrayRegion(val, 0, n, p); bOk = pEnv->CallBooleanMethod(win, aMethod[CW_PUTBLOB].id, val, iRow, i); break; } } if( bOk==0 ){ pEnv->CallVoidMethod(win, aMethod[CW_FREELASTROW].id); } } return bOk; } static jboolean setWindowNumColumns( JNIEnv *pEnv, jobject win, sqlite3_stmt *pStmt, CWMethod *aMethod ){ int nCol; pEnv->CallVoidMethod(win, aMethod[CW_CLEAR].id); nCol = sqlite3_column_count(pStmt); return pEnv->CallBooleanMethod(win, aMethod[CW_SETNUMCOLUMNS].id, (jint)nCol); } /* ** This method has been rewritten for org.sqlite.database.*. The original ** android implementation used the C++ interface to populate a CursorWindow ** object. Since the NDK does not export this interface, we invoke the Java ** interface using standard JNI methods to do the same thing. ** ** This function executes the SQLite statement object passed as the 4th ** argument and copies one or more returned rows into the CursorWindow ** object passed as the 5th argument. The set of rows copied into the ** CursorWindow is always contiguous. ** ** The only row that *must* be copied into the CursorWindow is row ** iRowRequired. Ideally, all rows from iRowStart through to the end ** of the query are copied into the CursorWindow. If this is not possible ** (CursorWindow objects have a finite capacity), some compromise position ** is found (see comments embedded in the code below for details). ** ** The return value is a 64-bit integer calculated as follows: ** ** (iStart << 32) | nRow ** ** where iStart is the index of the first row copied into the CursorWindow. ** If the countAllRows argument is true, nRow is the total number of rows ** returned by the query. Otherwise, nRow is one greater than the index of ** the last row copied into the CursorWindow. */ static jlong nativeExecuteForCursorWindow( JNIEnv *pEnv, jclass clazz, jint connectionPtr, /* Pointer to SQLiteConnection C++ object */ jint statementPtr, /* Pointer to sqlite3_stmt object */ jobject win, /* The CursorWindow object to populate */ jint startPos, /* First row to add (advisory) */ jint iRowRequired, /* Required row */ jboolean countAllRows ) { SQLiteConnection *pConnection = reinterpret_cast<SQLiteConnection*>(connectionPtr); sqlite3_stmt *pStmt = reinterpret_cast<sqlite3_stmt*>(statementPtr); CWMethod aMethod[] = { {0, "clear", "()V"}, {0, "setNumColumns", "(I)Z"}, {0, "allocRow", "()Z"}, {0, "freeLastRow", "()V"}, {0, "putNull", "(II)Z"}, {0, "putLong", "(JII)Z"}, {0, "putDouble", "(DII)Z"}, {0, "putString", "(Ljava/lang/String;II)Z"}, {0, "putBlob", "([BII)Z"}, }; jclass cls; /* Class android.database.CursorWindow */ int i; /* Iterator variable */ int nCol; /* Number of columns returned by pStmt */ int nRow; jboolean bOk; int iStart; /* First row copied to CursorWindow */ /* Locate all required CursorWindow methods. */ cls = pEnv->FindClass("android/database/CursorWindow"); for(i=0; i<(sizeof(aMethod)/sizeof(struct CWMethod)); i++){ aMethod[i].id = pEnv->GetMethodID(cls, aMethod[i].zName, aMethod[i].zSig); if( aMethod[i].id==NULL ){ jniThrowExceptionFmt(pEnv, "java/lang/Exception", "Failed to find method CursorWindow.%s()", aMethod[i].zName ); return 0; } } /* Set the number of columns in the window */ bOk = setWindowNumColumns(pEnv, win, pStmt, aMethod); if( bOk==0 ) return 0; nRow = 0; iStart = startPos; while( sqlite3_step(pStmt)==SQLITE_ROW ){ /* Only copy in rows that occur at or after row index iStart. */ if( nRow>=iStart && bOk ){ bOk = copyRowToWindow(pEnv, win, (nRow - iStart), pStmt, aMethod); if( bOk==0 ){ /* The CursorWindow object ran out of memory. If row iRowRequired was ** not successfully added before this happened, clear the CursorWindow ** and try to add the current row again. */ if( nRow<=iRowRequired ){ bOk = setWindowNumColumns(pEnv, win, pStmt, aMethod); if( bOk==0 ){ sqlite3_reset(pStmt); return 0; } iStart = nRow; bOk = copyRowToWindow(pEnv, win, (nRow - iStart), pStmt, aMethod); } /* If the CursorWindow is still full and the countAllRows flag is not ** set, break out of the loop here. If countAllRows is set, continue ** so as to set variable nRow correctly. */ if( bOk==0 && countAllRows==0 ) break; } } nRow++; } /* Finalize the statement. If this indicates an error occurred, throw an ** SQLiteException exception. */ int rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK ){ throw_sqlite3_exception(pEnv, sqlite3_db_handle(pStmt)); return 0; } jlong lRet = jlong(iStart) << 32 | jlong(nRow); return lRet; } static jint nativeGetDbLookaside(JNIEnv* env, jobject clazz, jint connectionPtr) { SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr); int cur = -1; int unused; sqlite3_db_status(connection->db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &unused, 0); |
︙ | ︙ | |||
891 892 893 894 895 896 897 | (void*)nativeExecuteForString }, { "nativeExecuteForBlobFileDescriptor", "(II)I", (void*)nativeExecuteForBlobFileDescriptor }, { "nativeExecuteForChangedRowCount", "(II)I", (void*)nativeExecuteForChangedRowCount }, { "nativeExecuteForLastInsertedRowId", "(II)J", (void*)nativeExecuteForLastInsertedRowId }, | | | 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 | (void*)nativeExecuteForString }, { "nativeExecuteForBlobFileDescriptor", "(II)I", (void*)nativeExecuteForBlobFileDescriptor }, { "nativeExecuteForChangedRowCount", "(II)I", (void*)nativeExecuteForChangedRowCount }, { "nativeExecuteForLastInsertedRowId", "(II)J", (void*)nativeExecuteForLastInsertedRowId }, { "nativeExecuteForCursorWindow", "(IILandroid/database/CursorWindow;IIZ)J", (void*)nativeExecuteForCursorWindow }, { "nativeGetDbLookaside", "(I)I", (void*)nativeGetDbLookaside }, { "nativeCancel", "(I)V", (void*)nativeCancel }, { "nativeResetCancel", "(IZ)V", (void*)nativeResetCancel }, |
︙ | ︙ |
Changes to jni/android_database_SQLiteGlobal.cpp.
︙ | ︙ | |||
38 39 40 41 42 43 44 | // Called each time a message is logged. static void sqliteLogCallback(void* data, int iErrCode, const char* zMsg) { bool verboseLog = !!data; if (iErrCode == 0 || iErrCode == SQLITE_CONSTRAINT || iErrCode == SQLITE_SCHEMA) { if (verboseLog) { | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | // Called each time a message is logged. static void sqliteLogCallback(void* data, int iErrCode, const char* zMsg) { bool verboseLog = !!data; if (iErrCode == 0 || iErrCode == SQLITE_CONSTRAINT || iErrCode == SQLITE_SCHEMA) { if (verboseLog) { ALOG(LOG_VERBOSE, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg); } } else { ALOG(LOG_ERROR, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg); } } // Sets the global SQLite configuration. |
︙ | ︙ |
Changes to res/layout/main.xml.
1 2 3 4 5 6 7 8 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView | > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | <?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView |
︙ | ︙ | |||
23 24 25 26 27 28 29 30 | android:id="@+id/tv_widget" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="<this text should be replaced by the test output>" android:typeface="monospace" /> </LinearLayout> | > | 28 29 30 31 32 33 34 35 36 | android:id="@+id/tv_widget" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="<this text should be replaced by the test output>" android:typeface="monospace" /> </LinearLayout> </ScrollView> |
Changes to src/org/sqlite/app/customsqlite/CustomSqlite.java.
︙ | ︙ | |||
38 39 40 41 42 43 44 45 46 47 48 49 50 51 | db = SQLiteDatabase.openOrCreateDatabase(":memory:", null); st = db.compileStatement("SELECT sqlite_version()"); res = st.simpleQueryForString(); myTV.append("SQLite version " + res + "\n\n"); } public void test_result(String name, String res, String expected){ myTV.append(name + "... "); myNTest++; if( res.equals(expected) ){ myTV.append("ok\n"); | > > > > | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | db = SQLiteDatabase.openOrCreateDatabase(":memory:", null); st = db.compileStatement("SELECT sqlite_version()"); res = st.simpleQueryForString(); myTV.append("SQLite version " + res + "\n\n"); } public void test_warning(String name, String warning){ myTV.append("WARNING:" + name + ": " + warning + "\n"); } public void test_result(String name, String res, String expected){ myTV.append(name + "... "); myNTest++; if( res.equals(expected) ){ myTV.append("ok\n"); |
︙ | ︙ | |||
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | Cursor c = db.rawQuery("SELECT x FROM t1", null); if( c!=null ){ boolean bRes; for(bRes=c.moveToFirst(); bRes; bRes=c.moveToNext()){ String x = c.getString(0); res = res + "." + x; } } test_result("csr_test_1", res, ".one.two.three"); } public void run_the_tests(View view){ System.loadLibrary("sqliteX"); myTV.setText(""); myNErr = 0; myNTest = 0; try { report_version(); csr_test_1(); myTV.append("\n" + myNErr + " errors from " + myNTest + " tests\n"); } catch(Exception e) { | > > | > | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | Cursor c = db.rawQuery("SELECT x FROM t1", null); if( c!=null ){ boolean bRes; for(bRes=c.moveToFirst(); bRes; bRes=c.moveToNext()){ String x = c.getString(0); res = res + "." + x; } }else{ test_warning("csr_test_1", "c==NULL"); } test_result("csr_test_1", res, ".one.two.three"); } public void run_the_tests(View view){ System.loadLibrary("sqliteX"); myTV.setText(""); myNErr = 0; myNTest = 0; try { report_version(); csr_test_1(); myTV.append("\n" + myNErr + " errors from " + myNTest + " tests\n"); } catch(Exception e) { myTV.append("Exception: " + e.toString() + "\n"); myTV.append(android.util.Log.getStackTraceString(e) + "\n"); } } } |
Changes to src/org/sqlite/database/sqlite/ExtraUtils.java.
︙ | ︙ | |||
82 83 84 85 86 87 88 89 | for (int i = 0; i < length; i++) { if (columnNames[i].equals("_id")) { return i; } } return -1; } } | > > > > > > > > > > > > > > > > > > > > > > > > > > | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | for (int i = 0; i < length; i++) { if (columnNames[i].equals("_id")) { return i; } } return -1; } /** * Picks a start position for {@link Cursor#fillWindow} such that the * window will contain the requested row and a useful range of rows * around it. * * When the data set is too large to fit in a cursor window, seeking the * cursor can become a very expensive operation since we have to run the * query again when we move outside the bounds of the current window. * * We try to choose a start position for the cursor window such that * 1/3 of the window's capacity is used to hold rows before the requested * position and 2/3 of the window's capacity is used to hold rows after the * requested position. * * @param cursorPosition The row index of the row we want to get. * @param cursorWindowCapacity The estimated number of rows that can fit in * a cursor window, or 0 if unknown. * @return The recommended start position, always less than or equal to * the requested row. * @hide */ public static int cursorPickFillWindowStartPosition( int cursorPosition, int cursorWindowCapacity) { return Math.max(cursorPosition - cursorWindowCapacity / 3, 0); } } |
Changes to src/org/sqlite/database/sqlite/SQLiteConnection.java.
︙ | ︙ | |||
147 148 149 150 151 152 153 | private static native String nativeExecuteForString(int connectionPtr, int statementPtr); private static native int nativeExecuteForBlobFileDescriptor( int connectionPtr, int statementPtr); private static native int nativeExecuteForChangedRowCount(int connectionPtr, int statementPtr); private static native long nativeExecuteForLastInsertedRowId( int connectionPtr, int statementPtr); private static native long nativeExecuteForCursorWindow( | | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | private static native String nativeExecuteForString(int connectionPtr, int statementPtr); private static native int nativeExecuteForBlobFileDescriptor( int connectionPtr, int statementPtr); private static native int nativeExecuteForChangedRowCount(int connectionPtr, int statementPtr); private static native long nativeExecuteForLastInsertedRowId( int connectionPtr, int statementPtr); private static native long nativeExecuteForCursorWindow( int connectionPtr, int statementPtr, CursorWindow win, int startPos, int requiredPos, boolean countAllRows); private static native int nativeGetDbLookaside(int connectionPtr); private static native void nativeCancel(int connectionPtr); private static native void nativeResetCancel(int connectionPtr, boolean cancelable); private SQLiteConnection(SQLiteConnectionPool pool, SQLiteDatabaseConfiguration configuration, |
︙ | ︙ | |||
816 817 818 819 820 821 822 | * @throws SQLiteException if an error occurs, such as a syntax error * or invalid number of bind arguments. * @throws OperationCanceledException if the operation was canceled. */ public int executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, CancellationSignal cancellationSignal) { | < | 816 817 818 819 820 821 822 823 824 825 826 827 828 829 | * @throws SQLiteException if an error occurs, such as a syntax error * or invalid number of bind arguments. * @throws OperationCanceledException if the operation was canceled. */ public int executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, CancellationSignal cancellationSignal) { if (sql == null) { throw new IllegalArgumentException("sql must not be null."); } if (window == null) { throw new IllegalArgumentException("window must not be null."); } |
︙ | ︙ | |||
840 841 842 843 844 845 846 | try { throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); applyBlockGuardPolicy(statement); attachCancellationSignal(cancellationSignal); try { final long result = nativeExecuteForCursorWindow( | | | 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 | try { throwIfStatementForbidden(statement); bindArguments(statement, bindArgs); applyBlockGuardPolicy(statement); attachCancellationSignal(cancellationSignal); try { final long result = nativeExecuteForCursorWindow( mConnectionPtr, statement.mStatementPtr, window, startPos, requiredPos, countAllRows); actualPos = (int)(result >> 32); countedRows = (int)result; filledRows = window.getNumRows(); window.setStartPosition(actualPos); return countedRows; } finally { |
︙ | ︙ | |||
868 869 870 871 872 873 874 | + ", filledRows=" + filledRows + ", countedRows=" + countedRows); } } } finally { window.releaseReference(); } | < < | 867 868 869 870 871 872 873 874 875 876 877 878 879 880 | + ", filledRows=" + filledRows + ", countedRows=" + countedRows); } } } finally { window.releaseReference(); } } private PreparedStatement acquirePreparedStatement(String sql) { PreparedStatement statement = mPreparedStatementCache.get(sql); boolean skipCache = false; if (statement != null) { if (!statement.mInUse) { |
︙ | ︙ |
Changes to src/org/sqlite/database/sqlite/SQLiteCursor.java.
︙ | ︙ | |||
17 18 19 20 21 22 23 | package org.sqlite.database.sqlite; import org.sqlite.database.ExtraUtils; import android.database.AbstractWindowedCursor; import android.database.CursorWindow; | < | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | package org.sqlite.database.sqlite; import org.sqlite.database.ExtraUtils; import android.database.AbstractWindowedCursor; import android.database.CursorWindow; import android.os.StrictMode; import android.util.Log; import java.util.HashMap; import java.util.Map; /** |
︙ | ︙ | |||
133 134 135 136 137 138 139 140 | @Override public int getCount() { if (mCount == NO_COUNT) { fillWindow(0); } return mCount; } | < | > > > > > > > > > > > > > > > > > > > | | | | < | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | @Override public int getCount() { if (mCount == NO_COUNT) { fillWindow(0); } return mCount; } /* ** The AbstractWindowClass contains protected methods clearOrCreateWindow() and ** closeWindow(), which are used by the android.database.sqlite.* version of this ** class. But, since they are marked with "@hide", the following replacement ** versions are required. */ private void awc_clearOrCreateWindow(String name){ CursorWindow win = getWindow(); if( win==null ){ win = new CursorWindow(name); setWindow(win); }else{ win.clear(); } } private void awc_closeWindow(){ setWindow(null); } private void fillWindow(int requiredPos) { awc_clearOrCreateWindow(getDatabase().getPath()); try { if (mCount == NO_COUNT) { int startPos = ExtraUtils.cursorPickFillWindowStartPosition(requiredPos, 0); mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true); mCursorWindowCapacity = mWindow.getNumRows(); if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "received count(*) from native_fill_window: " + mCount); } } else { int startPos = ExtraUtils.cursorPickFillWindowStartPosition(requiredPos, mCursorWindowCapacity); mQuery.fillWindow(mWindow, startPos, requiredPos, false); } } catch (RuntimeException ex) { // Close the cursor window if the query failed and therefore will // not produce any results. This helps to avoid accidentally leaking // the cursor window if the client does not correctly handle exceptions // and fails to close the cursor. awc_closeWindow(); throw ex; } } @Override public int getColumnIndex(String columnName) { // Create mColumnNameMap on demand if (mColumnNameMap == null) { String[] columns = mColumns; |
︙ | ︙ |