OSDN Git Service

merge from open-source master
[android-x86/frameworks-base.git] / core / java / android / database / sqlite / SQLiteProgram.java
index d6aa9e6..4d96f12 100644 (file)
@@ -25,99 +25,150 @@ import android.util.Log;
  * threads should perform its own synchronization when using the SQLiteProgram.
  */
 public abstract class SQLiteProgram extends SQLiteClosable {
+
     private static final String TAG = "SQLiteProgram";
 
-    /** The database this program is compiled against. */
+    /** The database this program is compiled against.
+     * @deprecated do not use this
+     */
+    @Deprecated
     protected SQLiteDatabase mDatabase;
 
+    /** The SQL used to create this query */
+    /* package */ final String mSql;
+
     /**
      * Native linkage, do not modify. This comes from the database and should not be modified
      * in here or in the native code.
+     * @deprecated do not use this
      */
+    @Deprecated
     protected int nHandle = 0;
 
     /**
-     * Native linkage, do not modify. When non-0 this holds a reference to a valid
-     * sqlite3_statement object. It is only updated by the native code, but may be
-     * checked in this class when the database lock is held to determine if there
-     * is a valid native-side program or not.
+     * the SQLiteCompiledSql object for the given sql statement.
      */
-    protected int nStatement = 0;
+    private SQLiteCompiledSql mCompiledSql;
 
     /**
-     * Used to find out where a cursor was allocated in case it never got
-     * released.
+     * SQLiteCompiledSql statement id is populated with the corresponding object from the above
+     * member. This member is used by the native_bind_* methods
+     * @deprecated do not use this
      */
-    private StackTraceElement[] mStackTraceElements;    
+    @Deprecated
+    protected int nStatement = 0;
+
     /* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
-        if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
-            mStackTraceElements = new Exception().getStackTrace();
-        }
-        
         mDatabase = db;
+        mSql = sql.trim();
         db.acquireReference();
         db.addSQLiteClosable(this);
         this.nHandle = db.mNativeHandle;
-        compile(sql, false);
-    }    
-    
+
+        // only cache CRUD statements
+        String prefixSql = mSql.substring(0, 6);
+        if (!prefixSql.equalsIgnoreCase("INSERT") && !prefixSql.equalsIgnoreCase("UPDATE") &&
+                !prefixSql.equalsIgnoreCase("REPLAC") &&
+                !prefixSql.equalsIgnoreCase("DELETE") && !prefixSql.equalsIgnoreCase("SELECT")) {
+            mCompiledSql = new SQLiteCompiledSql(db, sql);
+            nStatement = mCompiledSql.nStatement;
+            // since it is not in the cache, no need to acquire() it.
+            return;
+        }
+
+        // it is not pragma
+        mCompiledSql = db.getCompiledStatementForSql(sql);
+        if (mCompiledSql == null) {
+            // create a new compiled-sql obj
+            mCompiledSql = new SQLiteCompiledSql(db, sql);
+
+            // add it to the cache of compiled-sqls
+            // but before adding it and thus making it available for anyone else to use it,
+            // make sure it is acquired by me.
+            mCompiledSql.acquire();
+            db.addToCompiledQueries(sql, mCompiledSql);
+            if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
+                Log.v(TAG, "Created DbObj (id#" + mCompiledSql.nStatement +
+                        ") for sql: " + sql);
+            }
+        } else {
+            // it is already in compiled-sql cache.
+            // try to acquire the object.
+            if (!mCompiledSql.acquire()) {
+                int last = mCompiledSql.nStatement;
+                // the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object.
+                // we can't have two different SQLiteProgam objects can't share the same
+                // CompiledSql object. create a new one.
+                // finalize it when I am done with it in "this" object.
+                mCompiledSql = new SQLiteCompiledSql(db, sql);
+                if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
+                    Log.v(TAG, "** possible bug ** Created NEW DbObj (id#" +
+                            mCompiledSql.nStatement +
+                            ") because the previously created DbObj (id#" + last +
+                            ") was not released for sql:" + sql);
+                }
+                // since it is not in the cache, no need to acquire() it.
+            }
+        }
+        nStatement = mCompiledSql.nStatement;
+    }
+
     @Override
     protected void onAllReferencesReleased() {
-        // Note that native_finalize() checks to make sure that nStatement is
-        // non-null before destroying it.
-        native_finalize();
+        releaseCompiledSqlIfNotInCache();
         mDatabase.releaseReference();
         mDatabase.removeSQLiteClosable(this);
     }
-    
+
     @Override
-    protected void onAllReferencesReleasedFromContainer(){
-        // Note that native_finalize() checks to make sure that nStatement is
-        // non-null before destroying it.
-        native_finalize();
-        mDatabase.releaseReference();        
+    protected void onAllReferencesReleasedFromContainer() {
+        releaseCompiledSqlIfNotInCache();
+        mDatabase.releaseReference();
+    }
+
+    private void releaseCompiledSqlIfNotInCache() {
+        if (mCompiledSql == null) {
+            return;
+        }
+        synchronized(mDatabase.mCompiledQueries) {
+            if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) {
+                // it is NOT in compiled-sql cache. i.e., responsibility of
+                // releasing this statement is on me.
+                mCompiledSql.releaseSqlStatement();
+                mCompiledSql = null;
+                nStatement = 0;
+            } else {
+                // it is in compiled-sql cache. reset its CompiledSql#mInUse flag
+                mCompiledSql.release();
+            }
+        } 
     }
 
     /**
      * Returns a unique identifier for this program.
-     * 
+     *
      * @return a unique identifier for this program
      */
     public final int getUniqueId() {
         return nStatement;
     }
 
+    /* package */ String getSqlString() {
+        return mSql;
+    }
+
     /**
-     * Compiles the given SQL into a SQLite byte code program using sqlite3_prepare_v2(). If
-     * this method has been called previously without a call to close and forCompilation is set
-     * to false the previous compilation will be used. Setting forceCompilation to true will
-     * always re-compile the program and should be done if you pass differing SQL strings to this
-     * method.
-     *
-     * <P>Note: this method acquires the database lock.</P>
+     * @deprecated This method is deprecated and must not be used.
      *
      * @param sql the SQL string to compile
      * @param forceCompilation forces the SQL to be recompiled in the event that there is an
      *  existing compiled SQL program already around
      */
+    @Deprecated
     protected void compile(String sql, boolean forceCompilation) {
-        // Only compile if we don't have a valid statement already or the caller has
-        // explicitly requested a recompile. 
-        if (nStatement == 0 || forceCompilation) {
-            mDatabase.lock();
-            try {
-                // Note that the native_compile() takes care of destroying any previously
-                // existing programs before it compiles.
-                acquireReference();                
-                native_compile(sql);
-            } finally {
-                releaseReference();
-                mDatabase.unlock();
-            }        
-        }
-    } 
-  
+        // TODO is there a need for this?
+    }
+
     /**
      * Bind a NULL value to this statement. The value remains bound until
      * {@link #clearBindings} is called.
@@ -125,6 +176,9 @@ public abstract class SQLiteProgram extends SQLiteClosable {
      * @param index The 1-based index to the parameter to bind null to
      */
     public void bindNull(int index) {
+        if (!mDatabase.isOpen()) {
+            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
+        }
         acquireReference();
         try {
             native_bind_null(index);
@@ -141,6 +195,9 @@ public abstract class SQLiteProgram extends SQLiteClosable {
      * @param value The value to bind
      */
     public void bindLong(int index, long value) {
+        if (!mDatabase.isOpen()) {
+            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
+        }
         acquireReference();
         try {
             native_bind_long(index, value);
@@ -157,6 +214,9 @@ public abstract class SQLiteProgram extends SQLiteClosable {
      * @param value The value to bind
      */
     public void bindDouble(int index, double value) {
+        if (!mDatabase.isOpen()) {
+            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
+        }
         acquireReference();
         try {
             native_bind_double(index, value);
@@ -176,6 +236,9 @@ public abstract class SQLiteProgram extends SQLiteClosable {
         if (value == null) {
             throw new IllegalArgumentException("the bind value at index " + index + " is null");
         }
+        if (!mDatabase.isOpen()) {
+            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
+        }
         acquireReference();
         try {
             native_bind_string(index, value);
@@ -195,6 +258,9 @@ public abstract class SQLiteProgram extends SQLiteClosable {
         if (value == null) {
             throw new IllegalArgumentException("the bind value at index " + index + " is null");
         }
+        if (!mDatabase.isOpen()) {
+            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
+        }
         acquireReference();
         try {
             native_bind_blob(index, value);
@@ -207,6 +273,9 @@ public abstract class SQLiteProgram extends SQLiteClosable {
      * Clears all existing bindings. Unset bindings are treated as NULL.
      */
     public void clearBindings() {
+        if (!mDatabase.isOpen()) {
+            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
+        }
         acquireReference();
         try {
             native_clear_bindings();
@@ -219,42 +288,31 @@ public abstract class SQLiteProgram extends SQLiteClosable {
      * Release this program's resources, making it invalid.
      */
     public void close() {
+        if (!mDatabase.isOpen()) {
+            return;
+        }
         mDatabase.lock();
         try {
             releaseReference();
         } finally {
             mDatabase.unlock();
-        }        
-    }
-    
-    /**
-     * Make sure that the native resource is cleaned up.
-     */
-    @Override
-    protected void finalize() {
-        if (nStatement != 0) {
-            if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
-                String message = "Finalizing " + this +  
-                    " that has not been closed";
-
-                Log.d(TAG, message + "\nThis cursor was created in:");
-                for (StackTraceElement ste : mStackTraceElements) {
-                    Log.d(TAG, "      " + ste);
-                }
-            }
-            // when in finalize() it is already removed from weakhashmap
-            // so it is safe to not removed itself from db
-            onAllReferencesReleasedFromContainer();
         }
     }
 
     /**
+     * @deprecated This method is deprecated and must not be used.
      * Compiles SQL into a SQLite program.
-     * 
+     *
      * <P>The database lock must be held when calling this method.
      * @param sql The SQL to compile.
      */
+    @Deprecated
     protected final native void native_compile(String sql);
+
+    /**
+     * @deprecated This method is deprecated and must not be used.
+     */
+    @Deprecated
     protected final native void native_finalize();
 
     protected final native void native_bind_null(int index);